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