xref: /illumos-gate/usr/src/cmd/sgs/elfedit/common/elfedit.c (revision e9e51ff11d1c924e585ab15b17926c4cdf1d6ddf)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include	<sys/types.h>
28 #include	<sys/stat.h>
29 #include	<sys/wait.h>
30 #include	<stdarg.h>
31 #include	<fcntl.h>
32 #include	<stdlib.h>
33 #include	<stdio.h>
34 #include	<signal.h>
35 #include	<dirent.h>
36 #include	<libelf.h>
37 #include	<gelf.h>
38 #include	<conv.h>
39 #include	<dlfcn.h>
40 #include	<link.h>
41 #include	<stdarg.h>
42 #include	<libgen.h>
43 #include	<libintl.h>
44 #include	<locale.h>
45 #include	<unistd.h>
46 #include	<errno.h>
47 #include	<ctype.h>
48 #include	<limits.h>
49 #include	<strings.h>
50 #include	<sgs.h>
51 #include	"msg.h"
52 #include	"_elfedit.h"
53 #include	<debug.h>	/* liblddb */
54 
55 
56 
57 /*
58  * Column at which elfedit_format_command_usage() will wrap the
59  * generated usage string if the wrap argument is True (1).
60  */
61 #define	USAGE_WRAP_COL 55
62 
63 
64 
65 
66 /*
67  * Type used to represent a string buffer that can grow as needed
68  * to hold strings of arbitrary length. The user should declare
69  * variables of this type sa static. The strbuf_ensure_size() function
70  * is used to ensure that it has a minimum desired size.
71  */
72 typedef struct {
73 	char *buf;		/* String buffer */
74 	size_t n;		/* Size of buffer */
75 } STRBUF;
76 
77 
78 
79 
80 /*
81  * Types used by tokenize_user_cmd() to represent the result of
82  * spliting a user command into individual tokens.
83  */
84 typedef struct {
85 	char	*tok_str;	/* Token string */
86 	size_t	tok_len;	/* strlen(str) */
87 	size_t	tok_line_off;	/* Token offset in original string */
88 } TOK_ELT;
89 typedef struct {
90 	size_t	tokst_cmd_len;	/* Length of original user command, without */
91 				/*	newline or NULL termination chars */
92 	size_t	tokst_str_size;	/* Space needed to hold all the resulting */
93 				/*	tokens, including terminating NULL */
94 	TOK_ELT	*tokst_buf;	/* The array of tokens */
95 	size_t	tokst_cnt;	/* # of tokens in array */
96 	size_t	tokst_bufsize;	/* capacity of array */
97 } TOK_STATE;
98 
99 
100 
101 
102 /* State block used by gettok_init() and gettok() */
103 typedef struct {
104 	const char	*gtok_buf;	/* Addr of buffer containing string */
105 	char		*gtok_cur_buf;	/* Addr withing buffer for next token */
106 	int		gtok_inc_null_final; /* True if final NULL token used */
107 	int		gtok_null_seen;	/* True when NULL byte seen */
108 	TOK_ELT		gtok_last_token; /* Last token parsed */
109 
110 } GETTOK_STATE;
111 
112 
113 
114 
115 /*
116  * The elfedit_cpl_*() functions are used for command line completion.
117  * Currently this uses the tecla library, but to allow for changing the
118  * library used, we hide all tecla interfaces from our modules. Instead,
119  * cmd_match_fcn() builds an ELFEDIT_CPL_STATE struct, and we pass the
120  * address of that struct as an opaque handle to the modules. Since the
121  * pointer is opaque, the contents of ELFEDIT_CPL_STATE are free to change
122  * as necessary.
123  */
124 typedef struct {
125 	WordCompletion	*ecpl_cpl;		/* tecla handle */
126 	const char	*ecpl_line;		/* raw input line */
127 	int		ecpl_word_start;	/* start offset within line */
128 	int		ecpl_word_end;		/* offset just past token */
129 	/*
130 	 * ecpl_add_mod_colon is a secret handshake between
131 	 * elfedit_cpl_command() and  elfedit_cpl_add_match(). It adds
132 	 * ':' to end of matched modules.
133 	 */
134 	int		ecpl_add_mod_colon;
135 	const char	*ecpl_token_str;	/* token being completed */
136 	size_t		ecpl_token_len;		/* strlen(ecpl_token_str) */
137 } ELFEDIT_CPL_STATE;
138 
139 
140 
141 
142 /* This structure maintains elfedit global state */
143 STATE_T state;
144 
145 
146 
147 /*
148  * Define a pair of static global variables that contain the
149  * ISA strings that correspond to %i and %I tokens in module search
150  * paths.
151  *
152  *	isa_i_str - The ISA string for the currently running program
153  *	isa_I_str - For 64-bit programs, the same as isa_i_str. For
154  *		32-bit programs, an empty string.
155  */
156 #ifdef __sparc
157 #ifdef __sparcv9
158 static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_64);
159 static const char *isa_I_str = MSG_ORIG(MSG_ISA_SPARC_64);
160 #else
161 static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_32);
162 static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY);
163 #endif
164 #endif
165 
166 #ifdef __i386
167 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_32);
168 static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY);
169 #endif
170 #ifdef __amd64
171 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_64);
172 static const char *isa_I_str = MSG_ORIG(MSG_ISA_X86_64);
173 #endif
174 
175 
176 
177 /* Forward declarations */
178 static void free_user_cmds(void);
179 static void elfedit_pager_cleanup(void);
180 
181 
182 
183 /*
184  * We supply this function for the msg module
185  */
186 const char *
_elfedit_msg(Msg mid)187 _elfedit_msg(Msg mid)
188 {
189 	return (gettext(MSG_ORIG(mid)));
190 }
191 
192 
193 /*
194  * Copy at most min(cpsize, dstsize-1) bytes from src into dst,
195  * truncating src if necessary.  The  result is always null-terminated.
196  *
197  * entry:
198  *	dst - Destination buffer
199  *	src - Source string
200  *	dstsize - sizeof(dst)
201  *
202  * note:
203  *	This is similar to strncpy(), but with two modifications:
204  *	1) You specify the number of characters to copy, not just
205  *		the size of the destination. Hence, you can copy non-NULL
206  *		terminated strings.
207  *	2) The destination is guaranteed to be NULL terminated. strncpy()
208  *		does not terminate a completely full buffer.
209  */
210 static void
elfedit_strnbcpy(char * dst,const char * src,size_t cpsize,size_t dstsize)211 elfedit_strnbcpy(char *dst, const char *src, size_t cpsize, size_t dstsize)
212 {
213 	if (cpsize >= dstsize)
214 		cpsize = dstsize - 1;
215 	if (cpsize > 0)
216 		(void) strncpy(dst, src, cpsize + 1);
217 	dst[cpsize] = '\0';
218 }
219 
220 
221 /*
222  * Calls exit() on behalf of elfedit.
223  */
224 void
elfedit_exit(int status)225 elfedit_exit(int status)
226 {
227 	if (state.file.present) {
228 		/* Exiting with unflushed changes pending? Issue debug notice */
229 		if (state.file.dirty)
230 			elfedit_msg(ELFEDIT_MSG_DEBUG,
231 			    MSG_INTL(MSG_DEBUG_DIRTYEXIT));
232 
233 		/*
234 		 * If the edit file is marked for unlink on exit, then
235 		 * take care of it here.
236 		 */
237 		if (state.file.unlink_on_exit) {
238 			elfedit_msg(ELFEDIT_MSG_DEBUG,
239 			    MSG_INTL(MSG_DEBUG_UNLINKFILE),
240 			    state.file.outfile);
241 			(void) unlink(state.file.outfile);
242 		}
243 	}
244 
245 	exit(status);
246 }
247 
248 
249 /*
250  * Standard message function for elfedit. All user visible
251  * output, for error or informational reasons, should go through
252  * this function.
253  *
254  * entry:
255  *	type - Type of message. One of the ELFEDIT_MSG_* values.
256  *	format, ... - As per the printf() family
257  *
258  * exit:
259  *	The desired message has been output. For informational
260  *	messages, control returns to the caller. For errors,
261  *	this routine will terminate execution or strip the execution
262  *	stack and return control directly to the outer control loop.
263  *	In either case, the caller will not receive control.
264  */
265 /*PRINTFLIKE2*/
266 void
elfedit_msg(elfedit_msg_t type,const char * format,...)267 elfedit_msg(elfedit_msg_t type, const char *format, ...)
268 {
269 	typedef enum {			/* What to do after finished */
270 		DISP_RET = 0,		/* Return to caller */
271 		DISP_JMP = 1,		/* if (interactive) longjmp else exit */
272 		DISP_EXIT = 2		/* exit under all circumstances */
273 	} DISP;
274 
275 	va_list args;
276 	FILE *stream = stderr;
277 	DISP disp = DISP_RET;
278 	int do_output = 1;
279 	int need_prefix = 1;
280 
281 	va_start(args, format);
282 
283 	switch (type) {
284 	case ELFEDIT_MSG_ERR:
285 	case ELFEDIT_MSG_CMDUSAGE:
286 		disp = DISP_JMP;
287 		break;
288 	case ELFEDIT_MSG_FATAL:
289 		disp = DISP_EXIT;
290 		break;
291 	case ELFEDIT_MSG_USAGE:
292 		need_prefix = 0;
293 		break;
294 	case ELFEDIT_MSG_DEBUG:
295 		if (!(state.flags & ELFEDIT_F_DEBUG))
296 			return;
297 		stream = stdout;
298 		break;
299 	case ELFEDIT_MSG_QUIET:
300 		do_output = 0;
301 		disp = DISP_JMP;
302 		break;
303 	}
304 
305 
306 	/*
307 	 * If there is a pager process running, we are returning to the
308 	 * caller, and the output is going to stdout, then let the
309 	 * pager handle it instead of writing it directly from this process.
310 	 * That way, the output gets paged along with everything else.
311 	 *
312 	 * If there is a pager process running, and we are not returning
313 	 * to the caller, then end the pager process now, before we generate
314 	 * any new output. This allows for any text buffered in the pager
315 	 * pipe to be output before the new stuff.
316 	 */
317 	if (state.pager.fptr != NULL) {
318 		if (disp == DISP_RET) {
319 			if (stream == stdout)
320 				stream = state.pager.fptr;
321 		} else {
322 			elfedit_pager_cleanup();
323 		}
324 	}
325 
326 	/*
327 	 * If this message is coming from within the libtecla command
328 	 * completion code, call gl_normal_io() to give the library notice.
329 	 * That function sets the tty back to cooked mode and advances
330 	 * the cursor to the beginning of the next line so that our output
331 	 * will appear properly. When we return to the command completion code,
332 	 * tecla will re-enter raw mode and redraw the current command line.
333 	 */
334 	if (state.input.in_tecla)
335 		(void) gl_normal_io(state.input.gl);
336 
337 	if (do_output) {
338 		if (need_prefix)
339 			(void) fprintf(stream, MSG_ORIG(MSG_STR_ELFEDIT));
340 		(void) vfprintf(stream, format, args);
341 		(void) fflush(stream);
342 	}
343 	va_end(args);
344 
345 	/*
346 	 * If this is an error, then we do not return to the caller.
347 	 * The action taken depends on whether the outer loop has registered
348 	 * a jump buffer for us or not.
349 	 */
350 	if (disp != DISP_RET) {
351 		if (state.msg_jbuf.active && (disp == DISP_JMP)) {
352 			/* Free the user command list */
353 			free_user_cmds();
354 
355 			/* Clean up to reflect effect of non-local goto */
356 			state.input.in_tecla = FALSE;
357 
358 			/* Jump to the outer loop to resume */
359 			siglongjmp(state.msg_jbuf.env, 1);
360 		} else {
361 			elfedit_exit(1);
362 		}
363 	}
364 }
365 
366 
367 /*
368  * Wrapper on elfedit_msg() that issues an error that results from
369  * a call to libelf.
370  *
371  * entry:
372  *	file - Name of ELF object
373  *	libelf_rtn_name - Name of routine that was called
374  *
375  * exit:
376  *	An error has been issued that shows the routine called
377  *	and the libelf error string for it from elf_errmsg().
378  *	This routine does not return to the caller.
379  */
380 void
elfedit_elferr(const char * file,const char * libelf_rtn_name)381 elfedit_elferr(const char *file, const char *libelf_rtn_name)
382 {
383 	const char *errstr = elf_errmsg(elf_errno());
384 
385 	elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_LIBELF), file,
386 	    libelf_rtn_name, errstr ? errstr : MSG_INTL(MSG_FMT_UNKNOWN));
387 }
388 
389 
390 /*
391  * Start an output pager process for elfedit_printf()/elfedit_write() to use.
392  *
393  * note:
394  *	If this elfedit session is not interactive, then no pager is
395  *	started. Paging is only intended for interactive use. The caller
396  *	is not supposed to worry about this point, but simply to use
397  *	this function to flag situations in which paging might be needed.
398  */
399 void
elfedit_pager_init(void)400 elfedit_pager_init(void)
401 {
402 	const char	*errstr;
403 	const char	*cmd;
404 	int		err;
405 
406 	/*
407 	 * If there is no pager process running, start one.
408 	 * Only do this for interactive sessions --- elfedit_pager()
409 	 * won't use a pager in batch mode.
410 	 */
411 	if (state.msg_jbuf.active && state.input.full_tty &&
412 	    (state.pager.fptr == NULL)) {
413 		/*
414 		 * If the user has the PAGER environment variable set,
415 		 * then we will use that program. Otherwise we default
416 		 * to /bin/more.
417 		 */
418 		cmd = getenv(MSG_ORIG(MSG_STR_PAGER));
419 		if ((cmd == NULL) || (*cmd == '\0'))
420 			cmd = MSG_ORIG(MSG_STR_BINMORE);
421 
422 		/*
423 		 * The popen() manpage says that on failure, it "may set errno",
424 		 * which is somewhat ambiguous. We explicitly zero it here, and
425 		 * assume that any change is due to popen() failing.
426 		 */
427 		errno = 0;
428 		state.pager.fptr = popen(cmd, MSG_ORIG(MSG_STR_W));
429 		if (state.pager.fptr == NULL) {
430 			err = errno;
431 			errstr = (err == 0) ? MSG_INTL(MSG_ERR_UNKNOWNSYSERR) :
432 			    strerror(err);
433 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTEXEC),
434 			    MSG_ORIG(MSG_STR_ELFEDIT), cmd, errstr);
435 		}
436 	}
437 }
438 
439 
440 /*
441  * If there is a pager process present, close it out.
442  *
443  * note:
444  *	This function is called from within elfedit_msg(), and as
445  *	such, must not use elfedit_msg() to report errors. Furthermore,
446  *	any such errors are not a sufficient reason to terminate the process
447  *	or to longjmp(). This is a rare case where errors are written
448  *	directly to stderr.
449  */
450 static void
elfedit_pager_cleanup(void)451 elfedit_pager_cleanup(void)
452 {
453 	if (state.pager.fptr != NULL) {
454 		if (pclose(state.pager.fptr) == -1)
455 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_PAGERFINI));
456 
457 		state.pager.fptr = NULL;
458 	}
459 }
460 
461 
462 /*
463  * Print general formtted text for the user, using printf()-style
464  * formatting. Uses the pager process if one has been started, or
465  * stdout otherwise.
466  */
467 void
elfedit_printf(const char * format,...)468 elfedit_printf(const char *format, ...)
469 {
470 	va_list	args;
471 	int	err;
472 	FILE	*fptr;
473 	int	pager;
474 	int	broken_pipe = 0;
475 
476 	/*
477 	 * If there is a pager process, then use it. Otherwise write
478 	 * directly to stdout.
479 	 */
480 	pager = (state.pager.fptr != NULL);
481 	fptr = pager ? state.pager.fptr : stdout;
482 
483 	va_start(args, format);
484 	errno = 0;
485 	err = vfprintf(fptr, format, args);
486 
487 	/* Did we fail because a child pager process has exited? */
488 	broken_pipe = pager && (err < 0) && (errno == EPIPE);
489 
490 	va_end(args);
491 
492 	/*
493 	 * On error, we simply issue the error without cleaning up
494 	 * the pager process. The message code handles that as a standard
495 	 * part of error processing.
496 	 *
497 	 * We handle failure due to an exited pager process differently
498 	 * than a normal error, because it is usually due to the user
499 	 * intentionally telling it to.
500 	 */
501 	if (err < 0) {
502 		if (broken_pipe)
503 			elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL));
504 		else
505 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF));
506 	}
507 }
508 
509 
510 /*
511  * Some our modules use liblddb routines to format ELF output.
512  * In order to ensure that such output is sent to the pager pipe
513  * when there is one, and stdout otherwise, we redefine the dbg_print()
514  * function here.
515  *
516  * This item should be defined NODIRECT.
517  */
518 /* PRINTFLIKE2 */
519 void
dbg_print(Lm_list * lml,const char * format,...)520 dbg_print(Lm_list *lml, const char *format, ...)
521 {
522 	va_list	ap;
523 	int	err;
524 	FILE	*fptr;
525 	int	pager;
526 	int	broken_pipe = 0;
527 
528 #if	defined(lint)
529 	/*
530 	 * The lml argument is only meaningful for diagnostics sent to ld.so.1.
531 	 * Supress the lint error by making a dummy assignment.
532 	 */
533 	lml = 0;
534 #endif
535 
536 	/*
537 	 * If there is a pager process, then use it. Otherwise write
538 	 * directly to stdout.
539 	 */
540 	pager = (state.pager.fptr != NULL);
541 	fptr = pager ? state.pager.fptr : stdout;
542 
543 	va_start(ap, format);
544 	errno = 0;
545 	err = vfprintf(fptr, format, ap);
546 	if (err >= 0)
547 		err = fprintf(fptr, MSG_ORIG(MSG_STR_NL));
548 
549 	/* Did we fail because a child pager process has exited? */
550 	broken_pipe = (err < 0) && pager && (errno == EPIPE);
551 
552 	va_end(ap);
553 
554 	/*
555 	 * On error, we simply issue the error without cleaning up
556 	 * the pager process. The message code handles that as a standard
557 	 * part of error processing.
558 	 *
559 	 * We handle failure due to an exited pager process differently
560 	 * than a normal error, because it is usually due to the user
561 	 * intentionally telling it to.
562 	 */
563 	if (err < 0) {
564 		if (broken_pipe)
565 			elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL));
566 		else
567 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF));
568 	}
569 }
570 
571 
572 /*
573  * Write raw bytes of text in a manner similar to fwrite().
574  * Uses the pager process if one has been started, or
575  * stdout otherwise.
576  */
577 void
elfedit_write(const void * ptr,size_t size)578 elfedit_write(const void *ptr, size_t size)
579 {
580 	FILE	*fptr;
581 	int	err;
582 
583 	/*
584 	 * If there is a pager process, then use it. Otherwise write
585 	 * directly to stdout.
586 	 */
587 	fptr = (state.pager.fptr == NULL) ? stdout : state.pager.fptr;
588 
589 	if (fwrite(ptr, 1, size, fptr) != size) {
590 		err = errno;
591 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_FWRITE),
592 		    strerror(err));
593 	}
594 }
595 
596 
597 /*
598  * Convert the NULL terminated string to the form used by the C
599  * language to represent literal strings. See conv_str_to_c_literal()
600  * for details.
601  *
602  * This routine differs from conv_str_to_c_literal() in two ways:
603  *	1) String is NULL terminated instead of counted
604  *	2) Signature of outfunc
605  *
606  * entry:
607  *	str - String to be processed
608  *	outfunc - Function to be called to move output characters. Note
609  *		that this function has the same signature as elfedit_write(),
610  *		and that function can be used to write the characters to
611  *		the output.
612  *
613  * exit:
614  *	The string has been processed, with the resulting data passed
615  *	to outfunc for processing.
616  */
617 static void
elfedit_str_to_c_literal_cb(const void * ptr,size_t size,void * uvalue)618 elfedit_str_to_c_literal_cb(const void *ptr, size_t size, void *uvalue)
619 {
620 	elfedit_write_func_t *outfunc = (elfedit_write_func_t *)uvalue;
621 
622 	(* outfunc)(ptr, size);
623 
624 }
625 void
elfedit_str_to_c_literal(const char * str,elfedit_write_func_t * outfunc)626 elfedit_str_to_c_literal(const char *str, elfedit_write_func_t *outfunc)
627 {
628 	conv_str_to_c_literal(str, strlen(str),
629 	    elfedit_str_to_c_literal_cb, (void *) outfunc);
630 }
631 
632 
633 /*
634  * Wrappers on malloc() and realloc() that check the result for success
635  * and issue an error if not. The caller can use the result of these
636  * functions without checking for a NULL pointer, as we do not return to
637  * the caller in the failure case.
638  */
639 void *
elfedit_malloc(const char * item_name,size_t size)640 elfedit_malloc(const char *item_name, size_t size)
641 {
642 	void *m;
643 
644 	m = malloc(size);
645 	if (m == NULL) {
646 		int err = errno;
647 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC),
648 		    item_name, strerror(err));
649 	}
650 
651 	return (m);
652 }
653 
654 void *
elfedit_realloc(const char * item_name,void * ptr,size_t size)655 elfedit_realloc(const char *item_name, void *ptr, size_t size)
656 {
657 	void *m;
658 
659 	m = realloc(ptr, size);
660 	if (m == NULL) {
661 		int err = errno;
662 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC),
663 		    item_name, strerror(err));
664 	}
665 
666 	return (m);
667 }
668 
669 
670 /*
671  * Ensure that the given buffer has room for n bytes of data.
672  */
673 static void
strbuf_ensure_size(STRBUF * str,size_t size)674 strbuf_ensure_size(STRBUF *str, size_t size)
675 {
676 #define	INITIAL_STR_ALLOC 128
677 
678 	size_t n;
679 
680 	n = (str->n == 0) ? INITIAL_STR_ALLOC : str->n;
681 	while (size > n)	/* Double buffer until string fits */
682 		n *= 2;
683 	if (n != str->n) {		/* Alloc new string buffer if needed */
684 		str->buf = elfedit_realloc(MSG_INTL(MSG_ALLOC_UCMDSTR),
685 		    str->buf, n);
686 		str->n = n;
687 	}
688 
689 #undef	INITIAL_STR_ALLOC
690 }
691 
692 
693 /*
694  * Extract the argument/option information for the next item referenced
695  * by optarg, and advance the pointer to the next item.
696  *
697  * entry:
698  *	optarg - Address of pointer to argument or option array
699  *	item - Struct to be filled in.
700  *
701  * exit:
702  *	The item block has been filled in with the information for
703  *	the next item in the optarg array. *optarg has been advanced
704  *	to the next item.
705  */
706 void
elfedit_next_optarg(elfedit_cmd_optarg_t ** optarg,elfedit_optarg_item_t * item)707 elfedit_next_optarg(elfedit_cmd_optarg_t **optarg, elfedit_optarg_item_t *item)
708 {
709 	/*
710 	 * Array of inheritable options/arguments. Indexed by one less
711 	 * than the corresponding ELFEDIT_STDOA_ value.
712 	 */
713 	static const elfedit_optarg_item_t stdoa[] = {
714 		/* ELFEDIT_STDOA_O */
715 		{ MSG_ORIG(MSG_STR_MINUS_O), MSG_ORIG(MSG_STR_OUTSTYLE),
716 		    /* MSG_INTL(MSG_STDOA_OPTDESC_O) */
717 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_O,
718 		    ELFEDIT_CMDOA_F_VALUE },
719 
720 		/* ELFEDIT_STDOA_AND */
721 		{ MSG_ORIG(MSG_STR_MINUS_AND), NULL,
722 		    /* MSG_INTL(MSG_STDOA_OPTDESC_AND) */
723 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_AND, 0 },
724 
725 		/* ELFEDIT_STDOA_CMP */
726 		{ MSG_ORIG(MSG_STR_MINUS_CMP), NULL,
727 		    /* MSG_INTL(MSG_STDOA_OPTDESC_CMP) */
728 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_CMP, 0 },
729 
730 		/* ELFEDIT_STDOA_OR */
731 		{ MSG_ORIG(MSG_STR_MINUS_OR), NULL,
732 		    /* MSG_INTL(MSG_STDOA_OPTDESC_OR) */
733 		    (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_OR, 0 },
734 	};
735 
736 	elfedit_cmd_optarg_t *oa;
737 
738 
739 	/* Grab first item, advance the callers pointer over it */
740 	oa = (*optarg)++;
741 
742 	if (oa->oa_flags & ELFEDIT_CMDOA_F_INHERIT) {
743 		/* Values are pre-chewed in the stdoa array above */
744 		*item = stdoa[((uintptr_t)oa->oa_name) - 1];
745 
746 		/*
747 		 * Set the inherited flag so that elfedit_optarg_helpstr()
748 		 * can tell who is responsible for translating the help string.
749 		 */
750 		item->oai_flags |= ELFEDIT_CMDOA_F_INHERIT;
751 	} else {	/* Non-inherited item */
752 		item->oai_name = oa->oa_name;
753 		if ((oa->oa_flags & ELFEDIT_CMDOA_F_VALUE) != 0) {
754 			item->oai_vname = oa[1].oa_name;
755 
756 			/* Advance users pointer past value element */
757 			(*optarg)++;
758 		} else {
759 			item->oai_vname = NULL;
760 		}
761 		item->oai_help = oa->oa_help;
762 		item->oai_flags = oa->oa_flags;
763 	}
764 
765 	/*
766 	 * The module determines the idmask and excmask fields whether
767 	 * or not inheritance is in play.
768 	 */
769 	item->oai_idmask = oa->oa_idmask;
770 	item->oai_excmask = oa->oa_excmask;
771 }
772 
773 
774 
775 /*
776  * Return the help string for an option/argument item, as returned
777  * by elfedit_next_optarg(). This routine handles the details of
778  * knowing whether the string is provided by elfedit itself (inherited),
779  * or needs to be translated by the module.
780  */
781 const char *
elfedit_optarg_helpstr(elfeditGC_module_t * mod,elfedit_optarg_item_t * item)782 elfedit_optarg_helpstr(elfeditGC_module_t *mod, elfedit_optarg_item_t *item)
783 {
784 	/*
785 	 * The help string from an inherited item comes right out
786 	 * of the main elfedit string table.
787 	 */
788 	if (item->oai_flags & ELFEDIT_CMDOA_F_INHERIT)
789 		return (MSG_INTL((Msg) item->oai_help));
790 
791 	/*
792 	 * If the string is defined by the module, then we need to
793 	 * have the module translate it for us.
794 	 */
795 	return ((* mod->mod_i18nhdl_to_str)(item->oai_help));
796 }
797 
798 
799 
800 /*
801  * Used by usage_optarg() to insert a character into the output buffer,
802  * advancing the buffer pointer and current column, and reducing the
803  * amount of remaining space.
804  */
805 static void
usage_optarg_insert_ch(int ch,char ** cur,size_t * n,size_t * cur_col)806 usage_optarg_insert_ch(int ch, char **cur, size_t *n, size_t *cur_col)
807 {
808 
809 	*(*cur)++ = ch;
810 	**cur = '\0';
811 	(*n)--;
812 	(*cur_col)++;
813 }
814 
815 /*
816  * Used by usage_optarg() to insert a string into the output
817  * buffer, advancing the buffer pointer and current column, and reducing
818  * the amount of remaining space.
819  */
820 static void
usage_optarg_insert_str(char ** cur,size_t * n,size_t * cur_col,const char * format,...)821 usage_optarg_insert_str(char **cur, size_t *n, size_t *cur_col,
822     const char *format, ...)
823 {
824 	size_t len;
825 	va_list args;
826 
827 	va_start(args, format);
828 	len = vsnprintf(*cur, *n, format, args);
829 	va_end(args);
830 
831 	*cur += len;
832 	*n -= len;
833 	*cur_col += len;
834 }
835 /*
836  * Used by usage_optarg() to insert an optarg item string into the output
837  * buffer, advancing the buffer pointer and current column, and reducing
838  * the amount of remaining space.
839  */
840 static void
usage_optarg_insert_item(elfedit_optarg_item_t * item,char ** cur,size_t * n,size_t * cur_col)841 usage_optarg_insert_item(elfedit_optarg_item_t *item, char **cur,
842     size_t *n, size_t *cur_col)
843 {
844 	size_t len;
845 
846 	if (item->oai_flags & ELFEDIT_CMDOA_F_VALUE) {
847 		len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG2),
848 		    item->oai_name, item->oai_vname);
849 	} else {
850 		len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG),
851 		    item->oai_name);
852 	}
853 	*cur += len;
854 	*n -= len;
855 	*cur_col += len;
856 }
857 
858 
859 
860 /*
861  * Write the options/arguments to the usage string.
862  *
863  * entry:
864  *	main_buf_n - Size of main buffer from which buf and buf_n are
865  *		allocated.
866  *	buf - Address of pointer to where next item is to be placed.
867  *	buf_n - Address of count of remaining bytes in buffer
868  *	buf_cur_col - Address of current output column for current line
869  *		of generated string.
870  *	optarg - Options list
871  *	isopt - True if these are options, false for arguments.
872  *	wrap_str - String to indent wrapped lines. If NULL, lines
873  *		are not wrapped
874  */
875 static void
usage_optarg(size_t main_buf_n,char ** buf,size_t * buf_n,size_t * buf_cur_col,elfedit_cmd_optarg_t * optarg,int isopt,const char * wrap_str)876 usage_optarg(size_t main_buf_n, char **buf, size_t *buf_n, size_t *buf_cur_col,
877     elfedit_cmd_optarg_t *optarg, int isopt, const char *wrap_str)
878 {
879 	/*
880 	 * An option can be combined into a simple format if it lacks
881 	 * these flags and is only one character in length.
882 	 */
883 	static const elfedit_cmd_oa_flag_t exflags =
884 	    (ELFEDIT_CMDOA_F_VALUE | ELFEDIT_CMDOA_F_MULT);
885 
886 	/*
887 	 * A static buffer, which is grown as needed to accomodate
888 	 * the maximum usage string seen.
889 	 */
890 	static STRBUF simple_str;
891 
892 	char			*cur = *buf;
893 	size_t			n = *buf_n;
894 	size_t			cur_col = *buf_cur_col;
895 	int			len;
896 	int			use_simple = 0;
897 	elfedit_optarg_item_t	item;
898 	elfedit_cmd_oa_mask_t	optmask = 0;
899 	int			use_bkt;
900 
901 	/*
902 	 * If processing options, pull the 1-character ones that don't have
903 	 * an associated value and don't have any mutual exclusion issues into
904 	 * a single combination string to go at the beginning of the usage.
905 	 */
906 	if (isopt) {
907 		elfedit_cmd_optarg_t *tmp_optarg = optarg;
908 		char *s;
909 
910 		/*
911 		 * The simple string is guaranteed to fit in the same
912 		 * amount of space reserved for the main buffer.
913 		 */
914 		strbuf_ensure_size(&simple_str, main_buf_n);
915 		s = simple_str.buf;
916 		*s++ = ' ';
917 		*s++ = '[';
918 		*s++ = '-';
919 		while (tmp_optarg->oa_name != NULL) {
920 			elfedit_next_optarg(&tmp_optarg, &item);
921 			if (((item.oai_flags & exflags) == 0) &&
922 			    (item.oai_name[2] == '\0') &&
923 			    (item.oai_excmask == 0)) {
924 				optmask |= item.oai_idmask;
925 				*s++ = item.oai_name[1];
926 			}
927 		}
928 
929 		/*
930 		 * If we found more than one, then finish the string and
931 		 * add it. Don't do this for a single option, because
932 		 * it looks better in that case if the option shows up
933 		 * in alphabetical order rather than being hoisted.
934 		 */
935 		use_simple = (s > (simple_str.buf + 4));
936 		if (use_simple) {
937 			*s++ = ']';
938 			*s++ = '\0';
939 			usage_optarg_insert_str(&cur, &n, &cur_col,
940 			    MSG_ORIG(MSG_STR_HLPOPTARG), simple_str.buf);
941 		} else {
942 			/* Not using it, so reset the cumulative options mask */
943 			optmask = 0;
944 		}
945 	}
946 
947 	while (optarg->oa_name != NULL) {
948 		elfedit_next_optarg(&optarg, &item);
949 
950 		if (isopt) {
951 			/*
952 			 * If this is an option that was pulled into the
953 			 * combination string above, then skip over it.
954 			 */
955 			if (use_simple && ((item.oai_flags & exflags) == 0) &&
956 			    (item.oai_name[2] == '\0') &&
957 			    (item.oai_excmask == 0))
958 				continue;
959 
960 			/*
961 			 * If this is a mutual exclusion option that was
962 			 * picked up out of order by a previous iteration
963 			 * of this loop, then skip over it.
964 			 */
965 			if ((optmask & item.oai_idmask) != 0)
966 				continue;
967 
968 			/* Add this item to the accumulating options mask */
969 			optmask |= item.oai_idmask;
970 		}
971 
972 		/* Wrap line, or insert blank separator */
973 		if ((wrap_str != NULL) && (cur_col > USAGE_WRAP_COL)) {
974 			len = snprintf(cur, n, MSG_ORIG(MSG_FMT_WRAPUSAGE),
975 			    wrap_str);
976 			cur += len;
977 			n -= len;
978 			cur_col = len - 1;   /* Don't count the newline */
979 		} else {
980 			usage_optarg_insert_ch(' ', &cur, &n, &cur_col);
981 		}
982 
983 		use_bkt = (item.oai_flags & ELFEDIT_CMDOA_F_OPT) || isopt;
984 		if (use_bkt)
985 			usage_optarg_insert_ch('[', &cur, &n, &cur_col);
986 
987 		/* Add the item to the buffer */
988 		usage_optarg_insert_item(&item, &cur, &n, &cur_col);
989 
990 		/*
991 		 * If this item has a non-zero mutual exclusion mask,
992 		 * then look for the other items and display them all
993 		 * together with alternation (|). Note that plain arguments
994 		 * cannot have a non-0 exclusion mask, so this is
995 		 * effectively options-only (isopt != 0).
996 		 */
997 		if (item.oai_excmask != 0) {
998 			elfedit_cmd_optarg_t *tmp_optarg = optarg;
999 			elfedit_optarg_item_t tmp_item;
1000 
1001 			/*
1002 			 * When showing alternation, elipses for multiple
1003 			 * copies need to appear inside the [] brackets.
1004 			 */
1005 			if (item.oai_flags & ELFEDIT_CMDOA_F_MULT)
1006 				usage_optarg_insert_str(&cur, &n, &cur_col,
1007 				    MSG_ORIG(MSG_STR_ELIPSES));
1008 
1009 
1010 			while (tmp_optarg->oa_name != NULL) {
1011 				elfedit_next_optarg(&tmp_optarg, &tmp_item);
1012 				if ((item.oai_excmask & tmp_item.oai_idmask) ==
1013 				    0)
1014 					continue;
1015 				usage_optarg_insert_str(&cur, &n, &cur_col,
1016 				    MSG_ORIG(MSG_STR_SP_BAR_SP));
1017 				usage_optarg_insert_item(&tmp_item,
1018 				    &cur, &n, &cur_col);
1019 
1020 				/*
1021 				 * Add it to the mask of seen options.
1022 				 * This will keep us from showing it twice.
1023 				 */
1024 				optmask |= tmp_item.oai_idmask;
1025 			}
1026 		}
1027 		if (use_bkt)
1028 			usage_optarg_insert_ch(']', &cur, &n, &cur_col);
1029 
1030 		/*
1031 		 * If alternation was not shown above (non-zero exclusion mask)
1032 		 * then the elipses for multiple copies are shown outside
1033 		 * any [] brackets.
1034 		 */
1035 		if ((item.oai_excmask == 0) &&
1036 		    (item.oai_flags & ELFEDIT_CMDOA_F_MULT))
1037 			usage_optarg_insert_str(&cur, &n, &cur_col,
1038 			    MSG_ORIG(MSG_STR_ELIPSES));
1039 
1040 	}
1041 
1042 	*buf = cur;
1043 	*buf_n = n;
1044 	*buf_cur_col = cur_col;
1045 }
1046 
1047 
1048 
1049 /*
1050  * Format the usage string for a command into a static buffer and
1051  * return the pointer to the user. The resultant string is valid
1052  * until the next call to this routine, and which point it
1053  * will be overwritten or the memory is freed.
1054  *
1055  * entry:
1056  *	mod, cmd - Module and command definitions for command to be described
1057  *	wrap_str - NULL, or string to be used to indent when
1058  *		lines are wrapped. If NULL, no wrapping is done, and
1059  *		all output is on a single line.
1060  *	cur_col - Starting column at which the string will be displayed.
1061  *		Ignored if wrap_str is NULL.
1062  */
1063 const char *
elfedit_format_command_usage(elfeditGC_module_t * mod,elfeditGC_cmd_t * cmd,const char * wrap_str,size_t cur_col)1064 elfedit_format_command_usage(elfeditGC_module_t *mod, elfeditGC_cmd_t *cmd,
1065     const char *wrap_str, size_t cur_col)
1066 {
1067 
1068 	/*
1069 	 * A static buffer, which is grown as needed to accomodate
1070 	 * the maximum usage string seen.
1071 	 */
1072 	static STRBUF str;
1073 
1074 	elfedit_cmd_optarg_t	*optarg;
1075 	size_t			len, n, elipses_len;
1076 	char			*cur;
1077 	elfedit_optarg_item_t	item;
1078 
1079 	/*
1080 	 * Estimate a worst case size for the usage string:
1081 	 *	- module name
1082 	 *	- lengths of the strings
1083 	 *	- every option or argument is enclosed in brackets
1084 	 *	- space in between each item, with an alternation (" | ")
1085 	 *	- elipses will be displayed with each option and argument
1086 	 */
1087 	n = strlen(mod->mod_name) + strlen(cmd->cmd_name[0]) + 6;
1088 	elipses_len = strlen(MSG_ORIG(MSG_STR_ELIPSES));
1089 	if ((optarg = cmd->cmd_opt) != NULL)
1090 		while (optarg->oa_name != NULL) {
1091 			elfedit_next_optarg(&optarg, &item);
1092 			n += strlen(item.oai_name) + 5 + elipses_len;
1093 		}
1094 	if ((optarg = cmd->cmd_args) != NULL)
1095 		while (optarg->oa_name != NULL) {
1096 			elfedit_next_optarg(&optarg, &item);
1097 			n += strlen(item.oai_name) + 5 + elipses_len;
1098 		}
1099 	n++;			/* Null termination */
1100 
1101 	/*
1102 	 * If wrapping lines, we insert a newline and then wrap_str
1103 	 * every USAGE_WRAP_COL characters.
1104 	 */
1105 	if (wrap_str != NULL)
1106 		n += ((n + USAGE_WRAP_COL) / USAGE_WRAP_COL) *
1107 		    (strlen(wrap_str) + 1);
1108 
1109 	strbuf_ensure_size(&str, n);
1110 
1111 	/* Command name */
1112 	cur = str.buf;
1113 	n = str.n;
1114 	if (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) == 0)
1115 		len = snprintf(cur, n, MSG_ORIG(MSG_FMT_SYSCMD),
1116 		    cmd->cmd_name[0]);
1117 	else
1118 		len = snprintf(cur, n, MSG_ORIG(MSG_FMT_MODCMD),
1119 		    mod->mod_name, cmd->cmd_name[0]);
1120 	cur += len;
1121 	n -= len;
1122 	cur_col += len;
1123 
1124 	if (cmd->cmd_opt != NULL)
1125 		usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_opt,
1126 		    1, wrap_str);
1127 	if (cmd->cmd_args != NULL)
1128 		usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_args,
1129 		    0, wrap_str);
1130 
1131 	return (str.buf);
1132 }
1133 
1134 /*
1135  * Wrapper on elfedit_msg() that issues an ELFEDIT_MSG_USAGE
1136  * error giving usage information for the command currently
1137  * referenced by state.cur_cmd.
1138  */
1139 void
elfedit_command_usage(void)1140 elfedit_command_usage(void)
1141 {
1142 	elfedit_msg(ELFEDIT_MSG_CMDUSAGE, MSG_INTL(MSG_USAGE_CMD),
1143 	    elfedit_format_command_usage(state.cur_cmd->ucmd_mod,
1144 	    state.cur_cmd->ucmd_cmd, NULL, 0));
1145 }
1146 
1147 
1148 /*
1149  * This function allows the loadable modules to get the command line
1150  * flags.
1151  */
1152 elfedit_flag_t
elfedit_flags(void)1153 elfedit_flags(void)
1154 {
1155 	return (state.flags);
1156 }
1157 
1158 /*
1159  * This function is used to register a per-command invocation output style
1160  * that will momentarily override the global output style for the duration
1161  * of the current command. This function must only be called by an
1162  * active command.
1163  *
1164  * entry:
1165  *	str - One of the valid strings for the output style
1166  */
1167 void
elfedit_set_cmd_outstyle(const char * str)1168 elfedit_set_cmd_outstyle(const char *str)
1169 {
1170 	if ((state.cur_cmd != NULL) && (str != NULL)) {
1171 		if (elfedit_atooutstyle(str, &state.cur_cmd->ucmd_ostyle) == 0)
1172 			elfedit_msg(ELFEDIT_MSG_ERR,
1173 			    MSG_INTL(MSG_ERR_BADOSTYLE), str);
1174 		state.cur_cmd->ucmd_ostyle_set = 1;
1175 	}
1176 }
1177 
1178 /*
1179  * This function allows the loadable modules to get the output style.
1180  */
1181 elfedit_outstyle_t
elfedit_outstyle(void)1182 elfedit_outstyle(void)
1183 {
1184 	/*
1185 	 * If there is an active  per-command output style,
1186 	 * return it.
1187 	 */
1188 	if ((state.cur_cmd != NULL) && (state.cur_cmd->ucmd_ostyle_set))
1189 		return (state.cur_cmd->ucmd_ostyle);
1190 
1191 
1192 	return (state.outstyle);
1193 }
1194 
1195 /*
1196  * Return the command descriptor of the currently executing command.
1197  * For use only by the modules or code called by the modules.
1198  */
1199 elfeditGC_cmd_t *
elfedit_curcmd(void)1200 elfedit_curcmd(void)
1201 {
1202 	return (state.cur_cmd->ucmd_cmd);
1203 }
1204 
1205 /*
1206  * Build a dynamically allocated elfedit_obj_state_t struct that
1207  * contains a cache of the ELF file contents. This pre-chewed form
1208  * is fed to each command, reducing the amount of ELF boilerplate
1209  * code each command needs to contain.
1210  *
1211  * entry:
1212  *	file - Name of file to process
1213  *
1214  * exit:
1215  *	Fills state.elf with the necessary information for the open file.
1216  *
1217  * note: The resulting elfedit_obj_state_t is allocated from a single
1218  *	piece of memory, such that a single call to free() suffices
1219  *	to release it as well as any memory it references.
1220  */
1221 static void
init_obj_state(const char * file)1222 init_obj_state(const char *file)
1223 {
1224 	int	fd;
1225 	Elf	*elf;
1226 	int	open_flag;
1227 
1228 	/*
1229 	 * In readonly mode, we open the file readonly so that it is
1230 	 * impossible to modify the file by accident. This also allows
1231 	 * us to access readonly files, perhaps in a case where we don't
1232 	 * intend to change it.
1233 	 *
1234 	 * We always use ELF_C_RDWR with elf_begin(), even in a readonly
1235 	 * session. This allows us to modify the in-memory image, which
1236 	 * can be useful when examining a file, even though we don't intend
1237 	 * to modify the on-disk data. The file is not writable in
1238 	 * this case, and we don't call elf_update(), so it is safe to do so.
1239 	 */
1240 	open_flag = ((state.flags & ELFEDIT_F_READONLY) ? O_RDONLY : O_RDWR);
1241 	if ((fd = open(file, open_flag)) == -1) {
1242 		int err = errno;
1243 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNFILE),
1244 		    file, strerror(err));
1245 	}
1246 	(void) elf_version(EV_CURRENT);
1247 	elf = elf_begin(fd, ELF_C_RDWR, NULL);
1248 	if (elf == NULL) {
1249 		(void) close(fd);
1250 		elfedit_elferr(file, MSG_ORIG(MSG_ELF_BEGIN));
1251 		/*NOTREACHED*/
1252 	}
1253 
1254 	/* We only handle standalone ELF files */
1255 	switch (elf_kind(elf)) {
1256 	case ELF_K_AR:
1257 		(void) close(fd);
1258 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOAR), file);
1259 		break;
1260 	case ELF_K_ELF:
1261 		break;
1262 	default:
1263 		(void) close(fd);
1264 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_UNRECELFFILE),
1265 		    file);
1266 		break;
1267 	}
1268 
1269 	/*
1270 	 * Tell libelf that we take responsibility for object layout.
1271 	 * Otherwise, it will compute "proper" values for layout and
1272 	 * alignment fields, and these values can overwrite the values
1273 	 * set in the elfedit session. We are modifying existing
1274 	 * objects --- the layout concerns have already been dealt
1275 	 * with when the object was built.
1276 	 */
1277 	(void) elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT);
1278 
1279 	/* Fill in state.elf.obj_state */
1280 	state.elf.elfclass = gelf_getclass(elf);
1281 	switch (state.elf.elfclass) {
1282 	case ELFCLASS32:
1283 		elfedit32_init_obj_state(file, fd, elf);
1284 		break;
1285 	case ELFCLASS64:
1286 		elfedit64_init_obj_state(file, fd, elf);
1287 		break;
1288 	default:
1289 		(void) close(fd);
1290 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADELFCLASS),
1291 		    file);
1292 		break;
1293 	}
1294 }
1295 
1296 
1297 #ifdef DEBUG_MODULE_LIST
1298 /*
1299  * Debug routine. Dump the module list to stdout.
1300  */
1301 static void
dbg_module_list(char * title)1302 dbg_module_list(char *title)
1303 {
1304 	MODLIST_T *m;
1305 
1306 	printf("<MODULE LIST: %s>\n", title);
1307 	for (m = state.modlist; m != NULL; m = m->next) {
1308 		printf("Module: >%s<\n", m->mod->mod_name);
1309 		printf("    hdl:  %llx\n", m->dl_hdl);
1310 		printf("    path: >%s<\n", m->path ? m->path : "<builtin>");
1311 	}
1312 	printf("<END OF MODULE LIST>\n");
1313 }
1314 #endif
1315 
1316 
1317 /*
1318  * Search the module list for the named module.
1319  *
1320  * entry:
1321  *	name - Name of module to find
1322  *	insdef - Address of variable to receive address of predecessor
1323  *		node to the desired one.
1324  *
1325  * exit:
1326  *	If the module is it is found, this routine returns the pointer to
1327  *	its MODLIST_T structure. *insdef references the predecessor node, or
1328  *	is NULL if the found item is at the head of the list.
1329  *
1330  *	If the module is not found, NULL is returned. *insdef references
1331  *	the predecessor node of the position where an entry for this module
1332  *	would be placed, or NULL if it would go at the beginning.
1333  */
1334 static MODLIST_T *
module_loaded(const char * name,MODLIST_T ** insdef)1335 module_loaded(const char *name, MODLIST_T **insdef)
1336 {
1337 	MODLIST_T	*moddef;
1338 	int		cmp;
1339 
1340 	*insdef = NULL;
1341 	moddef = state.modlist;
1342 	if (moddef != NULL) {
1343 		cmp = strcasecmp(name, moddef->ml_mod->mod_name);
1344 		if (cmp == 0) {		/* Desired module is first in list */
1345 			return (moddef);
1346 		} else if (cmp > 0) {	/* cmp > 0: Insert in middle/end */
1347 			*insdef = moddef;
1348 			moddef = moddef->ml_next;
1349 			cmp = -1;
1350 			while (moddef && (cmp < 0)) {
1351 				cmp = strcasecmp(moddef->ml_mod->mod_name,
1352 				    name);
1353 				if (cmp == 0)
1354 					return (moddef);
1355 				if (cmp < 0) {
1356 					*insdef = moddef;
1357 					moddef = (*insdef)->ml_next;
1358 				}
1359 			}
1360 		}
1361 	}
1362 
1363 	return (NULL);
1364 }
1365 
1366 
1367 /*
1368  * Determine if a file is a sharable object based on its file path.
1369  * If path ends in a .so, followed optionally by a period and 1 or more
1370  * digits, we say that it is and return a pointer to the first character
1371  * of the suffix. Otherwise NULL is returned.
1372  */
1373 static const char *
path_is_so(const char * path)1374 path_is_so(const char *path)
1375 {
1376 	int		dotso_len;
1377 	const char	*tail;
1378 	size_t		len;
1379 
1380 	len = strlen(path);
1381 	if (len == 0)
1382 		return (NULL);
1383 	tail = path + len;
1384 	if (isdigit(*(tail - 1))) {
1385 		while ((tail > path) && isdigit(*(tail - 1)))
1386 			tail--;
1387 		if ((tail <= path) || (*tail != '.'))
1388 			return (NULL);
1389 	}
1390 	dotso_len = strlen(MSG_ORIG(MSG_STR_DOTSO));
1391 	if ((tail - path) < dotso_len)
1392 		return (NULL);
1393 	tail -= dotso_len;
1394 	if (strncmp(tail, MSG_ORIG(MSG_STR_DOTSO), dotso_len) == 0)
1395 		return (tail);
1396 
1397 	return (NULL);
1398 }
1399 
1400 
1401 /*
1402  * Locate the start of the unsuffixed file name within path. Returns pointer
1403  * to first character of that name in path.
1404  *
1405  * entry:
1406  *	path - Path to be examined.
1407  *	tail - NULL, or pointer to position at tail of path from which
1408  *		the search for '/' characters should start. If NULL,
1409  *		strlen() is used to locate the end of the string.
1410  *	buf - NULL, or buffer to receive a copy of the characters that
1411  *		lie between the start of the filename and tail.
1412  *	bufsize - sizeof(buf)
1413  *
1414  * exit:
1415  *	The pointer to the first character of the unsuffixed file name
1416  *	within path is returned. If buf is non-NULL, the characters
1417  *	lying between that point and tail (or the end of path if tail
1418  *	is NULL) are copied into buf.
1419  */
1420 static const char *
elfedit_basename(const char * path,const char * tail,char * buf,size_t bufsiz)1421 elfedit_basename(const char *path, const char *tail, char *buf, size_t bufsiz)
1422 {
1423 	const char	*s;
1424 
1425 	if (tail == NULL)
1426 		tail = path + strlen(path);
1427 	s = tail;
1428 	while ((s > path) && (*(s - 1) != '/'))
1429 		s--;
1430 	if (buf != NULL)
1431 		elfedit_strnbcpy(buf, s, tail - s, bufsiz);
1432 	return (s);
1433 }
1434 
1435 
1436 /*
1437  * Issue an error on behalf of load_module(), taking care to release
1438  * resources that routine may have aquired:
1439  *
1440  * entry:
1441  *	moddef - NULL, or a module definition to be released via free()
1442  *	dl_hdl - NULL, or a handle to a sharable object to release via
1443  *		dlclose().
1444  *	dl_path - If dl_hdl is non-NULL, the path to the sharable object
1445  *		file that was loaded.
1446  *	format - A format string to pass to elfedit_msg(), containing
1447  *		no more than (3) %s format codes, and no other format codes.
1448  *	[s1-s4] - Strings to pass to elfedit_msg() to satisfy the four
1449  *		allowed %s codes in format. Should be set to NULL if the
1450  *		format string does not need them.
1451  *
1452  * note:
1453  *	This routine makes a copy of the s1-s4 strings before freeing any
1454  *	memory or unmapping the sharable library. It is therefore safe to
1455  *	use strings from moddef, or from the sharable library (which will
1456  *	be unmapped) to satisfy the other arguments s1-s4.
1457  */
1458 static void
load_module_err(MODLIST_T * moddef,void * dl_hdl,const char * dl_path,const char * format,const char * s1,const char * s2,const char * s3,const char * s4)1459 load_module_err(MODLIST_T *moddef, void *dl_hdl, const char *dl_path,
1460     const char *format, const char *s1, const char *s2, const char *s3,
1461     const char *s4)
1462 {
1463 #define	SCRBUFSIZE (PATH_MAX + 256)   /* A path, plus some extra */
1464 
1465 	char s1_buf[SCRBUFSIZE];
1466 	char s2_buf[SCRBUFSIZE];
1467 	char s3_buf[SCRBUFSIZE];
1468 	char s4_buf[SCRBUFSIZE];
1469 
1470 	/*
1471 	 * The caller may provide strings for s1-s3 that are from
1472 	 * moddef. If we free moddef, the printf() will die on access
1473 	 * to free memory. We could push back on the user and force
1474 	 * each call to carefully make copies of such data. However, this
1475 	 * is an easy case to miss. Furthermore, this is an error case,
1476 	 * and machine efficiency is not the main issue. We therefore make
1477 	 * copies of the s1-s3 strings here into auto variables, and then
1478 	 * use those copies. The user is freed from worrying about it.
1479 	 *
1480 	 * We use oversized stack based buffers instead of malloc() to
1481 	 * reduce the number of ways that things can go wrong while
1482 	 * reporting the error.
1483 	 */
1484 	if (s1 != NULL)
1485 		(void) strlcpy(s1_buf, s1, sizeof (s1_buf));
1486 	if (s2 != NULL)
1487 		(void) strlcpy(s2_buf, s2, sizeof (s2_buf));
1488 	if (s3 != NULL)
1489 		(void) strlcpy(s3_buf, s3, sizeof (s3_buf));
1490 	if (s4 != NULL)
1491 		(void) strlcpy(s4_buf, s4, sizeof (s4_buf));
1492 
1493 
1494 	if (moddef != NULL)
1495 		free(moddef);
1496 
1497 	if ((dl_hdl != NULL) && (dlclose(dl_hdl) != 0))
1498 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE),
1499 		    dl_path, dlerror());
1500 
1501 	elfedit_msg(ELFEDIT_MSG_ERR, format, s1_buf, s2_buf, s3_buf, s4_buf);
1502 #undef	SCRBUFSIZE
1503 }
1504 
1505 
1506 /*
1507  * Load a module sharable object for load_module().
1508  *
1509  * entry:
1510  *	path - Path of file to open
1511  *	moddef - If this function issues a non-returning error, it will
1512  *		first return the memory referenced by moddef. This argument
1513  *		is not used otherwise.
1514  *	must_exist - If True, we consider it to be an error if the file given
1515  *		by path does not exist. If False, no error is issued
1516  *		and a NULL value is quietly returned.
1517  *
1518  * exit:
1519  *	Returns a handle to the loaded object on success, or NULL if no
1520  *	file was loaded.
1521  */
1522 static void *
load_module_dlopen(const char * path,MODLIST_T * moddef,int must_exist)1523 load_module_dlopen(const char *path, MODLIST_T *moddef, int must_exist)
1524 {
1525 	int	fd;
1526 	void	*hdl;
1527 
1528 	/*
1529 	 * If the file is not required to exist, and it doesn't, then
1530 	 * we want to quietly return without an error.
1531 	 */
1532 	if (!must_exist) {
1533 		fd = open(path, O_RDONLY);
1534 		if (fd >= 0) {
1535 			(void) close(fd);
1536 		} else if (errno == ENOENT) {
1537 			return (NULL);
1538 		}
1539 	}
1540 
1541 	if ((hdl = dlopen(path, RTLD_LAZY|RTLD_FIRST)) == NULL)
1542 		load_module_err(moddef, NULL, NULL,
1543 		    MSG_INTL(MSG_ERR_CNTDLOPEN), path, dlerror(), NULL, NULL);
1544 
1545 	return (hdl);
1546 }
1547 
1548 
1549 /*
1550  * Sanity check option arguments to prevent common errors. The rest of
1551  * elfedit assumes these tests have been done, and does not check
1552  * again.
1553  */
1554 static void
validate_optarg(elfedit_cmd_optarg_t * optarg,int isopt,MODLIST_T * moddef,const char * mod_name,const char * cmd_name,void * dl_hdl,const char * dl_path)1555 validate_optarg(elfedit_cmd_optarg_t *optarg, int isopt, MODLIST_T *moddef,
1556     const char *mod_name, const char *cmd_name,
1557     void *dl_hdl, const char *dl_path)
1558 {
1559 #define	FAIL(_msg) errmsg = _msg; goto fail
1560 
1561 	Msg errmsg;
1562 	elfedit_cmd_oa_mask_t	optmask = 0;
1563 
1564 	for (; optarg->oa_name != NULL; optarg++) {
1565 		/*
1566 		 * If ELFEDIT_CMDOA_F_INHERIT is set:
1567 		 *	- oa_name must be a value in the range of
1568 		 *		known ELFEDIT_STDOA_ values.
1569 		 *	- oa_help must be NULL
1570 		 *	- ELFEDIT_CMDOA_F_INHERIT must be the only flag set
1571 		 */
1572 		if (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) {
1573 			if ((((uintptr_t)optarg->oa_name) >
1574 			    ELFEDIT_NUM_STDOA) ||
1575 			    (optarg->oa_help != 0) ||
1576 			    (optarg->oa_flags != ELFEDIT_CMDOA_F_INHERIT))
1577 				/*
1578 				 * Can't use FAIL --- oa_name is not a valid
1579 				 * string, and load_module_err() looks at args.
1580 				 */
1581 				load_module_err(moddef, dl_hdl, dl_path,
1582 				    MSG_INTL(MSG_ERR_BADSTDOA), dl_path,
1583 				    mod_name, cmd_name, NULL);
1584 			continue;
1585 		}
1586 
1587 		if (isopt) {
1588 			/*
1589 			 * Option name must start with a '-', and must
1590 			 * have at one following character.
1591 			 */
1592 			if (optarg->oa_name[0] != '-') {
1593 				/* MSG_INTL(MSG_ERR_OPT_MODPRE) */
1594 				FAIL(MSG_ERR_OPT_MODPRE);
1595 			}
1596 			if (optarg->oa_name[1] == '\0') {
1597 				/* MSG_INTL(MSG_ERR_OPT_MODLEN) */
1598 				FAIL(MSG_ERR_OPT_MODLEN);
1599 			}
1600 
1601 			/*
1602 			 * oa_idmask must be 0, or it must have a single
1603 			 * bit set (a power of 2).oa_excmask must be 0
1604 			 * if oa_idmask is 0
1605 			 */
1606 			if (optarg->oa_idmask == 0) {
1607 				if (optarg->oa_excmask != 0) {
1608 					/* MSG_INTL(MSG_ERR_OPT_EXCMASKN0) */
1609 					FAIL(MSG_ERR_OPT_EXCMASKN0);
1610 				}
1611 			} else {
1612 				if (elfedit_bits_set(optarg->oa_idmask,
1613 				    sizeof (optarg->oa_idmask)) != 1) {
1614 					/* MSG_INTL(MSG_ERR_OPT_IDMASKPOW2) */
1615 					FAIL(MSG_ERR_OPT_IDMASKPOW2);
1616 				}
1617 
1618 				/* Non-zero idmask must be unique */
1619 				if ((optarg->oa_idmask & optmask) != 0) {
1620 					/* MSG_INTL(MSG_ERR_OPT_IDMASKUNIQ) */
1621 					FAIL(MSG_ERR_OPT_IDMASKUNIQ);
1622 				}
1623 
1624 				/* Add this one to the overall mask */
1625 				optmask |= optarg->oa_idmask;
1626 			}
1627 		} else {
1628 			/*
1629 			 * Argument name cannot start with a'-', and must
1630 			 * not be a null string.
1631 			 */
1632 			if (optarg->oa_name[0] == '-') {
1633 				/* MSG_INTL(MSG_ERR_ARG_MODPRE) */
1634 				FAIL(MSG_ERR_ARG_MODPRE);
1635 			}
1636 			if (optarg->oa_name[1] == '\0') {
1637 				/* MSG_INTL(MSG_ERR_ARG_MODLEN) */
1638 				FAIL(MSG_ERR_ARG_MODLEN);
1639 			}
1640 
1641 
1642 			/* oa_idmask and oa_excmask must both be 0 */
1643 			if ((optarg->oa_idmask != 0) ||
1644 			    (optarg->oa_excmask != 0)) {
1645 				/* MSG_INTL(MSG_ERR_ARG_MASKNOT0) */
1646 				FAIL(MSG_ERR_ARG_MASKNOT0);
1647 			}
1648 
1649 		}
1650 
1651 		/*
1652 		 * If it takes a value, make sure that we are
1653 		 * processing options, because CMDOA_F_VALUE is not
1654 		 * allowed for plain arguments. Then check the following
1655 		 * item in the list:
1656 		 *	- There must be a following item.
1657 		 *	- oa_name must be non-NULL. This is the only field
1658 		 *		that is used by elfedit.
1659 		 *	- oa_help, oa_flags, oa_idmask, and oa_excmask
1660 		 *		must be 0.
1661 		 */
1662 		if (optarg->oa_flags & ELFEDIT_CMDOA_F_VALUE) {
1663 			elfedit_cmd_optarg_t *oa1 = optarg + 1;
1664 
1665 			if (!isopt) {
1666 				/* MSG_INTL(MSG_ERR_ARG_CMDOA_VAL) */
1667 				FAIL(MSG_ERR_ARG_CMDOA_VAL);
1668 			}
1669 
1670 			if ((optarg + 1)->oa_name == NULL) {
1671 				/* MSG_INTL(MSG_ERR_BADMODOPTVAL) */
1672 				FAIL(MSG_ERR_BADMODOPTVAL);
1673 			}
1674 
1675 			if (oa1->oa_name == NULL) {
1676 				/* MSG_INTL(MSG_ERR_CMDOA_VALNAM) */
1677 				FAIL(MSG_ERR_CMDOA_VALNAM);
1678 			}
1679 			if ((oa1->oa_help != 0) || (oa1->oa_flags != 0) ||
1680 			    (oa1->oa_idmask != 0) || (oa1->oa_excmask != 0)) {
1681 				/* MSG_INTL(MSG_ERR_CMDOA_VALNOT0) */
1682 				FAIL(MSG_ERR_CMDOA_VALNOT0);
1683 			}
1684 			optarg++;
1685 		}
1686 	}
1687 
1688 
1689 	return;
1690 
1691 fail:
1692 	load_module_err(moddef, dl_hdl, dl_path, MSG_INTL(errmsg),
1693 	    dl_path, mod_name, cmd_name, optarg->oa_name);
1694 }
1695 
1696 /*
1697  * Look up the specified module, loading the module if necessary,
1698  * and return its definition, or NULL on failure.
1699  *
1700  * entry:
1701  *	name - Name of module to load. If name contains a '/' character or has
1702  *		a ".so" suffix, then it is taken to be an absolute file path,
1703  *		and is used directly as is. If name does not contain a '/'
1704  *		character, then we look for it against the locations in
1705  *		the module path, addint the '.so' suffix, and taking the first
1706  *		one we find.
1707  *	must_exist - If True, we consider it to be an error if we are unable
1708  *		to locate a file to load and the module does not already exist.
1709  *		If False, NULL is returned quietly in this case.
1710  *	allow_abs - True if absolute paths are allowed. False to disallow
1711  *		them.
1712  *
1713  * note:
1714  *	If the path is absolute, then we load the file and take the module
1715  *	name from the data returned by its elfedit_init() function. If a
1716  *	module of that name is already loaded, it is unloaded and replaced
1717  *	with the new one.
1718  *
1719  *	If the path is non absolute, then we check to see if the module has
1720  *	already been loaded, and if so, we return that module definition.
1721  *	In this case, nothing new is loaded. If the module has not been loaded,
1722  *	we search the path for it and load it. If the module name provided
1723  *	by the elfedit_init() function does not match the name of the file,
1724  *	an error results.
1725  */
1726 elfeditGC_module_t *
elfedit_load_module(const char * name,int must_exist,int allow_abs)1727 elfedit_load_module(const char *name, int must_exist, int allow_abs)
1728 {
1729 	elfedit_init_func_t	*init_func;
1730 	elfeditGC_module_t	*mod;
1731 	MODLIST_T		*moddef, *insdef;
1732 	const char		*path;
1733 	char			path_buf[PATH_MAX + 1];
1734 	void			*hdl;
1735 	size_t			i;
1736 	int			is_abs_path;
1737 	elfeditGC_cmd_t		*cmd;
1738 
1739 	/*
1740 	 * If the name includes a .so suffix, or has any '/' characters,
1741 	 * then it is an absolute path that we use as is to load the named
1742 	 * file. Otherwise, we iterate over the path, adding the .so suffix
1743 	 * and load the first file that matches.
1744 	 */
1745 	is_abs_path = (path_is_so(name) != NULL) ||
1746 	    (name != elfedit_basename(name, NULL, NULL, 0));
1747 
1748 	if (is_abs_path && !allow_abs)
1749 		load_module_err(NULL, NULL, NULL,
1750 		    MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL);
1751 
1752 	/*
1753 	 * If this is a non-absolute path, search for the module already
1754 	 * having been loaded, and return it if so.
1755 	 */
1756 	if (!is_abs_path) {
1757 		moddef = module_loaded(name, &insdef);
1758 		if (moddef != NULL)
1759 			return (moddef->ml_mod);
1760 		/*
1761 		 * As a result of module_loaded(), insdef now contains the
1762 		 * immediate predecessor node for the new one, or NULL if
1763 		 * it goes at the front. In the absolute-path case, we take
1764 		 * care of this below, after the sharable object is loaded.
1765 		 */
1766 	}
1767 
1768 	/*
1769 	 * malloc() a module definition block before trying to dlopen().
1770 	 * Doing things in the other order can cause the dlopen()'d object
1771 	 * to leak: If elfedit_malloc() fails, it can cause a jump to the
1772 	 * outer command loop without returning to the caller. Hence,
1773 	 * there will be no opportunity to clean up. Allocaing the module
1774 	 * first allows us to free it if necessary.
1775 	 */
1776 	moddef = elfedit_malloc(MSG_INTL(MSG_ALLOC_MODDEF),
1777 	    sizeof (*moddef) + PATH_MAX + 1);
1778 	moddef->ml_path = ((char *)moddef) + sizeof (*moddef);
1779 
1780 	if (is_abs_path) {
1781 		path = name;
1782 		hdl = load_module_dlopen(name, moddef, must_exist);
1783 	} else {
1784 		hdl = NULL;
1785 		path = path_buf;
1786 		for (i = 0; i < state.modpath.n; i++) {
1787 			if (snprintf(path_buf, sizeof (path_buf),
1788 			    MSG_ORIG(MSG_FMT_BLDSOPATH), state.modpath.seg[i],
1789 			    name) > sizeof (path_buf))
1790 				load_module_err(moddef, NULL, NULL,
1791 				    MSG_INTL(MSG_ERR_PATHTOOLONG),
1792 				    state.modpath.seg[i], name, NULL, NULL);
1793 			hdl = load_module_dlopen(path, moddef, 0);
1794 		}
1795 		if (must_exist && (hdl == NULL))
1796 			load_module_err(moddef, NULL, NULL,
1797 			    MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL);
1798 	}
1799 
1800 	if (hdl == NULL) {
1801 		free(moddef);
1802 		return (NULL);
1803 	}
1804 
1805 	if (state.elf.elfclass == ELFCLASS32) {
1806 		init_func = (elfedit_init_func_t *)
1807 		    dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT32));
1808 	} else {
1809 		init_func = (elfedit_init_func_t *)
1810 		    dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT64));
1811 	}
1812 	if (init_func == NULL)
1813 		load_module_err(moddef, hdl, path,
1814 		    MSG_INTL(MSG_ERR_SONOTMOD), path, NULL, NULL, NULL);
1815 
1816 	/*
1817 	 * Note that the init function will be passing us an
1818 	 * elfedit[32|64]_module_t pointer, which we cast to the
1819 	 * generic module pointer type in order to be able to manage
1820 	 * either type with one set of code.
1821 	 */
1822 	if (!(mod = (elfeditGC_module_t *)(* init_func)(ELFEDIT_VER_CURRENT)))
1823 		load_module_err(moddef, hdl, path,
1824 		    MSG_INTL(MSG_ERR_BADMODLOAD), path, NULL, NULL, NULL);
1825 
1826 	/*
1827 	 * Enforce some rules, to help module developers:
1828 	 *	- The primary name of a command must not be
1829 	 *		the empty string ("").
1830 	 *	- Options must start with a '-' followed by at least
1831 	 *		one character.
1832 	 *	- Arguments and options must be well formed.
1833 	 */
1834 	for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++) {
1835 		if (**cmd->cmd_name == '\0')
1836 			load_module_err(moddef, hdl, path,
1837 			    MSG_INTL(MSG_ERR_NULLPRICMDNAM), mod->mod_name,
1838 			    NULL, NULL, NULL);
1839 
1840 		if (cmd->cmd_args != NULL)
1841 			validate_optarg(cmd->cmd_args, 0, moddef, mod->mod_name,
1842 			    cmd->cmd_name[0], hdl, path);
1843 		if (cmd->cmd_opt != NULL)
1844 			validate_optarg(cmd->cmd_opt, 1, moddef, mod->mod_name,
1845 			    cmd->cmd_name[0], hdl, path);
1846 	}
1847 
1848 	/*
1849 	 * Check the name the module provides. How we handle this depends
1850 	 * on whether the path is absolute or the result of a path search.
1851 	 */
1852 	if (is_abs_path) {
1853 		MODLIST_T *old_moddef = module_loaded(mod->mod_name, &insdef);
1854 
1855 		if (old_moddef != NULL) {	/* Replace existing */
1856 			free(moddef);		/* Rare case: Don't need it */
1857 			/*
1858 			 * Be sure we don't unload builtin modules!
1859 			 * These have a NULL dl_hdl field.
1860 			 */
1861 			if (old_moddef->ml_dl_hdl == NULL)
1862 				load_module_err(NULL, hdl, path,
1863 				    MSG_INTL(MSG_ERR_CNTULSMOD),
1864 				    old_moddef->ml_mod->mod_name, NULL,
1865 				    NULL, NULL);
1866 
1867 			/* Unload existing */
1868 			if (dlclose(old_moddef->ml_dl_hdl) != 0)
1869 				elfedit_msg(ELFEDIT_MSG_ERR,
1870 				    MSG_INTL(MSG_ERR_CNTDLCLOSE),
1871 				    old_moddef->ml_path, dlerror());
1872 			elfedit_msg(ELFEDIT_MSG_DEBUG,
1873 			    MSG_INTL(MSG_DEBUG_MODUNLOAD),
1874 			    old_moddef->ml_mod->mod_name, old_moddef->ml_path);
1875 			old_moddef->ml_mod = mod;
1876 			old_moddef->ml_dl_hdl = hdl;
1877 			(void) strlcpy((char *)old_moddef->ml_path, path,
1878 			    PATH_MAX + 1);
1879 			elfedit_msg(ELFEDIT_MSG_DEBUG,
1880 			    MSG_INTL(MSG_DEBUG_MODLOAD),
1881 			    old_moddef->ml_mod->mod_name, path);
1882 			return (old_moddef->ml_mod);
1883 		}
1884 		/*
1885 		 * insdef now contains the insertion point for the absolute
1886 		 * path case.
1887 		 */
1888 	} else {
1889 		/* If the names don't match, then error */
1890 		if (strcasecmp(name, mod->mod_name) != 0)
1891 			load_module_err(moddef, hdl, path,
1892 			    MSG_INTL(MSG_ERR_BADMODNAME),
1893 			    mod->mod_name, name, path, NULL);
1894 	}
1895 
1896 	/*
1897 	 * Link module into the module list. If insdef is NULL,
1898 	 * it goes at the head. If insdef is non-NULL, it goes immediately
1899 	 * after
1900 	 */
1901 	if (insdef == NULL) {
1902 		moddef->ml_next = state.modlist;
1903 		state.modlist = moddef;
1904 	} else {
1905 		moddef->ml_next = insdef->ml_next;
1906 		insdef->ml_next = moddef;
1907 	}
1908 	moddef->ml_mod = mod;
1909 	moddef->ml_dl_hdl = hdl;
1910 	(void) strlcpy((char *)moddef->ml_path, path, PATH_MAX + 1);
1911 
1912 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODLOAD),
1913 	    moddef->ml_mod->mod_name, path);
1914 
1915 	return (moddef->ml_mod);
1916 }
1917 
1918 
1919 /*
1920  * Unload the specified module
1921  */
1922 void
elfedit_unload_module(const char * name)1923 elfedit_unload_module(const char *name)
1924 {
1925 	MODLIST_T	*moddef, *insdef;
1926 
1927 	moddef = module_loaded(name, &insdef);
1928 	if (moddef == NULL)
1929 		return;
1930 
1931 	/* Built in modules cannot be unloaded. They have a NULL dl_hdl field */
1932 	if (moddef->ml_dl_hdl == NULL)
1933 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTULSMOD),
1934 		    moddef->ml_mod->mod_name);
1935 
1936 	/*
1937 	 * When we unload it, the name string goes with it. So
1938 	 * announce it while we still can without having to make a copy.
1939 	 */
1940 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODUNLOAD),
1941 	    moddef->ml_mod->mod_name, moddef->ml_path);
1942 
1943 	/*
1944 	 * Close it before going further. On failure, we'll jump, and the
1945 	 * record will remain in the module list. On success,
1946 	 * we'll retain control, and can safely remove it.
1947 	 */
1948 	if (dlclose(moddef->ml_dl_hdl) != 0)
1949 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE),
1950 		    moddef->ml_path, dlerror());
1951 
1952 	/* Unlink the record from the module list */
1953 	if (insdef == NULL)
1954 		state.modlist = moddef->ml_next;
1955 	else
1956 		insdef->ml_next = moddef->ml_next;
1957 
1958 	/* Release the memory */
1959 	free(moddef);
1960 }
1961 
1962 
1963 /*
1964  * Load all sharable objects found in the specified directory.
1965  *
1966  * entry:
1967  *	dirpath - Path of directory to process.
1968  *	must_exist - If True, it is an error if diropen() fails to open
1969  *		the given directory. Of False, we quietly ignore it and return.
1970  *	abs_path - If True, files are loaded using their literal paths.
1971  *		If False, their module name is extracted from the dirpath
1972  *		and a path based search is used to locate it.
1973  */
1974 void
elfedit_load_moddir(const char * dirpath,int must_exist,int abs_path)1975 elfedit_load_moddir(const char *dirpath, int must_exist, int abs_path)
1976 {
1977 	char		path[PATH_MAX + 1];
1978 	DIR		*dir;
1979 	struct dirent	*dp;
1980 	const char	*tail;
1981 
1982 	dir = opendir(dirpath);
1983 	if (dir == NULL) {
1984 		int err = errno;
1985 
1986 		if (!must_exist && (err == ENOENT))
1987 			return;
1988 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNDIR),
1989 		    dirpath, strerror(err));
1990 		/*NOTREACHED*/
1991 	}
1992 
1993 	while ((dp = readdir(dir)) != NULL) {
1994 		if ((tail = path_is_so(dp->d_name)) != NULL) {
1995 			if (abs_path) {
1996 				(void) snprintf(path, sizeof (path),
1997 				    MSG_ORIG(MSG_FMT_BLDPATH), dirpath,
1998 				    dp->d_name);
1999 			} else {
2000 				(void) elfedit_basename(dp->d_name, tail,
2001 				    path, sizeof (path));
2002 			}
2003 			(void) elfedit_load_module(path, must_exist, 1);
2004 		}
2005 	}
2006 	(void) closedir(dir);
2007 }
2008 
2009 
2010 /*
2011  * Follow the module load path, and load the first module found for each
2012  * given name.
2013  */
2014 void
elfedit_load_modpath(void)2015 elfedit_load_modpath(void)
2016 {
2017 	size_t		i;
2018 
2019 	for (i = 0; i < state.modpath.n; i++)
2020 		elfedit_load_moddir(state.modpath.seg[i], 0, 0);
2021 }
2022 
2023 /*
2024  * Given a module definition, look for the specified command.
2025  * Returns the command if found, and NULL otherwise.
2026  */
2027 static elfeditGC_cmd_t *
find_cmd(elfeditGC_module_t * mod,const char * name)2028 find_cmd(elfeditGC_module_t *mod, const char *name)
2029 {
2030 	elfeditGC_cmd_t *cmd;
2031 	const char **cmd_name;
2032 
2033 	for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++)
2034 		for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++)
2035 			if (strcasecmp(name, *cmd_name) == 0) {
2036 				if (cmd_name != cmd->cmd_name)
2037 					elfedit_msg(ELFEDIT_MSG_DEBUG,
2038 					    MSG_INTL(MSG_DEBUG_CMDALIAS),
2039 					    mod->mod_name, *cmd_name,
2040 					    mod->mod_name, *cmd->cmd_name);
2041 				return (cmd);
2042 			}
2043 
2044 	return (NULL);
2045 }
2046 
2047 
2048 /*
2049  * Given a command name, return its command definition.
2050  *
2051  * entry:
2052  *	name - Command to be looked up
2053  *	must_exist - If True, we consider it to be an error if the command
2054  *		does not exist. If False, NULL is returned quietly in
2055  *		this case.
2056  *	mod_ret - NULL, or address of a variable to receive the
2057  *		module definition block of the module containing
2058  *		the command.
2059  *
2060  * exit:
2061  *	On success, returns a pointer to the command definition, and
2062  *	if mod_ret is non-NULL, *mod_ret receives a pointer to the
2063  *	module definition. On failure, must_exist determines the
2064  *	action taken: If must_exist is True, an error is issued and
2065  *	control does not return to the caller. If must_exist is False,
2066  *	NULL is quietly returned.
2067  *
2068  * note:
2069  *	A ':' in name is used to delimit the module and command names.
2070  *	If it is omitted, or if it is the first non-whitespace character
2071  *	in the name, then the built in sys: module is implied.
2072  */
2073 elfeditGC_cmd_t *
elfedit_find_command(const char * name,int must_exist,elfeditGC_module_t ** mod_ret)2074 elfedit_find_command(const char *name, int must_exist,
2075     elfeditGC_module_t **mod_ret)
2076 {
2077 	elfeditGC_module_t	*mod;
2078 	const char		*mod_str;
2079 	const char		*cmd_str;
2080 	char			mod_buf[ELFEDIT_MAXMODNAM + 1];
2081 	size_t			n;
2082 	elfeditGC_cmd_t		*cmd;
2083 
2084 
2085 	cmd_str = strstr(name, MSG_ORIG(MSG_STR_COLON));
2086 	if (cmd_str == NULL) {		/* No module name -> sys: */
2087 		mod_str = MSG_ORIG(MSG_MOD_SYS);
2088 		cmd_str = name;
2089 	} else if (cmd_str == name) {	/* Empty module name -> sys: */
2090 		mod_str = MSG_ORIG(MSG_MOD_SYS);
2091 		cmd_str++;		/* Skip the colon */
2092 	} else {			/* Have both module and command */
2093 		n = cmd_str - name;
2094 		if (n >= sizeof (mod_buf)) {
2095 			if (must_exist)
2096 				elfedit_msg(ELFEDIT_MSG_ERR,
2097 				    MSG_INTL(MSG_ERR_MODNAMTOOLONG), name);
2098 			return (NULL);
2099 		}
2100 		(void) strlcpy(mod_buf, name, n + 1);
2101 		mod_str = mod_buf;
2102 		cmd_str++;
2103 	}
2104 
2105 	/* Lookup/load module. Won't return on error */
2106 	mod = elfedit_load_module(mod_str, must_exist, 0);
2107 	if (mod == NULL)
2108 		return (NULL);
2109 
2110 	/* Locate the command */
2111 	cmd = find_cmd(mod, cmd_str);
2112 	if (cmd == NULL) {
2113 		if (must_exist) {
2114 			/*
2115 			 * Catch empty command in order to provide
2116 			 * a better error message.
2117 			 */
2118 			if (*cmd_str == '\0') {
2119 				elfedit_msg(ELFEDIT_MSG_ERR,
2120 				    MSG_INTL(MSG_ERR_MODNOCMD), mod_str);
2121 			} else {
2122 				elfedit_msg(ELFEDIT_MSG_ERR,
2123 				    MSG_INTL(MSG_ERR_UNRECCMD),
2124 				    mod_str, cmd_str);
2125 			}
2126 		}
2127 	} else {
2128 		if (mod_ret != NULL)
2129 			*mod_ret = mod;
2130 	}
2131 	return (cmd);
2132 }
2133 
2134 
2135 /*
2136  * Release all user command blocks found on state.ucmd
2137  */
2138 static void
free_user_cmds(void)2139 free_user_cmds(void)
2140 {
2141 	USER_CMD_T *next;
2142 
2143 	while (state.ucmd.list) {
2144 		next = state.ucmd.list->ucmd_next;
2145 		free(state.ucmd.list);
2146 		state.ucmd.list = next;
2147 	}
2148 	state.ucmd.tail = NULL;
2149 	state.ucmd.n = 0;
2150 	state.cur_cmd = NULL;
2151 }
2152 
2153 
2154 /*
2155  * Process all user command blocks found on state.ucmd, and then
2156  * remove them from the list.
2157  */
2158 static void
dispatch_user_cmds()2159 dispatch_user_cmds()
2160 {
2161 	USER_CMD_T		*ucmd;
2162 	elfedit_cmdret_t	cmd_ret;
2163 
2164 	ucmd = state.ucmd.list;
2165 	if (ucmd) {
2166 		/* Do them, in order */
2167 		for (; ucmd; ucmd = ucmd->ucmd_next) {
2168 			state.cur_cmd = ucmd;
2169 			if (!state.msg_jbuf.active)
2170 				elfedit_msg(ELFEDIT_MSG_DEBUG,
2171 				    MSG_INTL(MSG_DEBUG_EXECCMD),
2172 				    ucmd->ucmd_orig_str);
2173 			/*
2174 			 * The cmd_func field is the generic definition.
2175 			 * We need to cast it to the type that matches
2176 			 * the proper ELFCLASS before calling it.
2177 			 */
2178 			if (state.elf.elfclass == ELFCLASS32) {
2179 				elfedit32_cmd_func_t *cmd_func =
2180 				    (elfedit32_cmd_func_t *)
2181 				    ucmd->ucmd_cmd->cmd_func;
2182 
2183 				cmd_ret = (* cmd_func)(state.elf.obj_state.s32,
2184 				    ucmd->ucmd_argc, ucmd->ucmd_argv);
2185 			} else {
2186 				elfedit64_cmd_func_t *cmd_func =
2187 				    (elfedit64_cmd_func_t *)
2188 				    ucmd->ucmd_cmd->cmd_func;
2189 
2190 				cmd_ret = (* cmd_func)(state.elf.obj_state.s64,
2191 				    ucmd->ucmd_argc, ucmd->ucmd_argv);
2192 			}
2193 			state.cur_cmd = NULL;
2194 			/* If a pager was started, wrap it up */
2195 			elfedit_pager_cleanup();
2196 
2197 			switch (cmd_ret) {
2198 			case ELFEDIT_CMDRET_MOD_OS_MACH:
2199 				/*
2200 				 * Inform the elfconst module that the machine
2201 				 * or osabi has has changed. It may be necessary
2202 				 * to fetch new strings from libconv.
2203 				 */
2204 				state.elf.elfconst_ehdr_change = 1;
2205 				/*FALLTHROUGH*/
2206 			case ELFEDIT_CMDRET_MOD:
2207 				/*
2208 				 * Command modified the output ELF image,
2209 				 * mark the file as needing a flush to disk.
2210 				 */
2211 				state.file.dirty = 1;
2212 				break;
2213 			case ELFEDIT_CMDRET_FLUSH:
2214 				/*
2215 				 * Command flushed the output file,
2216 				 * clear the dirty bit.
2217 				 */
2218 				state.file.dirty = 0;
2219 				break;
2220 			case ELFEDIT_CMDRET_NONE:
2221 				break;
2222 			}
2223 		}
2224 		free_user_cmds();
2225 	}
2226 }
2227 
2228 
2229 /*
2230  * Prepare a GETTOK_STATE struct for gettok().
2231  *
2232  * entry:
2233  *	gettok_state - gettok state block to use
2234  *	str - Writable buffer to tokenize. Note that gettok()
2235  *		is allowed to change the contents of this buffer.
2236  *	inc_null_final - If the line ends in whitespace instead of
2237  *		immediately hitting a NULL, and inc_null_final is TRUE,
2238  *		then a null final token is generated. Otherwise trailing
2239  *		whitespace is ignored.
2240  */
2241 static void
gettok_init(GETTOK_STATE * gettok_state,char * buf,int inc_null_final)2242 gettok_init(GETTOK_STATE *gettok_state, char *buf, int inc_null_final)
2243 {
2244 	gettok_state->gtok_buf = gettok_state->gtok_cur_buf = buf;
2245 	gettok_state->gtok_inc_null_final = inc_null_final;
2246 	gettok_state->gtok_null_seen = 0;
2247 }
2248 
2249 
2250 /*
2251  * Locate the next token from the buffer.
2252  *
2253  * entry:
2254  *	gettok_state - State of gettok() operation. Initialized
2255  *		by gettok_init(), and passed to gettok().
2256  *
2257  * exit:
2258  *	If a token is found, gettok_state->gtok_last_token is filled in
2259  *	with the details and True (1) is returned. If no token is found,
2260  *	False (1) is returned, and the contents of
2261  *	gettok_state->gtok_last_token are undefined.
2262  *
2263  * note:
2264  *	- The token returned references the memory in gettok_state->gtok_buf.
2265  *		The caller should not modify the buffer until all such
2266  *		pointers have been discarded.
2267  *	- This routine will modify the contents of gettok_state->gtok_buf
2268  *		as necessary to remove quotes and eliminate escape
2269  *		(\)characters.
2270  */
2271 static int
gettok(GETTOK_STATE * gettok_state)2272 gettok(GETTOK_STATE *gettok_state)
2273 {
2274 	char	*str = gettok_state->gtok_cur_buf;
2275 	char	*look;
2276 	int	quote_ch = '\0';
2277 
2278 	/* Skip leading whitespace */
2279 	while (isspace(*str))
2280 		str++;
2281 
2282 	if (*str == '\0') {
2283 		/*
2284 		 * If user requested it, and there was whitespace at the
2285 		 * end, then generate one last null token.
2286 		 */
2287 		if (gettok_state->gtok_inc_null_final &&
2288 		    !gettok_state->gtok_null_seen) {
2289 			gettok_state->gtok_inc_null_final = 0;
2290 			gettok_state->gtok_null_seen = 1;
2291 			gettok_state->gtok_last_token.tok_str = str;
2292 			gettok_state->gtok_last_token.tok_len = 0;
2293 			gettok_state->gtok_last_token.tok_line_off =
2294 			    str - gettok_state->gtok_buf;
2295 			return (1);
2296 		}
2297 		gettok_state->gtok_null_seen = 1;
2298 		return (0);
2299 	}
2300 
2301 	/*
2302 	 * Read token: The standard delimiter is whitespace, but
2303 	 * we honor either single or double quotes. Also, we honor
2304 	 * backslash escapes.
2305 	 */
2306 	gettok_state->gtok_last_token.tok_str = look = str;
2307 	gettok_state->gtok_last_token.tok_line_off =
2308 	    look - gettok_state->gtok_buf;
2309 	for (; *look; look++) {
2310 		if (*look == quote_ch) {	/* Terminates active quote */
2311 			quote_ch = '\0';
2312 			continue;
2313 		}
2314 
2315 		if (quote_ch == '\0') {		/* No quote currently active */
2316 			if ((*look == '\'') || (*look == '"')) {
2317 				quote_ch = *look;	/* New active quote */
2318 				continue;
2319 			}
2320 			if (isspace(*look))
2321 				break;
2322 		}
2323 
2324 		/*
2325 		 * The semantics of the backslash character depends on
2326 		 * the quote style in use:
2327 		 *	- Within single quotes, backslash is not
2328 		 *		an escape character, and is taken literally.
2329 		 *	- If outside of quotes, the backslash is an escape
2330 		 *		character. The backslash is ignored and the
2331 		 *		following character is taken literally, losing
2332 		 *		any special properties it normally has.
2333 		 *	- Within double quotes, backslash works like a
2334 		 *		backslash escape within a C literal. Certain
2335 		 *		escapes are recognized and replaced with their
2336 		 *		special character. Any others are an error.
2337 		 */
2338 		if (*look == '\\') {
2339 			if (quote_ch == '\'') {
2340 				*str++ = *look;
2341 				continue;
2342 			}
2343 
2344 			look++;
2345 			if (*look == '\0') {	/* Esc applied to NULL term? */
2346 				elfedit_msg(ELFEDIT_MSG_ERR,
2347 				    MSG_INTL(MSG_ERR_ESCEOL));
2348 				/*NOTREACHED*/
2349 			}
2350 
2351 			if (quote_ch == '"') {
2352 				int ch = conv_translate_c_esc(&look);
2353 
2354 				if (ch == -1)
2355 					elfedit_msg(ELFEDIT_MSG_ERR,
2356 					    MSG_INTL(MSG_ERR_BADCESC), *look);
2357 				*str++ = ch;
2358 				look--;		/* for() will advance by 1 */
2359 				continue;
2360 			}
2361 		}
2362 
2363 		if (look != str)
2364 			*str = *look;
2365 		str++;
2366 	}
2367 
2368 	/* Don't allow unterminated quoted tokens */
2369 	if (quote_ch != '\0')
2370 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_UNTERMQUOTE),
2371 		    quote_ch);
2372 
2373 	gettok_state->gtok_last_token.tok_len = str -
2374 	    gettok_state->gtok_last_token.tok_str;
2375 	gettok_state->gtok_null_seen = *look == '\0';
2376 	if (!gettok_state->gtok_null_seen)
2377 		look++;
2378 	*str = '\0';
2379 	gettok_state->gtok_cur_buf = look;
2380 
2381 #ifdef DEBUG_GETTOK
2382 	printf("GETTOK >");
2383 	elfedit_str_to_c_literal(gettok_state->gtok_last_token.tok_str,
2384 	    elfedit_write);
2385 	printf("< \tlen(%d) offset(%d)\n",
2386 	    gettok_state->gtok_last_token.tok_len,
2387 	    gettok_state->gtok_last_token.tok_line_off);
2388 #endif
2389 
2390 	return (1);
2391 }
2392 
2393 
2394 /*
2395  * Tokenize the user command string, and return a pointer to the
2396  * TOK_STATE buffer maintained by this function. That buffer contains
2397  * the tokenized strings.
2398  *
2399  * entry:
2400  *	user_cmd_str - String to tokenize
2401  *	len - # of characters in user_cmd_str to examine. If
2402  *		(len < 0), then the complete string is processed
2403  *		stopping with the NULL termination. Otherwise,
2404  *		processing stops after len characters, and any
2405  *		remaining characters are ignored.
2406  *	inc_null_final - If True, and if user_cmd_str has whitespace
2407  *		at the end following the last non-null token, then
2408  *		a final null token will be included. If False, null
2409  *		tokens are ignored.
2410  *
2411  * note:
2412  *	This routine returns pointers to internally allocated memory.
2413  *	The caller must not alter anything contained in the TOK_STATE
2414  *	buffer returned. Furthermore, the the contents of TOK_STATE
2415  *	are only valid until the next call to tokenize_user_cmd().
2416  */
2417 static TOK_STATE *
tokenize_user_cmd(const char * user_cmd_str,size_t len,int inc_null_final)2418 tokenize_user_cmd(const char *user_cmd_str, size_t len, int inc_null_final)
2419 {
2420 #define	INITIAL_TOK_ALLOC 5
2421 
2422 	/*
2423 	 * As we parse the user command, we need temporary space to
2424 	 * hold the tokens. We do this by dynamically allocating a string
2425 	 * buffer and a token array, and doubling them as necessary. This
2426 	 * is a single threaded application, so static variables suffice.
2427 	 */
2428 	static STRBUF str;
2429 	static TOK_STATE tokst;
2430 
2431 	GETTOK_STATE	gettok_state;
2432 	size_t		n;
2433 
2434 	/*
2435 	 * Make a copy we can modify. If (len == 0), take the entire
2436 	 * string. Otherwise limit it to the specified length.
2437 	 */
2438 	tokst.tokst_cmd_len = strlen(user_cmd_str);
2439 	if ((len > 0) && (len < tokst.tokst_cmd_len))
2440 		tokst.tokst_cmd_len = len;
2441 	tokst.tokst_cmd_len++;	/* Room for NULL termination */
2442 	strbuf_ensure_size(&str, tokst.tokst_cmd_len);
2443 	(void) strlcpy(str.buf, user_cmd_str, tokst.tokst_cmd_len);
2444 
2445 	/* Trim off any newline character that might be present */
2446 	if ((tokst.tokst_cmd_len > 1) &&
2447 	    (str.buf[tokst.tokst_cmd_len - 2] == '\n')) {
2448 		tokst.tokst_cmd_len--;
2449 		str.buf[tokst.tokst_cmd_len - 1] = '\0';
2450 	}
2451 
2452 	/* Tokenize the user command string into tok struct */
2453 	gettok_init(&gettok_state, str.buf, inc_null_final);
2454 	tokst.tokst_str_size = 0;	/* Space needed for token strings */
2455 	for (tokst.tokst_cnt = 0; gettok(&gettok_state) != 0;
2456 	    tokst.tokst_cnt++) {
2457 		/* If we need more room, expand the token buffer */
2458 		if (tokst.tokst_cnt >= tokst.tokst_bufsize) {
2459 			n = (tokst.tokst_bufsize == 0) ?
2460 			    INITIAL_TOK_ALLOC : (tokst.tokst_bufsize * 2);
2461 			tokst.tokst_buf = elfedit_realloc(
2462 			    MSG_INTL(MSG_ALLOC_TOKBUF), tokst.tokst_buf,
2463 			    n * sizeof (*tokst.tokst_buf));
2464 			tokst.tokst_bufsize = n;
2465 		}
2466 		tokst.tokst_str_size +=
2467 		    gettok_state.gtok_last_token.tok_len + 1;
2468 		tokst.tokst_buf[tokst.tokst_cnt] = gettok_state.gtok_last_token;
2469 	}
2470 	/* fold the command token to lowercase */
2471 	if (tokst.tokst_cnt > 0) {
2472 		char *s;
2473 
2474 		for (s = tokst.tokst_buf[0].tok_str; *s; s++)
2475 			if (isupper(*s))
2476 				*s = tolower(*s);
2477 	}
2478 
2479 	return (&tokst);
2480 
2481 #undef	INITIAL_TOK_ALLOC
2482 }
2483 
2484 
2485 /*
2486  * Parse the user command string, and put an entry for it at the end
2487  * of state.ucmd.
2488  */
2489 static void
parse_user_cmd(const char * user_cmd_str)2490 parse_user_cmd(const char *user_cmd_str)
2491 {
2492 	TOK_STATE	*tokst;
2493 	char		*s;
2494 	size_t		n;
2495 	size_t		len;
2496 	USER_CMD_T	*ucmd;
2497 	elfeditGC_module_t *mod;
2498 	elfeditGC_cmd_t	*cmd;
2499 
2500 	/*
2501 	 * Break it into tokens. If there are none, then it is
2502 	 * an empty command and is ignored.
2503 	 */
2504 	tokst = tokenize_user_cmd(user_cmd_str, -1, 0);
2505 	if (tokst->tokst_cnt == 0)
2506 		return;
2507 
2508 	/* Find the command. Won't return on error */
2509 	cmd = elfedit_find_command(tokst->tokst_buf[0].tok_str, 1, &mod);
2510 
2511 	/*
2512 	 * If there is no ELF file being edited, then only commands
2513 	 * from the sys: module are allowed.
2514 	 */
2515 	if ((state.file.present == 0) &&
2516 	    (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) != 0))
2517 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOFILSYSONLY),
2518 		    mod->mod_name, cmd->cmd_name[0]);
2519 
2520 
2521 	/* Allocate, fill in, and insert a USER_CMD_T block */
2522 	n = S_DROUND(sizeof (USER_CMD_T));
2523 	ucmd = elfedit_malloc(MSG_INTL(MSG_ALLOC_UCMD),
2524 	    n + (sizeof (char *) * (tokst->tokst_cnt - 1)) +
2525 	    tokst->tokst_cmd_len + tokst->tokst_str_size);
2526 	ucmd->ucmd_next = NULL;
2527 	ucmd->ucmd_argc = tokst->tokst_cnt - 1;
2528 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
2529 	ucmd->ucmd_argv = (const char **)(n + (char *)ucmd);
2530 	ucmd->ucmd_orig_str = (char *)(ucmd->ucmd_argv + ucmd->ucmd_argc);
2531 	(void) strncpy(ucmd->ucmd_orig_str, user_cmd_str, tokst->tokst_cmd_len);
2532 	ucmd->ucmd_mod = mod;
2533 	ucmd->ucmd_cmd = cmd;
2534 	ucmd->ucmd_ostyle_set = 0;
2535 	s = ucmd->ucmd_orig_str + tokst->tokst_cmd_len;
2536 	for (n = 1; n < tokst->tokst_cnt; n++) {
2537 		len = tokst->tokst_buf[n].tok_len + 1;
2538 		ucmd->ucmd_argv[n - 1] = s;
2539 		(void) strncpy(s, tokst->tokst_buf[n].tok_str, len);
2540 		s += len;
2541 	}
2542 	if (state.ucmd.list == NULL) {
2543 		state.ucmd.list = state.ucmd.tail = ucmd;
2544 	} else {
2545 		state.ucmd.tail->ucmd_next = ucmd;
2546 		state.ucmd.tail = ucmd;
2547 	}
2548 	state.ucmd.n++;
2549 }
2550 
2551 
2552 /*
2553  * Copy infile to a new file with the name given by outfile.
2554  */
2555 static void
create_outfile(const char * infile,const char * outfile)2556 create_outfile(const char *infile, const char *outfile)
2557 {
2558 	pid_t pid;
2559 	int statloc;
2560 	struct stat statbuf;
2561 
2562 
2563 	pid = fork();
2564 	switch (pid) {
2565 	case -1:			/* Unable to create process */
2566 		{
2567 			int err = errno;
2568 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTFORK),
2569 			    strerror(err));
2570 		}
2571 		/*NOTREACHED*/
2572 		return;
2573 
2574 	case 0:
2575 		(void) execl(MSG_ORIG(MSG_STR_BINCP),
2576 		    MSG_ORIG(MSG_STR_BINCP), infile, outfile, NULL);
2577 		/*
2578 		 * exec() only returns on error. This is the child process,
2579 		 * so we want to stay away from the usual error mechanism
2580 		 * and handle things directly.
2581 		 */
2582 		{
2583 			int err = errno;
2584 			(void) fprintf(stderr, MSG_INTL(MSG_ERR_CNTEXEC),
2585 			    MSG_ORIG(MSG_STR_ELFEDIT),
2586 			    MSG_ORIG(MSG_STR_BINCP), strerror(err));
2587 		}
2588 		exit(1);
2589 		/*NOTREACHED*/
2590 	}
2591 
2592 	/* This is the parent: Wait for the child to terminate */
2593 	if (waitpid(pid, &statloc,  0) != pid) {
2594 		int err = errno;
2595 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTWAIT),
2596 		    strerror(err));
2597 	}
2598 	/*
2599 	 * If the child failed, then terminate the process. There is no
2600 	 * need for an error message, because the child will have taken
2601 	 * care of that.
2602 	 */
2603 	if (!WIFEXITED(statloc) || (WEXITSTATUS(statloc) != 0))
2604 		exit(1);
2605 
2606 	/* Make sure the copy allows user write access */
2607 	if (stat(outfile, &statbuf) == -1) {
2608 		int err = errno;
2609 		(void) unlink(outfile);
2610 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTSTAT),
2611 		    outfile, strerror(err));
2612 	}
2613 	if ((statbuf.st_mode & S_IWUSR) == 0) {
2614 		/* Only keep permission bits, and add user write */
2615 		statbuf.st_mode |= S_IWUSR;
2616 		statbuf.st_mode &= 07777;   /* Only keep the permission bits */
2617 		if (chmod(outfile, statbuf.st_mode) == -1) {
2618 			int err = errno;
2619 			(void) unlink(outfile);
2620 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTCHMOD),
2621 			    outfile, strerror(err));
2622 		}
2623 	}
2624 }
2625 
2626 /*
2627  * Given a module path string, determine how long the resulting path will
2628  * be when all % tokens have been expanded.
2629  *
2630  * entry:
2631  *	path - Path for which expanded length is desired
2632  *	origin_root - Root of $ORIGIN  tree containing running elfedit program
2633  *
2634  * exit:
2635  *	Returns the value strlen() will give for the expanded path.
2636  */
2637 static size_t
modpath_strlen(const char * path,const char * origin_root)2638 modpath_strlen(const char *path, const char *origin_root)
2639 {
2640 	size_t len = 0;
2641 	const char *s;
2642 
2643 	s = path;
2644 	len = 0;
2645 	for (s = path; *s != '\0'; s++) {
2646 		if (*s == '%') {
2647 			s++;
2648 			switch (*s) {
2649 			case 'i':	/* ISA of running elfedit */
2650 				len += strlen(isa_i_str);
2651 				break;
2652 			case 'I':	/* "" for 32-bit, same as %i for 64 */
2653 				len += strlen(isa_I_str);
2654 				break;
2655 			case 'o':	/* Insert default path */
2656 				len +=
2657 				    modpath_strlen(MSG_ORIG(MSG_STR_MODPATH),
2658 				    origin_root);
2659 				break;
2660 			case 'r':	/* root of tree with running elfedit */
2661 				len += strlen(origin_root);
2662 				break;
2663 
2664 			case '%':	/* %% is reduced to just '%' */
2665 				len++;
2666 				break;
2667 			default:	/* All other % codes are reserved */
2668 				elfedit_msg(ELFEDIT_MSG_ERR,
2669 				    MSG_INTL(MSG_ERR_BADPATHCODE), *s);
2670 				/*NOTREACHED*/
2671 				break;
2672 			}
2673 		} else {	/* Non-% character passes straight through */
2674 			len++;
2675 		}
2676 	}
2677 
2678 	return (len);
2679 }
2680 
2681 
2682 /*
2683  * Given a module path string, and a buffer large enough to hold the results,
2684  * fill the buffer with the expanded path.
2685  *
2686  * entry:
2687  *	path - Path for which expanded length is desired
2688  *	origin_root - Root of tree containing running elfedit program
2689  *	buf - Buffer to receive the result. buf must as large or larger
2690  *		than the value given by modpath_strlen().
2691  *
2692  * exit:
2693  *	Returns pointer to location following the last character
2694  *	written to buf. A NULL byte is written to that address.
2695  */
2696 static char *
modpath_expand(const char * path,const char * origin_root,char * buf)2697 modpath_expand(const char *path, const char *origin_root, char *buf)
2698 {
2699 	size_t len;
2700 	const char *cp_str;
2701 
2702 	for (; *path != '\0'; path++) {
2703 		if (*path == '%') {
2704 			path++;
2705 			cp_str = NULL;
2706 			switch (*path) {
2707 			case 'i':	/* ISA of running elfedit */
2708 				cp_str = isa_i_str;
2709 				break;
2710 			case 'I':	/* "" for 32-bit, same as %i for 64 */
2711 				cp_str = isa_I_str;
2712 				break;
2713 			case 'o':	/* Insert default path */
2714 				buf = modpath_expand(MSG_ORIG(MSG_STR_MODPATH),
2715 				    origin_root, buf);
2716 				break;
2717 			case 'r':
2718 				cp_str = origin_root;
2719 				break;
2720 			case '%':	/* %% is reduced to just '%' */
2721 				*buf++ = *path;
2722 				break;
2723 			default:	/* All other % codes are reserved */
2724 				elfedit_msg(ELFEDIT_MSG_ERR,
2725 				    MSG_INTL(MSG_ERR_BADPATHCODE), *path);
2726 				/*NOTREACHED*/
2727 				break;
2728 			}
2729 			if ((cp_str != NULL) && ((len = strlen(cp_str)) > 0)) {
2730 				bcopy(cp_str, buf, len);
2731 				buf += len;
2732 			}
2733 		} else {	/* Non-% character passes straight through */
2734 			*buf++ = *path;
2735 		}
2736 	}
2737 
2738 	*buf = '\0';
2739 	return (buf);
2740 }
2741 
2742 
2743 /*
2744  * Establish the module search path: state.modpath
2745  *
2746  * The path used comes from the following sources, taking the first
2747  * one that has a value, and ignoring any others:
2748  *
2749  *	- ELFEDIT_PATH environment variable
2750  *	- -L command line argument
2751  *	- Default value
2752  *
2753  * entry:
2754  *	path - NULL, or the value of the -L command line argument
2755  *
2756  * exit:
2757  *	state.modpath has been filled in
2758  */
2759 static void
establish_modpath(const char * cmdline_path)2760 establish_modpath(const char *cmdline_path)
2761 {
2762 	char origin_root[PATH_MAX + 1];	/* Where elfedit binary is */
2763 	const char	*path;		/* Initial path */
2764 	char		*expath;	/* Expanded path */
2765 	size_t		len;
2766 	char		*src, *dst;
2767 
2768 	path = getenv(MSG_ORIG(MSG_STR_ENVVAR));
2769 	if (path == NULL)
2770 		path = cmdline_path;
2771 	if (path == NULL)
2772 		path = MSG_ORIG(MSG_STR_MODPATH);
2773 
2774 
2775 	/*
2776 	 * Root of tree containing running for running program. 32-bit elfedit
2777 	 * is installed in /usr/bin, and 64-bit elfedit is one level lower
2778 	 * in an ISA-specific subdirectory. So, we find the root by
2779 	 * getting the $ORGIN of the current running program, and trimming
2780 	 * off the last 2 (32-bit) or 3 (64-bit) directories.
2781 	 *
2782 	 * On a standard system, this will simply yield '/'. However,
2783 	 * doing it this way allows us to run elfedit from a proto area,
2784 	 * and pick up modules from the same proto area instead of those
2785 	 * installed on the system.
2786 	 */
2787 	if (dlinfo(RTLD_SELF, RTLD_DI_ORIGIN, &origin_root) == -1)
2788 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTGETORIGIN));
2789 	len = (sizeof (char *) == 8) ? 3 : 2;
2790 	src = origin_root + strlen(origin_root);
2791 	while ((src > origin_root) && (len > 0)) {
2792 		if (*(src - 1) == '/')
2793 			len--;
2794 		src--;
2795 	}
2796 	*src = '\0';
2797 
2798 
2799 	/*
2800 	 * Calculate space needed to hold expanded path. Note that
2801 	 * this assumes that MSG_STR_MODPATH will never contain a '%o'
2802 	 * code, and so, the expansion is not recursive. The codes allowed
2803 	 * are:
2804 	 *	%i - ISA of running elfedit (sparc, sparcv9, etc)
2805 	 *	%I - 64-bit ISA: Same as %i for 64-bit versions of elfedit,
2806 	 *		but yields empty string for 32-bit ISAs.
2807 	 *	%o - The original (default) path.
2808 	 *	%r - Root of tree holding elfedit program.
2809 	 *	%% - A single %
2810 	 *
2811 	 * A % followed by anything else is an error. This allows us to
2812 	 * add new codes in the future without backward compatability issues.
2813 	 */
2814 	len = modpath_strlen(path, origin_root);
2815 
2816 	expath = elfedit_malloc(MSG_INTL(MSG_ALLOC_EXPATH), len + 1);
2817 	(void) modpath_expand(path, origin_root, expath);
2818 
2819 	/*
2820 	 * Count path segments, eliminate extra '/', and replace ':'
2821 	 * with NULL.
2822 	 */
2823 	state.modpath.n = 1;
2824 	for (src = dst = expath; *src; src++) {
2825 		if (*src == '/') {
2826 			switch (*(src + 1)) {
2827 			case '/':
2828 			case ':':
2829 			case '\0':
2830 				continue;
2831 			}
2832 		}
2833 		if (*src == ':') {
2834 			state.modpath.n++;
2835 			*dst = '\0';
2836 		} else if (src != dst) {
2837 			*dst = *src;
2838 		}
2839 		dst++;
2840 	}
2841 	if (src != dst)
2842 		*dst = '\0';
2843 
2844 	state.modpath.seg = elfedit_malloc(MSG_INTL(MSG_ALLOC_PATHARR),
2845 	    sizeof (state.modpath.seg[0]) * state.modpath.n);
2846 
2847 	src = expath;
2848 	for (len = 0; len < state.modpath.n; len++) {
2849 		if (*src == '\0') {
2850 			state.modpath.seg[len] = MSG_ORIG(MSG_STR_DOT);
2851 			src++;
2852 		} else {
2853 			state.modpath.seg[len] = src;
2854 			src += strlen(src) + 1;
2855 		}
2856 	}
2857 }
2858 
2859 /*
2860  * When interactive (reading commands from a tty), we catch
2861  * SIGINT in order to restart the outer command loop.
2862  */
2863 /*ARGSUSED*/
2864 static void
sigint_handler(int sig,siginfo_t * sip,void * ucp)2865 sigint_handler(int sig, siginfo_t *sip, void *ucp)
2866 {
2867 	/* Jump to the outer loop to resume */
2868 	if (state.msg_jbuf.active) {
2869 		state.msg_jbuf.active = 0;
2870 		siglongjmp(state.msg_jbuf.env, 1);
2871 	}
2872 }
2873 
2874 
2875 static void
usage(int full)2876 usage(int full)
2877 {
2878 	elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_BRIEF));
2879 	if (full) {
2880 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL1));
2881 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL2));
2882 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL3));
2883 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL4));
2884 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL5));
2885 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL6));
2886 		elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL_LAST));
2887 	}
2888 	elfedit_exit(2);
2889 }
2890 
2891 
2892 /*
2893  * In order to complete commands, we need to know about them,
2894  * which means that we need to force all the modules to be
2895  * loaded. This is a relatively expensive operation, so we use
2896  * this function, which avoids doing it more than once in a session.
2897  */
2898 static void
elfedit_cpl_load_modules(void)2899 elfedit_cpl_load_modules(void)
2900 {
2901 	static int loaded;
2902 
2903 	if (!loaded) {
2904 		elfedit_load_modpath();
2905 		loaded = 1;	/* Don't do it again */
2906 	}
2907 }
2908 
2909 /*
2910  * Compare the token to the given string, and if they share a common
2911  * initial sequence, add the tail of string to the tecla command completion
2912  * buffer:
2913  *
2914  * entry:
2915  *	cpldata - Current completion state
2916  *	str - String to match against token
2917  *	casefold - True to allow case insensitive completion, False
2918  *		if case must match exactly.
2919  */
2920 void
elfedit_cpl_match(void * cpldata,const char * str,int casefold)2921 elfedit_cpl_match(void *cpldata, const char *str, int casefold)
2922 {
2923 	ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata;
2924 	const char	*cont_suffix;
2925 	const char	*type_suffix;
2926 
2927 	/*
2928 	 * Reasons to return immediately:
2929 	 *	- NULL strings have no completion value
2930 	 *	- The string is shorter than the existing item being completed
2931 	 */
2932 	if ((str == NULL) || (*str == '\0') ||
2933 	    ((cstate->ecpl_token_len != 0) &&
2934 	    ((strlen(str) < cstate->ecpl_token_len))))
2935 		return;
2936 
2937 	/* If the string does not share the existing prefix, don't use it */
2938 	if (casefold) {
2939 		if (strncasecmp(cstate->ecpl_token_str, str,
2940 		    cstate->ecpl_token_len) != 0)
2941 			return;
2942 	} else {
2943 		if (strncmp(cstate->ecpl_token_str, str,
2944 		    cstate->ecpl_token_len) != 0)
2945 			return;
2946 	}
2947 
2948 	if (cstate->ecpl_add_mod_colon) {
2949 		cont_suffix = type_suffix = MSG_ORIG(MSG_STR_COLON);
2950 	} else {
2951 		cont_suffix = MSG_ORIG(MSG_STR_SPACE);
2952 		type_suffix = NULL;
2953 	}
2954 	(void) cpl_add_completion(cstate->ecpl_cpl, cstate->ecpl_line,
2955 	    cstate->ecpl_word_start, cstate->ecpl_word_end,
2956 	    str + cstate->ecpl_token_len, type_suffix, cont_suffix);
2957 
2958 }
2959 
2960 
2961 /*
2962  * Convenience wrapper on elfedit_cpl_match(): Format an unsigned
2963  * 32-bit integer as a string and enter the result for command completion.
2964  */
2965 void
elfedit_cpl_ndx(void * cpldata,uint_t ndx)2966 elfedit_cpl_ndx(void *cpldata, uint_t ndx)
2967 {
2968 	Conv_inv_buf_t	buf;
2969 
2970 	(void) snprintf(buf.buf, sizeof (buf.buf),
2971 	    MSG_ORIG(MSG_FMT_WORDVAL), ndx);
2972 	elfedit_cpl_match(cpldata, buf.buf, 0);
2973 }
2974 
2975 
2976 /*
2977  * Compare the token to the names of the commands from the given module,
2978  * and if they share a common initial sequence, add the tail of string
2979  * to the tecla command completion buffer:
2980  *
2981  * entry:
2982  *	tok_buf - Token user has entered
2983  *	tok_len - strlen(tok_buf)
2984  *	mod - Module definition from which commands should be matched
2985  *	cpl, line, word_start, word_end, cont_suffix - As documented
2986  *		for gl_get_line() and cpl_add_completion.
2987  */
2988 static void
match_module_cmds(ELFEDIT_CPL_STATE * cstate,elfeditGC_module_t * mod)2989 match_module_cmds(ELFEDIT_CPL_STATE *cstate, elfeditGC_module_t *mod)
2990 {
2991 	elfeditGC_cmd_t *cmd;
2992 	const char **cmd_name;
2993 
2994 	for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++)
2995 		for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++)
2996 			elfedit_cpl_match(cstate, *cmd_name, 1);
2997 }
2998 
2999 
3000 /*
3001  * Compare the token to the known module names, and add those that
3002  * match to the list of alternatives via elfedit_cpl_match().
3003  *
3004  * entry:
3005  *	load_all_modules - If True, causes all modules to be loaded
3006  *		before processing is done. If False, only the modules
3007  *		currently seen will be used.
3008  */
3009 void
elfedit_cpl_module(void * cpldata,int load_all_modules)3010 elfedit_cpl_module(void *cpldata, int load_all_modules)
3011 {
3012 	ELFEDIT_CPL_STATE	*cstate = (ELFEDIT_CPL_STATE *) cpldata;
3013 	MODLIST_T		*modlist;
3014 
3015 	if (load_all_modules)
3016 		elfedit_cpl_load_modules();
3017 
3018 	for (modlist = state.modlist; modlist != NULL;
3019 	    modlist = modlist->ml_next) {
3020 		elfedit_cpl_match(cstate, modlist->ml_mod->mod_name, 1);
3021 	}
3022 }
3023 
3024 
3025 /*
3026  * Compare the token to all the known commands, and add those that
3027  * match to the list of alternatives.
3028  *
3029  * note:
3030  *	This routine will force modules to be loaded as necessary to
3031  *	obtain the names it needs to match.
3032  */
3033 void
elfedit_cpl_command(void * cpldata)3034 elfedit_cpl_command(void *cpldata)
3035 {
3036 	ELFEDIT_CPL_STATE	*cstate = (ELFEDIT_CPL_STATE *) cpldata;
3037 	ELFEDIT_CPL_STATE	colon_state;
3038 	const char		*colon_pos;
3039 	MODLIST_T		*modlist;
3040 	MODLIST_T		*insdef;
3041 	char			buf[128];
3042 
3043 	/*
3044 	 * Is there a colon in the command? If so, locate its offset within
3045 	 * the raw input line.
3046 	 */
3047 	for (colon_pos = cstate->ecpl_token_str;
3048 	    *colon_pos && (*colon_pos != ':'); colon_pos++)
3049 		;
3050 
3051 	/*
3052 	 * If no colon was seen, then we are completing a module name,
3053 	 * or one of the commands from 'sys:'
3054 	 */
3055 	if (*colon_pos == '\0') {
3056 		/*
3057 		 * Setting cstate->add_mod_colon tells elfedit_cpl_match()
3058 		 * to add an implicit ':' to the names it matches. We use it
3059 		 * here so the user doesn't have to enter the ':' manually.
3060 		 * Hiding this in the opaque state instead of making it
3061 		 * an argument to that function gives us the ability to
3062 		 * change it later without breaking the published interface.
3063 		 */
3064 		cstate->ecpl_add_mod_colon = 1;
3065 		elfedit_cpl_module(cpldata, 1);
3066 		cstate->ecpl_add_mod_colon = 0;
3067 
3068 		/* Add bare (no sys: prefix) commands from the sys: module */
3069 		match_module_cmds(cstate,
3070 		    elfedit_load_module(MSG_ORIG(MSG_MOD_SYS), 1, 0));
3071 
3072 		return;
3073 	}
3074 
3075 	/*
3076 	 * A colon was seen, so we have a module name. Extract the name,
3077 	 * substituting 'sys' for the case where the given name is empty.
3078 	 */
3079 	if (colon_pos == 0)
3080 		(void) strlcpy(buf, MSG_ORIG(MSG_MOD_SYS), sizeof (buf));
3081 	else
3082 		elfedit_strnbcpy(buf, cstate->ecpl_token_str,
3083 		    colon_pos - cstate->ecpl_token_str, sizeof (buf));
3084 
3085 	/*
3086 	 * Locate the module. If it isn't already loaded, make an explicit
3087 	 * attempt to load it and try again. If a module definition is
3088 	 * obtained, process the commands it supplies.
3089 	 */
3090 	modlist = module_loaded(buf, &insdef);
3091 	if (modlist == NULL) {
3092 		(void) elfedit_load_module(buf, 0, 0);
3093 		modlist = module_loaded(buf, &insdef);
3094 	}
3095 	if (modlist != NULL) {
3096 		/*
3097 		 * Make a copy of the cstate, and adjust the line and
3098 		 * token so that the new one starts just past the colon
3099 		 * character. We know that the colon exists because
3100 		 * of the preceeding test that found it. Therefore, we do
3101 		 * not need to test against running off the end of the
3102 		 * string here.
3103 		 */
3104 		colon_state = *cstate;
3105 		while (colon_state.ecpl_line[colon_state.ecpl_word_start] !=
3106 		    ':')
3107 			colon_state.ecpl_word_start++;
3108 		while (*colon_state.ecpl_token_str != ':') {
3109 			colon_state.ecpl_token_str++;
3110 			colon_state.ecpl_token_len--;
3111 		}
3112 		/* Skip past the ':' character */
3113 		colon_state.ecpl_word_start++;
3114 		colon_state.ecpl_token_str++;
3115 		colon_state.ecpl_token_len--;
3116 
3117 		match_module_cmds(&colon_state, modlist->ml_mod);
3118 	}
3119 }
3120 
3121 
3122 /*
3123  * Command completion function for use with libtacla.
3124  */
3125 /*ARGSUSED1*/
3126 static int
cmd_match_fcn(WordCompletion * cpl,void * data,const char * line,int word_end)3127 cmd_match_fcn(WordCompletion *cpl, void *data, const char *line, int word_end)
3128 {
3129 	const char		*argv[ELFEDIT_MAXCPLARGS];
3130 	ELFEDIT_CPL_STATE	cstate;
3131 	TOK_STATE		*tokst;
3132 	int			ndx;
3133 	int			i;
3134 	elfeditGC_module_t	*mod;
3135 	elfeditGC_cmd_t		*cmd;
3136 	int			num_opt;
3137 	int			opt_term_seen;
3138 	int			skip_one;
3139 	elfedit_cmd_optarg_t	*optarg;
3140 	elfedit_optarg_item_t	item;
3141 	int			ostyle_ndx = -1;
3142 
3143 	/*
3144 	 * For debugging, enable the following block. It tells the tecla
3145 	 * library that the program using is going to write to stdout.
3146 	 * It will put the tty back into normal mode, and it will cause
3147 	 * tecla to redraw the current input line when it gets control back.
3148 	 */
3149 #ifdef DEBUG_CMD_MATCH
3150 	gl_normal_io(state.input.gl);
3151 #endif
3152 
3153 	/*
3154 	 * Tokenize the line up through word_end. The last token in
3155 	 * the list is the one requiring completion.
3156 	 */
3157 	tokst = tokenize_user_cmd(line, word_end, 1);
3158 	if (tokst->tokst_cnt == 0)
3159 		return (0);
3160 
3161 	/* Set up the cstate block, containing the completion state */
3162 	ndx = tokst->tokst_cnt - 1;	/* Index of token to complete */
3163 	cstate.ecpl_cpl = cpl;
3164 	cstate.ecpl_line = line;
3165 	cstate.ecpl_word_start = tokst->tokst_buf[ndx].tok_line_off;
3166 	cstate.ecpl_word_end = word_end;
3167 	cstate.ecpl_add_mod_colon = 0;
3168 	cstate.ecpl_token_str = tokst->tokst_buf[ndx].tok_str;
3169 	cstate.ecpl_token_len = tokst->tokst_buf[ndx].tok_len;
3170 
3171 	/*
3172 	 * If there is only one token, then we are completing the
3173 	 * command itself.
3174 	 */
3175 	if (ndx == 0) {
3176 		elfedit_cpl_command(&cstate);
3177 		return (0);
3178 	}
3179 
3180 	/*
3181 	 * There is more than one token. Use the first one to
3182 	 * locate the definition for the command. If we don't have
3183 	 * a definition for the command, then there's nothing more
3184 	 * we can do.
3185 	 */
3186 	cmd = elfedit_find_command(tokst->tokst_buf[0].tok_str, 0, &mod);
3187 	if (cmd == NULL)
3188 		return (0);
3189 
3190 	/*
3191 	 * Since we know the command, give them a quick usage message.
3192 	 * It may be that they just need a quick reminder about the form
3193 	 * of the command and the options.
3194 	 */
3195 	(void) gl_normal_io(state.input.gl);
3196 	elfedit_printf(MSG_INTL(MSG_USAGE_CMD),
3197 	    elfedit_format_command_usage(mod, cmd, NULL, 0));
3198 
3199 
3200 	/*
3201 	 * We have a generous setting for ELFEDIT_MAXCPLARGS, so there
3202 	 * should always be plenty of room. If there's not room, we
3203 	 * can't proceed.
3204 	 */
3205 	if (ndx >= ELFEDIT_MAXCPLARGS)
3206 		return (0);
3207 
3208 	/*
3209 	 * Put pointers to the tokens into argv, and determine how
3210 	 * many of the tokens are optional arguments.
3211 	 *
3212 	 * We consider the final optional argument to be the rightmost
3213 	 * argument that starts with a '-'. If a '--' is seen, then
3214 	 * we stop there, and any argument that follows is a plain argument
3215 	 * (even if it starts with '-').
3216 	 *
3217 	 * We look for an inherited '-o' option, because we are willing
3218 	 * to supply command completion for these values.
3219 	 */
3220 	num_opt = 0;
3221 	opt_term_seen = 0;
3222 	skip_one = 0;
3223 	for (i = 0; i < ndx; i++) {
3224 		argv[i] = tokst->tokst_buf[i + 1].tok_str;
3225 		if (opt_term_seen || skip_one) {
3226 			skip_one = 0;
3227 			continue;
3228 		}
3229 		skip_one = 0;
3230 		ostyle_ndx = -1;
3231 		if ((strcmp(argv[i], MSG_ORIG(MSG_STR_MINUS_MINUS)) == 0) ||
3232 		    (*argv[i] != '-')) {
3233 			opt_term_seen = 1;
3234 			continue;
3235 		}
3236 		num_opt = i + 1;
3237 		/*
3238 		 * If it is a recognised ELFEDIT_CMDOA_F_VALUE option,
3239 		 * then the item following it is the associated value.
3240 		 * Check for this and skip the value.
3241 		 *
3242 		 * At the same time, look for STDOA_OPT_O inherited
3243 		 * options. We want to identify the index of any such
3244 		 * item. Although the option is simply "-o", we are willing
3245 		 * to treat any option that starts with "-o" as a potential
3246 		 * STDOA_OPT_O. This lets us to command completion for things
3247 		 * like "-onum", and is otherwise harmless, the only cost
3248 		 * being a few additional strcmps by the cpl code.
3249 		 */
3250 		if ((optarg = cmd->cmd_opt) == NULL)
3251 			continue;
3252 		while (optarg->oa_name != NULL) {
3253 			int is_ostyle_optarg =
3254 			    (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) &&
3255 			    (optarg->oa_name == ELFEDIT_STDOA_OPT_O);
3256 
3257 			elfedit_next_optarg(&optarg, &item);
3258 			if (item.oai_flags & ELFEDIT_CMDOA_F_VALUE) {
3259 				if (is_ostyle_optarg && (strncmp(argv[i],
3260 				    MSG_ORIG(MSG_STR_MINUS_O), 2) == 0))
3261 					ostyle_ndx = i + 1;
3262 
3263 				if (strcmp(item.oai_name, argv[i]) == 0) {
3264 					num_opt = i + 2;
3265 					skip_one = 1;
3266 					break;
3267 				}
3268 				/*
3269 				 * If it didn't match "-o" exactly, but it is
3270 				 * ostyle_ndx, then it is a potential combined
3271 				 * STDOA_OPT_O, as discussed above. It counts
3272 				 * as a single argument.
3273 				 */
3274 				if (ostyle_ndx == ndx)
3275 					break;
3276 			}
3277 		}
3278 	}
3279 
3280 #ifdef DEBUG_CMD_MATCH
3281 	(void) printf("NDX(%d) NUM_OPT(%d) ostyle_ndx(%d)\n", ndx, num_opt,
3282 	    ostyle_ndx);
3283 #endif
3284 
3285 	if (ostyle_ndx != -1) {
3286 		/*
3287 		 * If ostyle_ndx is one less than ndx, and ndx is
3288 		 * the same as num_opt, then we have a definitive
3289 		 * STDOA_OPT_O inherited outstyle option. We supply
3290 		 * the value strings, and are done.
3291 		 */
3292 		if ((ostyle_ndx == (ndx - 1)) && (ndx == num_opt)) {
3293 			elfedit_cpl_atoconst(&cstate, ELFEDIT_CONST_OUTSTYLE);
3294 			return (0);
3295 		}
3296 
3297 		/*
3298 		 * If ostyle is the same as ndx, then we have an option
3299 		 * staring with "-o" that may end up being a STDOA_OPT_O,
3300 		 * and we are still inside that token. In this case, we
3301 		 * supply completion strings that include the leading
3302 		 * "-o" followed by the values, without a space
3303 		 * (i.e. "-onum"). We then fall through, allowing any
3304 		 * other options starting with "-o" to be added
3305 		 * below. elfedit_cpl_match() will throw out the incorrect
3306 		 * options, so it is harmless to add these extra items in
3307 		 * the worst case, and useful otherwise.
3308 		 */
3309 		if (ostyle_ndx == ndx)
3310 			elfedit_cpl_atoconst(&cstate,
3311 			    ELFEDIT_CONST_OUTSTYLE_MO);
3312 	}
3313 
3314 	/*
3315 	 * If (ndx <= num_opt), then the token needing completion
3316 	 * is an option. If the leading '-' is there, then we should fill
3317 	 * in all of the option alternatives. If anything follows the '-'
3318 	 * though, we assume that the user has already figured out what
3319 	 * option to use, and we leave well enough alone.
3320 	 *
3321 	 * Note that we are intentionally ignoring a related case
3322 	 * where supplying option strings would be legal: In the case
3323 	 * where we are one past the last option (ndx == (num_opt + 1)),
3324 	 * and the current option is an empty string, the argument can
3325 	 * be either a plain argument or an option --- the user needs to
3326 	 * enter the next character before we can tell. It would be
3327 	 * OK to enter the option strings in this case. However, consider
3328 	 * what happens when the first plain argument to the command does
3329 	 * not provide any command completion (e.g. it is a plain integer).
3330 	 * In this case, tecla will see that all the alternatives start
3331 	 * with '-', and will insert a '-' into the input. If the user
3332 	 * intends the next argument to be plain, they will have to delete
3333 	 * this '-', which is annoying. Worse than that, they may be confused
3334 	 * by it, and think that the plain argument is not allowed there.
3335 	 * The best solution is to not supply option strings unless the
3336 	 * user first enters the '-'.
3337 	 */
3338 	if ((ndx <= num_opt) && (argv[ndx - 1][0] == '-')) {
3339 		if ((optarg = cmd->cmd_opt) != NULL) {
3340 			while (optarg->oa_name != NULL) {
3341 				elfedit_next_optarg(&optarg, &item);
3342 				elfedit_cpl_match(&cstate, item.oai_name, 1);
3343 			}
3344 		}
3345 		return (0);
3346 	}
3347 
3348 	/*
3349 	 * At this point we know that ndx and num_opt are not equal.
3350 	 * If num_opt is larger than ndx, then we have an ELFEDIT_CMDOA_F_VALUE
3351 	 * argument at the end, and the following value has not been entered.
3352 	 *
3353 	 * If ndx is greater than num_opt, it means that we are looking
3354 	 * at a plain argument (or in the case where (ndx == (num_opt + 1)),
3355 	 * a *potential* plain argument.
3356 	 *
3357 	 * If the command has a completion function registered, then we
3358 	 * hand off the remaining work to it. The cmd_cplfunc field is
3359 	 * the generic definition. We need to cast it to the type that matches
3360 	 * the proper ELFCLASS before calling it.
3361 	 */
3362 	if (state.elf.elfclass == ELFCLASS32) {
3363 		elfedit32_cmdcpl_func_t *cmdcpl_func =
3364 		    (elfedit32_cmdcpl_func_t *)cmd->cmd_cplfunc;
3365 
3366 		if (cmdcpl_func != NULL)
3367 			(* cmdcpl_func)(state.elf.obj_state.s32,
3368 			    &cstate, ndx, argv, num_opt);
3369 	} else {
3370 		elfedit64_cmdcpl_func_t *cmdcpl_func =
3371 		    (elfedit64_cmdcpl_func_t *)cmd->cmd_cplfunc;
3372 
3373 		if (cmdcpl_func != NULL)
3374 			(* cmdcpl_func)(state.elf.obj_state.s64,
3375 			    &cstate, ndx, argv, num_opt);
3376 	}
3377 
3378 	return (0);
3379 }
3380 
3381 
3382 /*
3383  * Read a line of input from stdin, and return pointer to it.
3384  *
3385  * This routine uses a private buffer, so the contents of the returned
3386  * string are only good until the next call.
3387  */
3388 static const char *
read_cmd(void)3389 read_cmd(void)
3390 {
3391 	char *s;
3392 
3393 	if (state.input.full_tty) {
3394 		state.input.in_tecla = TRUE;
3395 		s = gl_get_line(state.input.gl,
3396 		    MSG_ORIG(MSG_STR_PROMPT), NULL, -1);
3397 		state.input.in_tecla = FALSE;
3398 		/*
3399 		 * gl_get_line() returns NULL for EOF or for error. EOF is fine,
3400 		 * but we need to catch and report anything else. Since
3401 		 * reading from stdin is critical to our operation, an
3402 		 * error implies that we cannot recover and must exit.
3403 		 */
3404 		if ((s == NULL) &&
3405 		    (gl_return_status(state.input.gl) == GLR_ERROR)) {
3406 			elfedit_msg(ELFEDIT_MSG_FATAL, MSG_INTL(MSG_ERR_GLREAD),
3407 			    gl_error_message(state.input.gl, NULL, 0));
3408 		}
3409 	} else {
3410 		/*
3411 		 * This should be a dynamically sized buffer, but for now,
3412 		 * I'm going to take a simpler path.
3413 		 */
3414 		static char cmd_buf[ELFEDIT_MAXCMD + 1];
3415 
3416 		s = fgets(cmd_buf, sizeof (cmd_buf), stdin);
3417 	}
3418 
3419 	/* Return user string, or 'quit' on EOF */
3420 	return (s ? s : MSG_ORIG(MSG_SYS_CMD_QUIT));
3421 }
3422 
3423 int
main(int argc,char ** argv,char ** envp)3424 main(int argc, char **argv, char **envp)
3425 {
3426 	/*
3427 	 * Note: This function can use setjmp()/longjmp() which does
3428 	 * not preserve the values of auto/register variables. Hence,
3429 	 * variables that need their values preserved across a jump must
3430 	 * be marked volatile, or must not be auto/register.
3431 	 *
3432 	 * Volatile can be messy, because it requires explictly casting
3433 	 * away the attribute when passing it to functions, or declaring
3434 	 * those functions with the attribute as well. In a single threaded
3435 	 * program like this one, an easier approach is to make things
3436 	 * static. That can be done here, or by putting things in the
3437 	 * 'state' structure.
3438 	 */
3439 
3440 	int		c, i;
3441 	int		num_batch = 0;
3442 	char		**batch_list = NULL;
3443 	const char	*modpath = NULL;
3444 
3445 	/*
3446 	 * Always have liblddb display unclipped section names.
3447 	 * This global is exported by liblddb, and declared in debug.h.
3448 	 */
3449 	dbg_desc->d_extra |= DBG_E_LONG;
3450 
3451 	opterr = 0;
3452 	while ((c = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) {
3453 		switch (c) {
3454 		case 'a':
3455 			state.flags |= ELFEDIT_F_AUTOPRINT;
3456 			break;
3457 
3458 		case 'd':
3459 			state.flags |= ELFEDIT_F_DEBUG;
3460 			break;
3461 
3462 		case 'e':
3463 			/*
3464 			 * Delay parsing the -e options until after the call to
3465 			 * conv_check_native() so that we won't bother loading
3466 			 * modules of the wrong class.
3467 			 */
3468 			if (batch_list == NULL)
3469 				batch_list = elfedit_malloc(
3470 				    MSG_INTL(MSG_ALLOC_BATCHLST),
3471 				    sizeof (*batch_list) * (argc - 1));
3472 			batch_list[num_batch++] = optarg;
3473 			break;
3474 
3475 		case 'L':
3476 			modpath = optarg;
3477 			break;
3478 
3479 		case 'o':
3480 			if (elfedit_atooutstyle(optarg, &state.outstyle) == 0)
3481 				usage(1);
3482 			break;
3483 
3484 		case 'r':
3485 			state.flags |= ELFEDIT_F_READONLY;
3486 			break;
3487 
3488 		case '?':
3489 			usage(1);
3490 		}
3491 	}
3492 
3493 	/*
3494 	 * We allow 0, 1, or 2 files:
3495 	 *
3496 	 * The no-file case is an extremely limited mode, in which the
3497 	 * only commands allowed to execute come from the sys: module.
3498 	 * This mode exists primarily to allow easy access to the help
3499 	 * facility.
3500 	 *
3501 	 * To get full access to elfedit's capablities, there must
3502 	 * be an input file. If this is not a readonly
3503 	 * session, then an optional second output file is allowed.
3504 	 *
3505 	 * In the case where two files are given and the session is
3506 	 * readonly, use a full usage message, because the simple
3507 	 * one isn't enough for the user to understand their error.
3508 	 * Otherwise, the simple usage message suffices.
3509 	 */
3510 	argc = argc - optind;
3511 	if ((argc == 2) && (state.flags & ELFEDIT_F_READONLY))
3512 		usage(1);
3513 	if (argc > 2)
3514 		usage(0);
3515 
3516 	state.file.present = (argc != 0);
3517 
3518 	/*
3519 	 * If we have a file to edit, and unless told otherwise by the
3520 	 * caller, we try to run the 64-bit version of this program
3521 	 * when the system is capable of it. If that fails, then we
3522 	 * continue on with the currently running version.
3523 	 *
3524 	 * To force 32-bit execution on a 64-bit host, set the
3525 	 * LD_NOEXEC_64 environment variable to a non-empty value.
3526 	 *
3527 	 * There is no reason to bother with this if in "no file" mode.
3528 	 */
3529 	if (state.file.present != 0)
3530 		(void) conv_check_native(argv, envp);
3531 
3532 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_VERSION),
3533 	    (sizeof (char *) == 8) ? 64 : 32);
3534 
3535 	/*
3536 	 * Put a module definition for the builtin system module on the
3537 	 * module list. We know it starts out empty, so we do not have
3538 	 * to go through a more general insertion process than this.
3539 	 */
3540 	state.modlist = elfedit_sys_init(ELFEDIT_VER_CURRENT);
3541 
3542 	/* Establish the search path for loadable modules */
3543 	establish_modpath(modpath);
3544 
3545 	/*
3546 	 * Now that we are running the final version of this program,
3547 	 * deal with the input/output file(s).
3548 	 */
3549 	if (state.file.present == 0) {
3550 		/*
3551 		 * This is arbitrary --- we simply need to be able to
3552 		 * load modules so that we can access their help strings
3553 		 * and command completion functions. Without a file, we
3554 		 * will refuse to call commands from any module other
3555 		 * than sys. Those commands have been written to be aware
3556 		 * of the case where there is no input file, and are
3557 		 * therefore safe to run.
3558 		 */
3559 		state.elf.elfclass = ELFCLASS32;
3560 		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_NOFILE));
3561 
3562 	} else {
3563 		state.file.infile = argv[optind];
3564 		if (argc == 1) {
3565 			state.file.outfile = state.file.infile;
3566 			if (state.flags & ELFEDIT_F_READONLY)
3567 				elfedit_msg(ELFEDIT_MSG_DEBUG,
3568 				    MSG_INTL(MSG_DEBUG_READONLY));
3569 			else
3570 				elfedit_msg(ELFEDIT_MSG_DEBUG,
3571 				    MSG_INTL(MSG_DEBUG_INPLACEWARN),
3572 				    state.file.infile);
3573 		} else {
3574 			state.file.outfile = argv[optind + 1];
3575 			create_outfile(state.file.infile, state.file.outfile);
3576 			elfedit_msg(ELFEDIT_MSG_DEBUG,
3577 			    MSG_INTL(MSG_DEBUG_CPFILE),
3578 			    state.file.infile, state.file.outfile);
3579 			/*
3580 			 * We are editing a copy of the original file that we
3581 			 * just created. If we should exit before the edits are
3582 			 * updated, then we want to unlink this copy so that we
3583 			 * don't leave junk lying around. Once an update
3584 			 * succeeds however, we'll leave it in place even
3585 			 * if an error occurs afterwards.
3586 			 */
3587 			state.file.unlink_on_exit = 1;
3588 			optind++;	/* Edit copy instead of the original */
3589 		}
3590 
3591 		init_obj_state(state.file.outfile);
3592 	}
3593 
3594 
3595 	/*
3596 	 * Process commands.
3597 	 *
3598 	 * If any -e options were used, then do them and
3599 	 * immediately exit. On error, exit immediately without
3600 	 * updating the target ELF file. On success, the 'write'
3601 	 * and 'quit' commands are implicit in this mode.
3602 	 *
3603 	 * If no -e options are used, read commands from stdin.
3604 	 * quit must be explicitly used. Exit is implicit on EOF.
3605 	 * If stdin is a tty, then errors do not cause the editor
3606 	 * to terminate. Rather, the error message is printed, and the
3607 	 * user prompted to continue.
3608 	 */
3609 	if (batch_list != NULL) {	/* -e was used */
3610 		/* Compile the commands */
3611 		for (i = 0; i < num_batch; i++)
3612 			parse_user_cmd(batch_list[i]);
3613 		free(batch_list);
3614 
3615 		/*
3616 		 * 'write' and 'quit' are implicit in this mode.
3617 		 * Add them as well.
3618 		 */
3619 		if ((state.flags & ELFEDIT_F_READONLY) == 0)
3620 			parse_user_cmd(MSG_ORIG(MSG_SYS_CMD_WRITE));
3621 		parse_user_cmd(MSG_ORIG(MSG_SYS_CMD_QUIT));
3622 
3623 		/* And run them. This won't return, thanks to the 'quit' */
3624 		dispatch_user_cmds();
3625 	} else {
3626 		state.input.is_tty = isatty(fileno(stdin));
3627 		state.input.full_tty = state.input.is_tty &&
3628 		    isatty(fileno(stdout));
3629 
3630 		if (state.input.full_tty) {
3631 			struct sigaction act;
3632 
3633 			act.sa_sigaction = sigint_handler;
3634 			(void) sigemptyset(&act.sa_mask);
3635 			act.sa_flags = 0;
3636 			if (sigaction(SIGINT, &act, NULL) == -1) {
3637 				int err = errno;
3638 				elfedit_msg(ELFEDIT_MSG_ERR,
3639 				    MSG_INTL(MSG_ERR_SIGACTION), strerror(err));
3640 			}
3641 			/*
3642 			 * If pager process exits before we are done
3643 			 * writing, we can see SIGPIPE. Prevent it
3644 			 * from killing the process.
3645 			 */
3646 			(void) sigignore(SIGPIPE);
3647 
3648 			/* Open tecla handle for command line editing */
3649 			state.input.gl = new_GetLine(ELFEDIT_MAXCMD,
3650 			    ELFEDIT_MAXHIST);
3651 			/* Register our command completion function */
3652 			(void) gl_customize_completion(state.input.gl,
3653 			    NULL, cmd_match_fcn);
3654 
3655 			/*
3656 			 * Make autoprint the default for interactive
3657 			 * sessions.
3658 			 */
3659 			state.flags |= ELFEDIT_F_AUTOPRINT;
3660 		}
3661 		for (;;) {
3662 			/*
3663 			 * If this is an interactive session, then use
3664 			 * sigsetjmp()/siglongjmp() to recover from bad
3665 			 * commands and keep going. A non-0 return from
3666 			 * sigsetjmp() means that an error just occurred.
3667 			 * In that case, we simply restart this loop.
3668 			 */
3669 			if (state.input.is_tty) {
3670 				if (sigsetjmp(state.msg_jbuf.env, 1) != 0) {
3671 					if (state.input.full_tty)
3672 						gl_abandon_line(state.input.gl);
3673 					continue;
3674 				}
3675 				state.msg_jbuf.active = TRUE;
3676 			}
3677 
3678 			/*
3679 			 * Force all output out before each command.
3680 			 * This is a no-OP when a tty is in use, but
3681 			 * in a pipeline, it ensures that the block
3682 			 * mode buffering doesn't delay output past
3683 			 * the completion of each command.
3684 			 *
3685 			 * If we didn't do this, the output would eventually
3686 			 * arrive at its destination, but the lag can be
3687 			 * annoying when you pipe the output into a tool
3688 			 * that displays the results in real time.
3689 			 */
3690 			(void) fflush(stdout);
3691 			(void) fflush(stderr);
3692 
3693 			parse_user_cmd(read_cmd());
3694 			dispatch_user_cmds();
3695 			state.msg_jbuf.active = FALSE;
3696 		}
3697 	}
3698 
3699 
3700 	/*NOTREACHED*/
3701 	return (0);
3702 }
3703