xref: /illumos-gate/usr/src/cmd/sgs/elfedit/common/sys.c (revision d29b2c44)
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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <fcntl.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <strings.h>
33 #include <elfedit.h>
34 #include "_elfedit.h"
35 #include "msg.h"
36 
37 
38 
39 
40 /*
41  * This file provides the builtin sys module. It is similar to the
42  * other modules, but differs in several important ways:
43  *
44  *	- It is built as a static part of elfedit, and not
45  *		as a sharable object.
46  *	- It must be avaialble before the ELFCLASS of the object
47  *		is known, so it is not ELFCLASS specific. We don't build
48  *		it twice with machdep.h, as we do for the loadable modules.
49  *		This means that commands need to test for the type
50  *		of their obj_state argument at runtime.
51  *	- The init function signature is different. We build an entire
52  *		module definition statically.
53  */
54 
55 
56 
57 /*
58  * This function is supplied to elfedit through our elfedit_module_t
59  * definition. It translates the opaque elfedit_i18nhdl_t handles
60  * in our module interface into the actual strings for elfedit to
61  * use.
62  *
63  * note:
64  *	This module uses Msg codes for its i18n handle type.
65  *	So the translation is simply to use MSG_INTL() to turn
66  *	it into a string and return it.
67  */
68 static const char *
69 mod_i18nhdl_to_str(elfedit_i18nhdl_t hdl)
70 {
71 	Msg msg = (Msg)hdl;
72 
73 	return (MSG_INTL(msg));
74 }
75 
76 
77 
78 /*
79  * The sys_opt_t enum specifies a bit value for every optional argument
80  * allowed by a command in this module.
81  */
82 typedef enum {
83 	SYS_OPT_F_ALL =		1,	/* -a */
84 	SYS_OPT_F_FORCE =	2,	/* -f */
85 	SYS_OPT_F_SYNOPSIS =	4,	/* -s */
86 } dyn_opt_t;
87 
88 
89 /*
90  * Given a generic (void *) pointer to an obj_state argument, determine
91  * which type it is, and return the st_file, st_fd and st_elf fields.
92  */
93 static void
94 get_obj_state_info(void *obj_state, const char **file, int *fd, Elf **elf)
95 {
96 	if (state.elf.elfclass == ELFCLASS32) {
97 		elfedit32_obj_state_t *s = (elfedit32_obj_state_t *)obj_state;
98 
99 		*file = s->os_file;
100 		*fd = s->os_fd;
101 		*elf = s->os_elf;
102 	} else {
103 		elfedit64_obj_state_t *s = (elfedit64_obj_state_t *)obj_state;
104 
105 		*file = s->os_file;
106 		*fd = s->os_fd;
107 		*elf = s->os_elf;
108 	}
109 }
110 
111 
112 
113 /*
114  * Helper for cmd_help(). Displays synopsis information for one command.
115  */
116 static void
117 cmd_help_synopsis(elfeditGC_module_t *mod, elfeditGC_cmd_t *cmd)
118 {
119 	char		name_buf[128];
120 	const char	*name;
121 	const char	**cmd_name;
122 
123 	if (cmd->cmd_name[1] == NULL) {   /* One name */
124 		name = *cmd->cmd_name;
125 	} else {
126 		const char *cname;
127 		int need_comma = 0;
128 
129 		name = name_buf;
130 		(void) snprintf(name_buf, sizeof (name_buf),
131 		    MSG_ORIG(MSG_HLPFMT_MULTNAM), cmd->cmd_name[0]);
132 		for (cmd_name = cmd->cmd_name + 1;
133 		    *cmd_name; cmd_name++) {
134 			if (need_comma)
135 				(void) strlcat(name_buf,
136 				    MSG_ORIG(MSG_STR_COMMA_SP),
137 				    sizeof (name_buf));
138 			need_comma = 1;
139 			cname = (cmd_name[0][0] == '\0') ?
140 			    MSG_INTL(MSG_HLPFMT_MODDEFCMD) : *cmd_name;
141 			(void) strlcat(name_buf, cname,
142 			    sizeof (name_buf));
143 		}
144 		(void) strlcat(name_buf, MSG_ORIG(MSG_STR_CPAREN),
145 		    sizeof (name_buf));
146 	}
147 	elfedit_printf(MSG_ORIG(MSG_HLPFMT_NAMSUMHDR), name,
148 	    (* mod->mod_i18nhdl_to_str)(cmd->cmd_desc));
149 	elfedit_printf(MSG_INTL(MSG_HLPFMT_SUMSYNOPSIS),
150 	    elfedit_format_command_usage(mod, cmd,
151 	    MSG_ORIG(MSG_STR_HLPSUMINDENT),
152 	    strlen(MSG_ORIG(MSG_STR_HLPSUMINDENT))));
153 }
154 
155 
156 /*
157  * Helper for cmd_help(). Displays synopsis information for one module.
158  */
159 static void
160 cmd_help_showmod(elfeditGC_module_t *mod)
161 {
162 	elfeditGC_cmd_t	*cmd;
163 
164 	elfedit_printf(MSG_ORIG(MSG_HLPFMT_NAMDSCHDR),
165 	    mod->mod_name, (* mod->mod_i18nhdl_to_str)(mod->mod_desc));
166 	for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++) {
167 		if (cmd != mod->mod_cmds)
168 			elfedit_printf(MSG_ORIG(MSG_STR_NL));
169 		elfedit_printf(MSG_ORIG(MSG_STR_NL));
170 		cmd_help_synopsis(mod, cmd);
171 	}
172 }
173 
174 
175 /*
176  * Given a string containing newline characters, break it into
177  * individual lines, and output each line with the given
178  * prefix string in front.
179  */
180 static void
181 write_help_str(const char *str, const char *prefix)
182 {
183 	size_t i;
184 
185 	if (str == NULL)
186 		return;
187 	while (*str) {
188 		i = strcspn(str, MSG_ORIG(MSG_STR_NL));
189 		if (*(str + i) != '\0')
190 			i++;
191 		elfedit_printf(prefix);
192 		elfedit_write(str, i);
193 		str += i;
194 	}
195 }
196 
197 
198 /*
199  * Given a title, and a NULL terminated list of option/argument
200  * descriptors, output the list contents.
201  */
202 static void
203 write_optarg(elfeditGC_module_t *mod, const char *title,
204     elfedit_cmd_optarg_t *optarg)
205 {
206 	int			cnt;
207 	int			len;
208 	const char		*help;
209 	elfedit_optarg_item_t	item;
210 
211 	elfedit_printf(title);
212 	for (cnt = 0; optarg->oa_name != NULL; cnt++) {
213 		elfedit_next_optarg(&optarg, &item);
214 
215 		/* Insert a blank line between items */
216 		if (cnt > 0)
217 			elfedit_printf(MSG_ORIG(MSG_STR_NL));
218 
219 		/* Indentation */
220 		elfedit_printf(MSG_ORIG(MSG_STR_HLPINDENT));
221 		len = strlen(item.oai_name);
222 		help = elfedit_optarg_helpstr(mod, &item);
223 		if (item.oai_flags & ELFEDIT_CMDOA_F_VALUE) {
224 			len += 1 + strlen(item.oai_vname);
225 			elfedit_printf(MSG_ORIG(MSG_STR_HLPOPTARG2),
226 			    item.oai_name, item.oai_vname);
227 		} else {
228 			elfedit_printf(MSG_ORIG(MSG_STR_HLPOPTARG),
229 			    item.oai_name);
230 		}
231 
232 		/*
233 		 * If name is too long, inject a newline to avoid
234 		 * crowding the help text.
235 		 */
236 		if (len > 3)
237 			elfedit_printf(MSG_ORIG(MSG_STR_NL));
238 
239 		/* Output the help text with a tab prefix */
240 		write_help_str(help, MSG_ORIG(MSG_STR_TAB));
241 	}
242 }
243 
244 
245 /*
246  * Implementation of sys:help
247  */
248 /*ARGSUSED*/
249 static elfedit_cmdret_t
250 cmd_help(void *obj_state, int argc, const char *argv[])
251 {
252 #define	INITIAL_ITEM_ALLOC 4
253 
254 
255 	/*
256 	 * An array of this type is used to collect the data needed to
257 	 * generate help output.
258 	 */
259 	typedef struct {
260 		elfeditGC_cmd_t		*cmd;
261 		elfeditGC_module_t	*cmd_mod;	/* Used with cmd */
262 		elfeditGC_module_t	*mod;
263 	} ITEM;
264 
265 	static ITEM	*item;
266 	static int	item_cnt;
267 
268 	MODLIST_T		*modlist;
269 	int			dispcnt;
270 	size_t			i;
271 	elfeditGC_module_t	*mod;
272 	elfeditGC_cmd_t		*cmd;
273 	int			minus_s = 0;
274 	elfedit_getopt_state_t	getopt_state;
275 	ITEM			*cur_item;
276 
277 	/*
278 	 * Process options. The only option accepted is -s, so we
279 	 * don't even have to check the idmask to know.
280 	 */
281 	elfedit_getopt_init(&getopt_state, &argc, &argv);
282 	while (elfedit_getopt(&getopt_state) != NULL)
283 		minus_s = 1;
284 
285 	/*
286 	 * This command can produce an arbitrary amount of output, so
287 	 * run a pager.
288 	 */
289 	elfedit_pager_init();
290 
291 	if (argc == 0) {
292 		if (minus_s) {
293 			/* Force all modules to load so we have data */
294 			elfedit_load_modpath();
295 			for (modlist = state.modlist; modlist;
296 			    modlist = modlist->ml_next) {
297 				cmd_help_showmod(modlist->ml_mod);
298 				if (modlist->ml_next != NULL) {
299 					elfedit_printf(MSG_ORIG(MSG_STR_NL));
300 					elfedit_printf(MSG_ORIG(MSG_STR_NL));
301 				}
302 			}
303 			return (ELFEDIT_CMDRET_NONE);
304 		}
305 
306 		/*
307 		 * If no arguments are present, we display a simple
308 		 * "how to use help" tutorial, which will hopefully
309 		 * bootstrap the user into a position where they
310 		 * know how to run the help command, and then find
311 		 * what they're really after.
312 		 */
313 		elfedit_printf(MSG_INTL(MSG_SYS_HELP_HELP_NOARG));
314 		return (ELFEDIT_CMDRET_NONE);
315 	}
316 
317 
318 	/*
319 	 * As we process the arguments, we are willing to treat each
320 	 * one as either a module or a command:
321 	 *	1) An item without a colon can be a module,
322 	 *		or a command from the sys: module.
323 	 *	2) An item with a colon, and no command part is
324 	 *		a module, and it can also be the default
325 	 *		command for the module, if it has one. We choose
326 	 *		to only display the module info in this case, since
327 	 *		the use of "" to represent the default command is
328 	 *		an implementation detail, not a user-facing concept.
329 	 *	3) An item with a colon and a command part can only be
330 	 *		a command.
331 	 *
332 	 * Note that there are cases where one argument can have two
333 	 * valid interpretations. In this case, we display them both.
334 	 *
335 	 * Pass over the arguments and determine how many distinct
336 	 * "things" we need to display. At the same time, force any
337 	 * needed modules to load so that the debug load messages won't
338 	 * show up in between the displayed items, and save the command
339 	 * and module definitions we will need to generate the output.
340 	 */
341 	if (argc > item_cnt) {
342 		int n = (item_cnt == 0) ? INITIAL_ITEM_ALLOC : item_cnt;
343 
344 		while (n < argc)
345 			n *= 2;
346 
347 		item = elfedit_realloc(MSG_INTL(MSG_ALLOC_HELPITEM), item,
348 		    n * sizeof (*item));
349 		item_cnt = n;
350 	}
351 
352 	dispcnt = 0;
353 	for (i = 0; i < argc; i++) {
354 		const char *colon = strchr(argv[i], ':');
355 
356 		if (colon == NULL) {	/* No colon: sys: cmd or module */
357 			item[i].cmd =
358 			    elfedit_find_command(argv[i], 0, &item[i].cmd_mod);
359 			if (item[i].cmd != NULL)
360 				dispcnt++;
361 
362 			/*
363 			 * Also try to load it as a module. If a command
364 			 * was found, then this need not succeed. Otherwise,
365 			 * it has to be a module, and we cause an error
366 			 * to be issued if not.
367 			 */
368 			item[i].mod = elfedit_load_module(argv[i],
369 			    item[i].cmd == NULL, 0);
370 			if (item[i].mod != NULL)
371 				dispcnt++;
372 		} else if (*(colon + 1) == '\0') {
373 			/* Just colon: Module (and maybe default command) */
374 			char buf[ELFEDIT_MAXMODNAM + 1];
375 			const char *str = argv[i];
376 			int len = colon - str;
377 
378 			item[i].cmd = NULL;
379 			/* Strip off the colon */
380 			if (len < sizeof (buf)) {
381 				(void) strncpy(buf, str, len);
382 				buf[len] = '\0';
383 				str = buf;
384 			}
385 			item[i].mod = elfedit_load_module(str, 1, 0);
386 			dispcnt++;
387 		} else {	/* A command */
388 			item[i].cmd =
389 			    elfedit_find_command(argv[i], 1, &item[i].cmd_mod);
390 			dispcnt++;
391 			item[i].mod = NULL;
392 		}
393 	}
394 
395 	/*
396 	 * Having validated the items, loop over them again and produce
397 	 * the required help output.
398 	 */
399 	for (cur_item = item; argc--; argv++, cur_item++) {
400 
401 
402 		/* Help for a module? */
403 		if (cur_item->mod != NULL) {
404 			if (dispcnt > 1)
405 				elfedit_printf(MSG_ORIG(MSG_HLPFMT_MULTIHDR),
406 				    *argv);
407 			cmd_help_showmod(cur_item->mod);
408 			if ((dispcnt > 1) && (argc > 0))
409 				elfedit_printf(MSG_INTL(MSG_HLPFMT_MULTIEND),
410 				    argv[0], argv[1]);
411 			/* An empty line after the last line of output */
412 			elfedit_printf(MSG_ORIG(MSG_STR_NL));
413 		}
414 
415 		/* Help for a command? */
416 		if (cur_item->cmd == NULL)
417 			continue;
418 		cmd = cur_item->cmd;
419 		mod = cur_item->cmd_mod;
420 		if (dispcnt > 1)
421 			elfedit_printf(MSG_ORIG(MSG_HLPFMT_MULTIHDR), *argv);
422 
423 		/* If -s, display quick synopsis rather than the whole thing */
424 		if (minus_s) {
425 			cmd_help_synopsis(mod, cmd);
426 			continue;
427 		}
428 
429 		elfedit_printf(MSG_INTL(MSG_HLPFMT_MOD), mod->mod_name,
430 		    (* mod->mod_i18nhdl_to_str)(mod->mod_desc));
431 		elfedit_printf(MSG_INTL(MSG_HLPFMT_NAME),
432 		    *cmd->cmd_name,
433 		    (* mod->mod_i18nhdl_to_str)(cmd->cmd_desc));
434 		elfedit_printf(MSG_INTL(MSG_HLPFMT_SYNOPSIS),
435 		    elfedit_format_command_usage(mod, cmd,
436 		    MSG_ORIG(MSG_STR_HLPUSEINDENT),
437 		    strlen(MSG_ORIG(MSG_STR_HLPINDENT))));
438 		/* If there are alias names, show them */
439 		if (cmd->cmd_name[1] != NULL) {
440 			const char **alias = cmd->cmd_name + 1;
441 
442 			elfedit_printf(MSG_INTL(MSG_HLPFMT_ALIASES));
443 			do {
444 				elfedit_printf(
445 				    MSG_ORIG(MSG_STR_HLPINDENT));
446 				elfedit_printf(
447 				    MSG_ORIG(MSG_FMT_MODCMD),
448 				    mod->mod_name, *alias);
449 				if (**alias == '\0')
450 					elfedit_printf(
451 					    MSG_INTL(MSG_HLPFMT_DEFCMD));
452 				elfedit_printf(MSG_ORIG(MSG_STR_NL));
453 				alias++;
454 			} while (*alias);
455 		}
456 		elfedit_printf(MSG_INTL(MSG_HLPFMT_DESC));
457 		write_help_str(
458 		    (* mod->mod_i18nhdl_to_str)(cmd->cmd_help),
459 		    MSG_ORIG(MSG_STR_HLPINDENT));
460 		if (cmd->cmd_args != NULL)
461 			write_optarg(mod, MSG_INTL(MSG_HLPFMT_ARGS),
462 			    cmd->cmd_args);
463 		if (cmd->cmd_opt != NULL)
464 			write_optarg(mod, MSG_INTL(MSG_HLPFMT_OPT),
465 			    cmd->cmd_opt);
466 		if ((dispcnt > 1) && (argc > 0))
467 			elfedit_printf(MSG_INTL(MSG_HLPFMT_MULTIEND),
468 			    argv[0], argv[1]);
469 		/* An empty line after the last line of output */
470 		elfedit_printf(MSG_ORIG(MSG_STR_NL));
471 	}
472 
473 	return (ELFEDIT_CMDRET_NONE);
474 
475 #undef	INITIAL_ITEM_ALLOC
476 }
477 
478 
479 /*
480  * Command completion function for sys:help
481  */
482 /*ARGSUSED*/
483 static void
484 cpl_help(void *obj_state, void *cpldata, int argc, const char *argv[],
485     int num_opt)
486 {
487 	/*
488 	 * The arguments can be any module or command. Supplying the
489 	 * commands implicitly supplies the modules too.
490 	 */
491 	elfedit_cpl_command(cpldata);
492 }
493 
494 
495 /*
496  * Implementation of sys:load
497  */
498 /*ARGSUSED*/
499 static elfedit_cmdret_t
500 cmd_load(void *obj_state, int argc, const char *argv[])
501 {
502 	elfedit_getopt_state_t	getopt_state;
503 	elfedit_getopt_ret_t	*getopt_ret;
504 	struct stat		statbuf;
505 
506 	elfedit_getopt_init(&getopt_state, &argc, &argv);
507 	while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL) {
508 		switch (getopt_ret->gor_idmask) {
509 		case SYS_OPT_F_ALL:
510 			elfedit_load_modpath();
511 			break;
512 		}
513 	}
514 
515 	/* For each remaining argument, load them individually */
516 	for (; argc-- > 0; argv++) {
517 		/* Is it a directory? Load everything in it */
518 		if ((stat(*argv, &statbuf) == 0) &&
519 		    (statbuf.st_mode & S_IFDIR)) {
520 			elfedit_load_moddir(*argv, 1, 1);
521 		} else {	/* Not a directory. Normal load */
522 			(void) elfedit_load_module(*argv, 1, 1);
523 		}
524 	}
525 
526 	return (0);
527 }
528 
529 
530 /*
531  * Command completion function for sys:load
532  */
533 /*ARGSUSED*/
534 static void
535 cpl_load(void *obj_state, void *cpldata, int argc, const char *argv[],
536     int num_opt)
537 {
538 	/*
539 	 * Module names. Note that this causes elfedit to load all
540 	 * of the modules, which probably makes the current load
541 	 * operation unnecessary. This could be improved, but I don't
542 	 * see it as worth the complexity. Explicit load calls are
543 	 * rare, and the user will usually not use command completion.
544 	 */
545 	elfedit_cpl_module(cpldata, 1);
546 }
547 
548 
549 /*
550  * Implementation of sys:quit
551  */
552 /*ARGSUSED*/
553 static elfedit_cmdret_t
554 cmd_quit(void *obj_state, int argc, const char *argv[])
555 {
556 	elfedit_getopt_state_t	getopt_state;
557 	elfedit_getopt_ret_t	*getopt_ret;
558 	int			force = 0;
559 	const char		*file;
560 	int			fd;
561 	Elf			*elf;
562 
563 	elfedit_getopt_init(&getopt_state, &argc, &argv);
564 	while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL) {
565 		switch (getopt_ret->gor_idmask) {
566 		case SYS_OPT_F_FORCE:
567 			force = 1;
568 			break;
569 		}
570 	}
571 	if (argc != 0)
572 		elfedit_command_usage();
573 
574 	if (state.file.present) {
575 		/*
576 		 * If session is not READONLY, then refuse to quit if file
577 		 * needs flushing and -f option was not used.
578 		 */
579 		if (!(state.flags & ELFEDIT_F_READONLY) && state.file.dirty &&
580 		    !force)
581 			elfedit_msg(ELFEDIT_MSG_ERR,
582 			    MSG_INTL(MSG_ERR_NODIRTYQUIT));
583 
584 		get_obj_state_info(obj_state, &file, &fd, &elf);
585 		(void) close(fd);
586 		(void) elf_end(elf);
587 		free(obj_state);
588 	}
589 
590 	elfedit_exit(0);
591 	/*NOTREACHED*/
592 	return (0);
593 }
594 
595 
596 /*
597  * Implementation of sys:status
598  */
599 /*ARGSUSED*/
600 static elfedit_cmdret_t
601 cmd_status(void *obj_state, int argc, const char *argv[])
602 {
603 	MODLIST_T	*modlist;
604 	const char	*s;
605 	size_t		i;
606 
607 	if (argc > 0)
608 		elfedit_command_usage();
609 
610 	/*
611 	 * This command can produce an arbitrary amount of output, so
612 	 * run a pager.
613 	 */
614 	elfedit_pager_init();
615 
616 	/* Files */
617 	if (state.file.present == 0) {
618 		elfedit_printf(MSG_INTL(MSG_HLPFMT_INFILENONE));
619 	} else if (state.flags & ELFEDIT_F_READONLY) {
620 		elfedit_printf(MSG_INTL(MSG_HLPFMT_INFILERO),
621 		    state.file.infile);
622 	} else {
623 		elfedit_printf(MSG_INTL(MSG_HLPFMT_INFILE), state.file.infile);
624 		elfedit_printf(MSG_INTL(MSG_HLPFMT_OUTFILE),
625 		    state.file.outfile);
626 	}
627 	if (state.file.dirty)
628 		elfedit_printf(MSG_INTL(MSG_HLPFMT_CNGPENDING));
629 
630 	/* Option Variables */
631 	elfedit_printf(MSG_INTL(MSG_HLPFMT_VARHDR));
632 	elfedit_printf(MSG_INTL(MSG_HLPFMT_AFLG),
633 	    (state.flags & ELFEDIT_F_AUTOPRINT) ? MSG_ORIG(MSG_STR_ON) :
634 	    MSG_ORIG(MSG_STR_OFF));
635 	elfedit_printf(MSG_INTL(MSG_HLPFMT_DFLG),
636 	    (state.flags & ELFEDIT_F_DEBUG) ? MSG_ORIG(MSG_STR_ON) :
637 	    MSG_ORIG(MSG_STR_OFF));
638 	elfedit_printf(MSG_INTL(MSG_HLPFMT_OFLG),
639 	    elfedit_atoconst_value_to_str(ELFEDIT_CONST_OUTSTYLE,
640 	    state.outstyle, 1));
641 
642 	/* Module Load Path */
643 	elfedit_printf(MSG_INTL(MSG_HLPFMT_PATHHDR));
644 	for (i = 0; i < state.modpath.n; i++)
645 		elfedit_printf(MSG_ORIG(MSG_HLPFMT_PATHELT),
646 		    state.modpath.seg[i]);
647 
648 	/* Currently Loaded Modules */
649 	elfedit_printf(MSG_INTL(MSG_HLPFMT_MODHDR));
650 	for (modlist = state.modlist; modlist;
651 	    modlist = modlist->ml_next) {
652 		s = modlist->ml_path ? modlist->ml_path :
653 		    MSG_INTL(MSG_FMT_BUILTIN);
654 		elfedit_printf(MSG_ORIG(MSG_HLPFMT_NAMDSCCOL),
655 		    modlist->ml_mod->mod_name, s);
656 	}
657 
658 	return (ELFEDIT_CMDRET_NONE);
659 }
660 
661 /*
662  * Implementation of sys:set
663  */
664 /*ARGSUSED*/
665 static elfedit_cmdret_t
666 cmd_set(void *obj_state, int argc, const char *argv[])
667 {
668 	if ((argc != 2) || (strlen(argv[0]) > 1))
669 		elfedit_command_usage();
670 
671 	switch (**argv) {
672 	case 'a':
673 	case 'A':
674 		if (elfedit_atobool(argv[1], MSG_INTL(MSG_SYSSET_A)))
675 			state.flags |= ELFEDIT_F_AUTOPRINT;
676 		else
677 			state.flags &= ~ELFEDIT_F_AUTOPRINT;
678 		break;
679 
680 	case 'd':
681 	case 'D':
682 		if (elfedit_atobool(argv[1], MSG_INTL(MSG_SYSSET_D)))
683 			state.flags |= ELFEDIT_F_DEBUG;
684 		else
685 			state.flags &= ~ELFEDIT_F_DEBUG;
686 		break;
687 
688 	case 'o':
689 	case 'O':
690 		if (elfedit_atooutstyle(argv[1], &state.outstyle) == 0)
691 			elfedit_msg(ELFEDIT_MSG_ERR,
692 			    MSG_INTL(MSG_ERR_BADOSTYLE), argv[1]);
693 		break;
694 
695 	default:
696 		elfedit_command_usage();
697 	}
698 
699 	return (0);
700 }
701 
702 
703 /*
704  * Command completion function for sys:set
705  */
706 /*ARGSUSED*/
707 static void
708 cpl_set(void *obj_state, void *cpldata, int argc, const char *argv[],
709     int num_opt)
710 {
711 	const char *s;
712 
713 	/*
714 	 * This command doesn't accept options, so num_opt should be
715 	 * 0. This is a defensive measure, in case that should change.
716 	 */
717 	argc -= num_opt;
718 	argv += num_opt;
719 
720 	if ((argc < 1) || (argc > 2))
721 		return;
722 
723 	if (argc == 1) {	/* The first argument is a variable letter */
724 		elfedit_cpl_match(cpldata, MSG_ORIG(MSG_STR_A), 1);
725 		elfedit_cpl_match(cpldata, MSG_ORIG(MSG_STR_D), 1);
726 		elfedit_cpl_match(cpldata, MSG_ORIG(MSG_STR_O), 1);
727 		elfedit_cpl_match(cpldata, MSG_ORIG(MSG_STR_W), 1);
728 		return;
729 	}
730 
731 	/* We're dealing with the second argument, the value */
732 	s = argv[0];
733 	if (strlen(s) > 1)	/* One letter variables */
734 		return;
735 	switch (*s) {
736 	case 'a':		/* Booleans */
737 	case 'A':
738 	case 'd':
739 	case 'D':
740 	case 'w':
741 	case 'W':
742 		/* The second argument is a boolean */
743 		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_BOOL);
744 
745 		/* The numbers are not symbolic, but we want them in the list */
746 		elfedit_cpl_match(cpldata, MSG_ORIG(MSG_STR_0), 1);
747 		elfedit_cpl_match(cpldata, MSG_ORIG(MSG_STR_1), 1);
748 		break;
749 
750 	case 'o':		/* Output style */
751 	case 'O':
752 		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_OUTSTYLE);
753 		break;
754 	}
755 }
756 
757 
758 /*
759  * Implementation of sys:unload
760  */
761 /*ARGSUSED*/
762 static elfedit_cmdret_t
763 cmd_unload(void *obj_state, int argc, const char *argv[])
764 {
765 	elfedit_getopt_state_t	getopt_state;
766 	elfedit_getopt_ret_t	*getopt_ret;
767 	MODLIST_T		*moddef;
768 	int			do_all = 0;
769 
770 	elfedit_getopt_init(&getopt_state, &argc, &argv);
771 	while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL) {
772 		switch (getopt_ret->gor_idmask) {
773 		case SYS_OPT_F_ALL:
774 			do_all = 1;
775 			break;
776 		}
777 	}
778 
779 	/*
780 	 * If -a is specified, unload everything except builtins. Don't
781 	 * allow plain arguments in this case because there is nothing
782 	 * left to unload after -a.
783 	 */
784 	if (do_all) {
785 		if (argc > 0)
786 			elfedit_command_usage();
787 		/*
788 		 * Until we run out of non-builtin modules, take the first
789 		 * one from the list and unload it. Each removal alters
790 		 * the list, so we always start at the beginning, but this
791 		 * is efficient since we always remove the first available item
792 		 */
793 		while (state.modlist != NULL) {
794 			for (moddef = state.modlist; moddef != NULL;
795 			    moddef = moddef->ml_next)
796 				if (moddef->ml_dl_hdl != NULL) break;
797 
798 			/* If we made it to the end, then the list is empty */
799 			if (moddef == NULL)
800 				break;
801 
802 			elfedit_unload_module(moddef->ml_mod->mod_name);
803 		}
804 		return (0);
805 	}
806 
807 	/* Unload each module individually */
808 	for (; argc-- > 0; argv++)
809 		elfedit_unload_module(*argv);
810 
811 	return (0);
812 }
813 
814 
815 /*
816  * Command completion function for sys:unload
817  */
818 /*ARGSUSED*/
819 static void
820 cpl_unload(void *obj_state, void *cpldata, int argc, const char *argv[],
821     int num_opt)
822 {
823 	/*
824 	 * Module names. Don't allow elfedit to load all the modules,
825 	 * as the only modules we want to unload are those already
826 	 * in memory.
827 	 */
828 	elfedit_cpl_module(cpldata, 0);
829 }
830 
831 
832 /*
833  * Implementation of sys:write
834  */
835 /*ARGSUSED2*/
836 static elfedit_cmdret_t
837 cmd_write(void *obj_state, int argc, const char *argv[])
838 {
839 	const char	*file;
840 	int		fd;
841 	Elf		*elf;
842 
843 	if (argc != 0)
844 		elfedit_command_usage();
845 
846 	if (state.file.present != 0) {
847 		if (state.flags & ELFEDIT_F_READONLY)
848 			elfedit_msg(ELFEDIT_MSG_ERR,
849 			    MSG_INTL(MSG_ERR_READONLY));
850 
851 		get_obj_state_info(obj_state, &file, &fd, &elf);
852 		if (elf_update(elf, ELF_C_WRITE) == -1)
853 			elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_LIBELF),
854 			    file, MSG_ORIG(MSG_ELF_UPDATE),
855 			    elf_errmsg(elf_errno()));
856 
857 		/*
858 		 * An update has succeeded for this file, so revoke the need
859 		 * to unlink it on exit.
860 		 */
861 		state.file.unlink_on_exit = 0;
862 	}
863 
864 	return (ELFEDIT_CMDRET_FLUSH);
865 }
866 
867 
868 
869 
870 
871 /*ARGSUSED*/
872 MODLIST_T *
873 elfedit_sys_init(elfedit_module_version_t version)
874 {
875 	/* sys:help */
876 	static const char *name_help[] = { MSG_ORIG(MSG_SYS_CMD_HELP),
877 	    MSG_ORIG(MSG_SYS_CMD_HELP_A1), MSG_ORIG(MSG_SYS_CMD_HELP_A2),
878 	    NULL };
879 	static elfedit_cmd_optarg_t opt_help[] = {
880 		{ MSG_ORIG(MSG_STR_MINUS_S),
881 		    /* MSG_INTL(MSG_SYS_OPTDESC_HELP_S) */
882 		    ELFEDIT_I18NHDL(MSG_SYS_OPTDESC_HELP_S), 0,
883 		    SYS_OPT_F_SYNOPSIS, 0 },
884 		{ NULL }
885 	};
886 	static elfedit_cmd_optarg_t arg_help[] = {
887 		{ MSG_ORIG(MSG_STR_ARG),
888 		    /* MSG_INTL(MSG_ARGDESC_HELP_ARG) */
889 		    ELFEDIT_I18NHDL(MSG_ARGDESC_HELP_ARG),
890 		    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
891 		{ NULL }
892 	};
893 
894 	/* sys:load */
895 	static const char *name_load[] = {
896 	    MSG_ORIG(MSG_SYS_CMD_LOAD), NULL };
897 	static elfedit_cmd_optarg_t opt_load[] = {
898 		{ MSG_ORIG(MSG_STR_MINUS_A),
899 		    /* MSG_INTL(MSG_SYS_OPTDESC_LOAD_A) */
900 		    ELFEDIT_I18NHDL(MSG_SYS_OPTDESC_LOAD_A), 0,
901 		    SYS_OPT_F_ALL, 0 },
902 		{ NULL }
903 	};
904 	static elfedit_cmd_optarg_t arg_load[] = {
905 		{ MSG_ORIG(MSG_STR_MODNAME),
906 		    /* MSG_INTL(MSG_ARGDESC_LOAD_MODNAME) */
907 		    ELFEDIT_I18NHDL(MSG_ARGDESC_LOAD_MODNAME),
908 		    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
909 		{ NULL }
910 	};
911 
912 	/* sys:quit */
913 	static const char *name_quit[] = { MSG_ORIG(MSG_SYS_CMD_QUIT),
914 	    MSG_ORIG(MSG_SYS_CMD_QUIT_A1), MSG_ORIG(MSG_SYS_CMD_QUIT_A2),
915 	    NULL };
916 	static elfedit_cmd_optarg_t opt_quit[] = {
917 		{ MSG_ORIG(MSG_STR_MINUS_F),
918 		    /* MSG_INTL(MSG_SYS_OPTDESC_QUIT_F) */
919 		    ELFEDIT_I18NHDL(MSG_SYS_OPTDESC_QUIT_F), 0,
920 		    SYS_OPT_F_FORCE, 0 },
921 		{ NULL }
922 	};
923 
924 	/* sys:status */
925 	static const char *name_status[] = {
926 	    MSG_ORIG(MSG_SYS_CMD_STATUS), NULL };
927 
928 	/* sys:set */
929 	static const char *name_set[] = {
930 	    MSG_ORIG(MSG_SYS_CMD_SET), NULL };
931 	static elfedit_cmd_optarg_t arg_set[] = {
932 		{ MSG_ORIG(MSG_STR_OPTION),
933 		    /* MSG_INTL(MSG_ARGDESC_SET_OPTION) */
934 		    ELFEDIT_I18NHDL(MSG_ARGDESC_SET_OPTION), 0 },
935 		{ MSG_ORIG(MSG_STR_VALUE),
936 		    /* MSG_INTL(MSG_ARGDESC_SET_VALUE) */
937 		    ELFEDIT_I18NHDL(MSG_ARGDESC_SET_VALUE), 0 },
938 		{ NULL }
939 	};
940 
941 	/* sys:unload */
942 	static const char *name_unload[] = {
943 	    MSG_ORIG(MSG_SYS_CMD_UNLOAD), NULL };
944 	static elfedit_cmd_optarg_t opt_unload[] = {
945 		{ MSG_ORIG(MSG_STR_MINUS_A),
946 		    /* MSG_INTL(MSG_SYS_OPTDESC_UNLOAD_A) */
947 		    ELFEDIT_I18NHDL(MSG_SYS_OPTDESC_UNLOAD_A), 0,
948 		    SYS_OPT_F_ALL, 0},
949 		{ NULL }
950 	};
951 	static elfedit_cmd_optarg_t arg_unload[] = {
952 		{ MSG_ORIG(MSG_STR_MODNAME),
953 		    /* MSG_INTL(MSG_ARGDESC_UNLOAD_MODNAME) */
954 		    ELFEDIT_I18NHDL(MSG_ARGDESC_UNLOAD_MODNAME),
955 		    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
956 		{ NULL }
957 	};
958 
959 	/* sys:write */
960 	static const char *name_write[] = { MSG_ORIG(MSG_SYS_CMD_WRITE),
961 	    MSG_ORIG(MSG_SYS_CMD_WRITE_A1), MSG_ORIG(MSG_SYS_CMD_WRITE_A2),
962 	    NULL };
963 
964 	static elfedit_cmd_t cmds[] = {
965 		/* sym:help */
966 		{ (elfedit_cmd_func_t *)cmd_help,
967 		    (elfedit_cmdcpl_func_t *)cpl_help, name_help,
968 		    /* MSG_INTL(MSG_SYS_DESC_HELP) */
969 		    ELFEDIT_I18NHDL(MSG_SYS_DESC_HELP),
970 		    /* MSG_INTL(MSG_SYS_HELP_HELP) */
971 		    ELFEDIT_I18NHDL(MSG_SYS_HELP_HELP),
972 		    opt_help, arg_help },
973 
974 		/* sym:load */
975 		{ (elfedit_cmd_func_t *)cmd_load,
976 		    (elfedit_cmdcpl_func_t *)cpl_load, name_load,
977 		    /* MSG_INTL(MSG_SYS_DESC_LOAD) */
978 		    ELFEDIT_I18NHDL(MSG_SYS_DESC_LOAD),
979 		    /* MSG_INTL(MSG_SYS_HELP_LOAD) */
980 		    ELFEDIT_I18NHDL(MSG_SYS_HELP_LOAD),
981 		    opt_load, arg_load },
982 
983 		/* sym:quit */
984 		{ (elfedit_cmd_func_t *)cmd_quit, NULL, name_quit,
985 		    /* MSG_INTL(MSG_SYS_DESC_QUIT) */
986 		    ELFEDIT_I18NHDL(MSG_SYS_DESC_QUIT),
987 		    /* MSG_INTL(MSG_SYS_HELP_QUIT) */
988 		    ELFEDIT_I18NHDL(MSG_SYS_HELP_QUIT),
989 		    opt_quit, NULL },
990 
991 		/* sym:status */
992 		{ (elfedit_cmd_func_t *)cmd_status, NULL, name_status,
993 		    /* MSG_INTL(MSG_SYS_DESC_STATUS) */
994 		    ELFEDIT_I18NHDL(MSG_SYS_DESC_STATUS),
995 		    /* MSG_INTL(MSG_SYS_HELP_STATUS) */
996 		    ELFEDIT_I18NHDL(MSG_SYS_HELP_STATUS),
997 		    NULL, NULL },
998 
999 		/* sym:set */
1000 		{ (elfedit_cmd_func_t *)cmd_set,
1001 		    (elfedit_cmdcpl_func_t *)cpl_set, name_set,
1002 		    /* MSG_INTL(MSG_SYS_DESC_SET) */
1003 		    ELFEDIT_I18NHDL(MSG_SYS_DESC_SET),
1004 		    /* MSG_INTL(MSG_SYS_HELP_SET) */
1005 		    ELFEDIT_I18NHDL(MSG_SYS_HELP_SET),
1006 		    NULL, arg_set },
1007 
1008 		/* sym:unload */
1009 		{ (elfedit_cmd_func_t *)cmd_unload,
1010 		    (elfedit_cmdcpl_func_t *)cpl_unload, name_unload,
1011 		    /* MSG_INTL(MSG_SYS_DESC_UNLOAD) */
1012 		    ELFEDIT_I18NHDL(MSG_SYS_DESC_UNLOAD),
1013 		    /* MSG_INTL(MSG_SYS_HELP_UNLOAD) */
1014 		    ELFEDIT_I18NHDL(MSG_SYS_HELP_UNLOAD),
1015 		    opt_unload, arg_unload },
1016 
1017 		/* sym:write */
1018 		{ (elfedit_cmd_func_t *)cmd_write, NULL, name_write,
1019 		    /* MSG_INTL(MSG_SYS_DESC_WRITE) */
1020 		    ELFEDIT_I18NHDL(MSG_SYS_DESC_WRITE),
1021 		    /* MSG_INTL(MSG_SYS_HELP_WRITE) */
1022 		    ELFEDIT_I18NHDL(MSG_SYS_HELP_WRITE),
1023 		    NULL, NULL},
1024 
1025 		{ NULL }
1026 	};
1027 
1028 	static elfedit_module_t module = {
1029 	    ELFEDIT_VER_CURRENT, MSG_ORIG(MSG_MOD_SYS),
1030 	    /* MSG_INTL(MSG_MOD_SYS_DESC) */
1031 	    ELFEDIT_I18NHDL(MSG_MOD_SYS_DESC),
1032 	    cmds, mod_i18nhdl_to_str };
1033 
1034 	static MODLIST_T moddef = {
1035 		NULL,		/* next */
1036 		(elfeditGC_module_t *)&module,	/* Module definition */
1037 		NULL,		/* Didn't dlopen() it, so NULL handle */
1038 		NULL		/* Didn't dlopen() it, so no file path */
1039 	};
1040 
1041 	return (&moddef);
1042 }
1043