Home | History | Annotate | Download | only in common
      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 #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 <sys/machelf.h>, as we do for the loadable
     49  *		modules. 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