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 */
68static const char *
69mod_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 */
82typedef 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 */
93static void
94get_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 */
116static void
117cmd_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 */
159static void
160cmd_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 */
180static void
181write_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 */
202static void
203write_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*/
249static elfedit_cmdret_t
250cmd_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*/
483static void
484cpl_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*/
499static elfedit_cmdret_t
500cmd_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*/
534static void
535cpl_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*/
553static elfedit_cmdret_t
554cmd_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*/
600static elfedit_cmdret_t
601cmd_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*/
665static elfedit_cmdret_t
666cmd_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*/
707static void
708cpl_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*/
762static elfedit_cmdret_t
763cmd_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*/
819static void
820cpl_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*/
836static elfedit_cmdret_t
837cmd_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*/
872MODLIST_T *
873elfedit_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