svccfg_engine.c revision 11996:91b62f7b8186
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27
28/*
29 * svccfg(1) interpreter and command execution engine.
30 */
31
32#include <sys/mman.h>
33#include <sys/stat.h>
34#include <sys/types.h>
35#include <assert.h>
36#include <errno.h>
37#include <fcntl.h>
38#include <libintl.h>
39#include <libtecla.h>
40#include <md5.h>
41#include <string.h>
42#include <stdlib.h>
43#include <unistd.h>
44
45#include "manifest_find.h"
46#include "manifest_hash.h"
47#include "svccfg.h"
48
49#define	MS_PER_US		1000
50
51engine_state_t *est;
52
53/*
54 * Replacement lex(1) character retrieval routines.
55 */
56int
57engine_cmd_getc(engine_state_t *E)
58{
59	if (E->sc_cmd_file != NULL)
60		return (getc(E->sc_cmd_file));
61
62	if (E->sc_cmd_flags & SC_CMD_EOF)
63		return (EOF);
64
65	if (E->sc_cmd_bufoff < E->sc_cmd_bufsz)
66		return (*(E->sc_cmd_buf + E->sc_cmd_bufoff++));
67
68	if (!(E->sc_cmd_flags & SC_CMD_IACTIVE)) {
69		E->sc_cmd_flags |= SC_CMD_EOF;
70
71		return (EOF);
72	} else {
73#ifdef NATIVE_BUILD
74		return (EOF);
75#else
76		extern int parens;
77
78		if (parens <= 0) {
79			E->sc_cmd_flags |= SC_CMD_EOF;
80			return (EOF);
81		}
82
83		for (;;) {
84			E->sc_cmd_buf = gl_get_line(E->sc_gl, "> ", NULL, -1);
85			if (E->sc_cmd_buf != NULL)
86				break;
87
88			switch (gl_return_status(E->sc_gl)) {
89			case GLR_SIGNAL:
90				gl_abandon_line(E->sc_gl);
91				continue;
92
93			case GLR_EOF:
94				E->sc_cmd_flags |= SC_CMD_EOF;
95				return (EOF);
96
97			case GLR_ERROR:
98				uu_die(gettext("Error reading terminal: %s.\n"),
99				    gl_error_message(E->sc_gl, NULL, 0));
100				/* NOTREACHED */
101
102			default:
103#ifndef NDEBUG
104				(void) fprintf(stderr, "%s:%d: gl_get_line() "
105				    "returned unexpected value %d.\n", __FILE__,
106				    __LINE__, gl_return_status(E->sc_gl));
107#endif
108				abort();
109			}
110		}
111
112		E->sc_cmd_bufsz = strlen(E->sc_cmd_buf);
113		E->sc_cmd_bufoff = 1;
114
115		return (E->sc_cmd_buf[0]);
116#endif	/* NATIVE_BUILD */
117	}
118}
119
120int
121engine_cmd_ungetc(engine_state_t *E, char c)
122{
123	if (E->sc_cmd_file != NULL)
124		return (ungetc(c, E->sc_cmd_file));
125
126	if (E->sc_cmd_buf != NULL)
127		*(E->sc_cmd_buf + --E->sc_cmd_bufoff) = c;
128
129	return (c);
130}
131
132/*ARGSUSED*/
133void
134engine_cmd_nputs(engine_state_t *E, char *c, size_t n)
135{
136	/* our lexer shouldn't need this state */
137	exit(11);
138}
139
140int
141engine_exec(char *cmd)
142{
143	est->sc_cmd_buf = cmd;
144	est->sc_cmd_bufsz = strlen(cmd) + 1;
145	est->sc_cmd_bufoff = 0;
146
147	(void) yyparse();
148
149	return (0);
150}
151
152#ifndef NATIVE_BUILD
153/* ARGSUSED */
154static
155CPL_CHECK_FN(check_xml)
156{
157	const char *ext;
158
159	if (strlen(pathname) < 4)
160		return (0);
161
162	ext = pathname + strlen(pathname) - 4;
163
164	return (strcmp(ext, ".xml") == 0 ? 1 : 0);
165}
166
167static const char * const whitespace = " \t";
168
169static
170CPL_MATCH_FN(complete_single_xml_file_arg)
171{
172	const char *arg1 = data;
173	int arg1end_i, ret;
174	CplFileConf *cfc;
175
176	arg1end_i = arg1 + strcspn(arg1, whitespace) - line;
177	if (arg1end_i < word_end)
178		return (0);
179
180	cfc = new_CplFileConf();
181	if (cfc == NULL) {
182		cpl_record_error(cpl, "Out of memory.");
183		return (1);
184	}
185
186	cfc_set_check_fn(cfc, check_xml, NULL);
187
188	ret = cpl_file_completions(cpl, cfc, line, word_end);
189
190	(void) del_CplFileConf(cfc);
191	return (ret);
192}
193
194static struct cmd_info {
195	const char	*name;
196	uint32_t	flags;
197	CplMatchFn	*complete_args_f;
198} cmds[] = {
199	{ "validate", CS_GLOBAL, complete_single_xml_file_arg },
200	{ "import", CS_GLOBAL, complete_single_xml_file_arg },
201	{ "cleanup", CS_GLOBAL, NULL},
202	{ "export", CS_GLOBAL, NULL },
203	{ "archive", CS_GLOBAL, NULL },
204	{ "apply", CS_GLOBAL, complete_single_xml_file_arg },
205	{ "extract", CS_GLOBAL, NULL },
206	{ "repository", CS_GLOBAL, NULL },
207	{ "inventory", CS_GLOBAL, complete_single_xml_file_arg },
208	{ "set", CS_GLOBAL, NULL },
209	{ "end", CS_GLOBAL, NULL },
210	{ "exit", CS_GLOBAL, NULL },
211	{ "quit", CS_GLOBAL, NULL },
212	{ "help", CS_GLOBAL, NULL },
213	{ "delete", CS_GLOBAL, NULL },
214	{ "select", CS_GLOBAL, complete_select },
215	{ "unselect", CS_SVC | CS_INST | CS_SNAP, NULL },
216	{ "list", CS_SCOPE | CS_SVC | CS_SNAP, NULL },
217	{ "add", CS_SCOPE | CS_SVC, NULL },
218	{ "listpg", CS_SVC | CS_INST | CS_SNAP, NULL },
219	{ "addpg", CS_SVC | CS_INST, NULL },
220	{ "delpg", CS_SVC | CS_INST, NULL },
221	{ "delhash", CS_GLOBAL, complete_single_xml_file_arg },
222	{ "listprop", CS_SVC | CS_INST | CS_SNAP, NULL },
223	{ "setprop", CS_SVC | CS_INST, NULL },
224	{ "delprop", CS_SVC | CS_INST, NULL },
225	{ "editprop", CS_SVC | CS_INST, NULL },
226	{ "describe", CS_SVC | CS_INST | CS_SNAP, NULL },
227	{ "listsnap", CS_INST | CS_SNAP, NULL },
228	{ "selectsnap", CS_INST | CS_SNAP, NULL },
229	{ "revert", CS_INST | CS_SNAP, NULL },
230	{ "refresh", CS_INST, NULL },
231	{ NULL }
232};
233
234int
235add_cmd_matches(WordCompletion *cpl, const char *line, int word_end,
236    uint32_t scope)
237{
238	int word_start, err;
239	size_t len;
240	const char *bol;
241	struct cmd_info *cip;
242
243	word_start = strspn(line, whitespace);
244	len = word_end - word_start;
245	bol = line + word_end - len;
246
247	for (cip = cmds; cip->name != NULL; ++cip) {
248		if ((cip->flags & scope) == 0)
249			continue;
250
251		if (strncmp(cip->name, bol, len) == 0) {
252			err = cpl_add_completion(cpl, line, word_start,
253			    word_end, cip->name + len, "", " ");
254			if (err != 0)
255				return (err);
256		}
257	}
258
259	return (0);
260}
261
262/*
263 * Suggest completions.  We must first determine if the cursor is in command
264 * position or in argument position.  If the former, complete_command() finds
265 * matching commands.  If the latter, we tail-call the command-specific
266 * argument-completion routine in the cmds table.
267 */
268/* ARGSUSED */
269static
270CPL_MATCH_FN(complete)
271{
272	const char *arg0, *arg1;
273	size_t arg0len;
274	struct cmd_info *cip;
275
276	arg0 = line + strspn(line, whitespace);
277	arg0len = strcspn(arg0, whitespace);
278	if ((arg0 + arg0len) - line >= word_end ||
279	    (arg0[arg0len] != ' ' && arg0[arg0len] != '\t'))
280		return (complete_command(cpl, (void *)arg0, line, word_end));
281
282	arg1 = arg0 + arg0len;
283	arg1 += strspn(arg1, whitespace);
284
285	for (cip = cmds; cip->name != NULL; ++cip) {
286		if (strlen(cip->name) != arg0len)
287			continue;
288
289		if (strncmp(cip->name, arg0, arg0len) != 0)
290			continue;
291
292		if (cip->complete_args_f == NULL)
293			break;
294
295		return (cip->complete_args_f(cpl, (void *)arg1, line,
296		    word_end));
297	}
298
299	return (0);
300}
301#endif	/* NATIVE_BUILD */
302
303int
304engine_interp()
305{
306#ifdef NATIVE_BUILD
307	uu_die("native build does not support interactive mode.");
308#else
309	char *selfmri;
310	size_t sfsz;
311	int r;
312
313	extern int parens;
314
315	(void) sigset(SIGINT, SIG_IGN);
316
317	est->sc_gl = new_GetLine(512, 8000);
318	if (est->sc_gl == NULL)
319		uu_die(gettext("Out of memory.\n"));
320
321	/* The longest string is "[snapname]fmri[:instname]> ". */
322	sfsz = 1 + max_scf_name_len + 1 + max_scf_fmri_len + 2 +
323	    max_scf_name_len + 1 + 2 + 1;
324	selfmri = safe_malloc(sfsz);
325
326	r = gl_customize_completion(est->sc_gl, NULL, complete);
327	assert(r == 0);
328
329	for (;;) {
330		lscf_get_selection_str(selfmri, sfsz - 2);
331		(void) strcat(selfmri, "> ");
332		est->sc_cmd_buf = gl_get_line(est->sc_gl, selfmri, NULL, -1);
333
334		if (est->sc_cmd_buf == NULL) {
335			switch (gl_return_status(est->sc_gl)) {
336			case GLR_SIGNAL:
337				gl_abandon_line(est->sc_gl);
338				continue;
339
340			case GLR_EOF:
341				break;
342
343			case GLR_ERROR:
344				uu_die(gettext("Error reading terminal: %s.\n"),
345				    gl_error_message(est->sc_gl, NULL, 0));
346				/* NOTREACHED */
347
348			default:
349#ifndef NDEBUG
350				(void) fprintf(stderr, "%s:%d: gl_get_line() "
351				    "returned unexpected value %d.\n", __FILE__,
352				    __LINE__, gl_return_status(est->sc_gl));
353#endif
354				abort();
355			}
356
357			break;
358		}
359
360		parens = 0;
361		est->sc_cmd_bufsz = strlen(est->sc_cmd_buf);
362		est->sc_cmd_bufoff = 0;
363		est->sc_cmd_flags = SC_CMD_IACTIVE;
364
365		(void) yyparse();
366	}
367
368	free(selfmri);
369	est->sc_gl = del_GetLine(est->sc_gl);	/* returns NULL */
370
371#endif	/* NATIVE_BUILD */
372	return (0);
373}
374
375int
376engine_source(const char *name, boolean_t dont_exit)
377{
378	engine_state_t *old = est;
379	struct stat st;
380	int ret;
381
382	est = uu_zalloc(sizeof (engine_state_t));
383
384	/* first, copy the stuff set up in engine_init */
385	est->sc_repo_pid = old->sc_repo_pid;
386	if (old->sc_repo_filename != NULL)
387		est->sc_repo_filename = safe_strdup(old->sc_repo_filename);
388	if (old->sc_repo_doordir != NULL)
389		est->sc_repo_doordir = safe_strdup(old->sc_repo_doordir);
390	if (old->sc_repo_doorname != NULL)
391		est->sc_repo_doorname = safe_strdup(old->sc_repo_doorname);
392	if (old->sc_repo_server != NULL)
393		est->sc_repo_server = safe_strdup(old->sc_repo_server);
394
395	/* set up the new guy */
396	est->sc_cmd_lineno = 1;
397
398	if (dont_exit)
399		est->sc_cmd_flags |= SC_CMD_DONT_EXIT;
400
401	if (strcmp(name, "-") == 0) {
402		est->sc_cmd_file = stdin;
403		est->sc_cmd_filename = "<stdin>";
404	} else {
405		errno = 0;
406		est->sc_cmd_filename = name;
407		est->sc_cmd_file = fopen(name, "r");
408		if (est->sc_cmd_file == NULL) {
409			if (errno == 0)
410				semerr(gettext("No free stdio streams.\n"));
411			else
412				semerr(gettext("Could not open %s"), name);
413
414			ret = -1;
415			goto fail;
416		}
417
418		do {
419			ret = fstat(fileno(est->sc_cmd_file), &st);
420		} while (ret != 0 && errno == EINTR);
421		if (ret != 0) {
422			(void) fclose(est->sc_cmd_file);
423			est->sc_cmd_file = NULL;	/* for semerr() */
424
425			semerr(gettext("Could not stat %s"), name);
426
427			ret = -1;
428			goto fail;
429		}
430
431		if (!S_ISREG(st.st_mode)) {
432			(void) fclose(est->sc_cmd_file);
433			est->sc_cmd_file = NULL;	/* for semerr() */
434
435			semerr(gettext("%s is not a regular file.\n"), name);
436
437			ret = -1;
438			goto fail;
439		}
440	}
441
442	(void) yyparse();
443
444	if (est->sc_cmd_file != stdin)
445		(void) fclose(est->sc_cmd_file);
446
447	ret = 0;
448
449fail:
450	if (est->sc_repo_pid != old->sc_repo_pid)
451		lscf_cleanup();		/* clean up any new repository */
452
453	if (est->sc_repo_filename != NULL)
454		free((void *)est->sc_repo_filename);
455	if (est->sc_repo_doordir != NULL)
456		free((void *)est->sc_repo_doordir);
457	if (est->sc_repo_doorname != NULL)
458		free((void *)est->sc_repo_doorname);
459	if (est->sc_repo_server != NULL)
460		free((void *)est->sc_repo_server);
461	free(est);
462
463	est = old;
464
465	return (ret);
466}
467
468/*
469 * Initialize svccfg state.  We recognize four environment variables:
470 *
471 * SVCCFG_REPOSITORY	Create a private instance of svc.configd(1M) to answer
472 *			requests for the specified repository file.
473 * SVCCFG_DOOR_PATH	Directory for door creation.
474 *
475 * SVCCFG_DOOR		Rendezvous via an alternative repository door.
476 *
477 * SVCCFG_CONFIGD_PATH	Resolvable path to alternative svc.configd(1M) binary.
478 */
479void
480engine_init()
481{
482	const char *cp;
483
484	est = uu_zalloc(sizeof (engine_state_t));
485
486	est->sc_cmd_lineno = 1;
487	est->sc_repo_pid = -1;
488
489	cp = getenv("SVCCFG_REPOSITORY");
490	est->sc_repo_filename = cp ? safe_strdup(cp) : NULL;
491
492	cp = getenv("SVCCFG_DOOR_PATH");
493	est->sc_repo_doordir = cp ? cp : "/var/run";
494
495	cp = getenv("SVCCFG_DOOR");
496	if (cp != NULL) {
497		if (est->sc_repo_filename != NULL) {
498			uu_warn(gettext("SVCCFG_DOOR unused when "
499			    "SVCCFG_REPOSITORY specified\n"));
500		} else {
501			est->sc_repo_doorname = safe_strdup(cp);
502		}
503	}
504
505	cp = getenv("SVCCFG_CONFIGD_PATH");
506	est->sc_repo_server = cp ? cp : "/lib/svc/bin/svc.configd";
507
508	est->sc_in_emi = 0;
509	cp = getenv("SMF_FMRI");
510	if ((cp != NULL) && (strcmp(cp, SCF_INSTANCE_EMI) == 0))
511		est->sc_in_emi = 1;
512
513	cp = smf_get_state(SCF_INSTANCE_FS_MINIMAL);
514	if (cp && (strcmp(cp, SCF_STATE_STRING_ONLINE) == 0))
515		est->sc_fs_minimal = B_TRUE;
516	free((void *) cp);
517}
518
519static int
520import_manifest_file(manifest_info_t *info, boolean_t validate, FILE *pout,
521    uint_t flags)
522{
523	bundle_t *b;
524	tmpl_errors_t *errs;
525	const char *file;
526	tmpl_validate_status_t vr;
527
528	file = info->mi_path;
529
530	/* Load the manifest */
531	b = internal_bundle_new();
532
533	if (lxml_get_bundle_file(b, file, SVCCFG_OP_IMPORT) != 0) {
534		internal_bundle_free(b);
535		return (-1);
536	}
537
538	/* Validate */
539	if ((vr = tmpl_validate_bundle(b, &errs)) != TVS_SUCCESS) {
540		char *prefix;
541
542		if ((validate == 0) || (vr == TVS_WARN)) {
543			prefix = gettext("Warning: ");
544		} else {
545			prefix = "";
546		}
547		tmpl_errors_print(stderr, errs, prefix);
548		if (validate && (vr != TVS_WARN)) {
549			tmpl_errors_destroy(errs);
550			semerr(gettext("Import of %s failed.\n"),
551			    info->mi_path);
552			if (pout != NULL) {
553				(void) fprintf(pout, gettext("WARNING: svccfg "
554				    "import of %s failed.\n"), info->mi_path);
555			}
556
557			return (-1);
558		}
559	}
560	tmpl_errors_destroy(errs);
561
562	/* Import */
563	if (lscf_bundle_import(b, file, flags) != 0) {
564		internal_bundle_free(b);
565		semerr(gettext("Import of %s failed.\n"), info->mi_path);
566		if (pout != NULL) {
567			(void) fprintf(pout, gettext("WARNING: svccfg import "
568			    "of %s failed.\n"), info->mi_path);
569		}
570		return (-1);
571	}
572
573	internal_bundle_free(b);
574
575	if (info->mi_prop) {
576		char *errstr;
577
578		if (mhash_store_entry(g_hndl, info->mi_prop, file,
579		    info->mi_hash, APPLY_NONE, &errstr)) {
580			if (errstr)
581				semerr(gettext("Could not store hash for %s. "
582				    "%s\n"), info->mi_path, errstr);
583			else
584				semerr(gettext("Unknown error from "
585				    "mhash_store_entry() for %s\n"),
586				    info->mi_path);
587		}
588
589	}
590
591	return (0);
592}
593
594/*
595 * Return values:
596 *	1	No manifests need to be imported.
597 *	0	Success
598 *	-1	Error
599 *	-2	Syntax error
600 */
601int
602engine_import(uu_list_t *args)
603{
604	int argc, i, o;
605	int dont_exit;
606	int failed_manifests;
607	int total_manifests;
608	char *file;
609	char **argv = NULL;
610	string_list_t *slp;
611	boolean_t validate = B_FALSE;
612	uint_t flags = SCI_GENERALLAST;
613	int dirarg = 0;
614	int isdir;
615	int rc = -1;
616	struct stat sb;
617	char **paths;
618	manifest_info_t ***manifest_sets = NULL;
619	manifest_info_t **manifests;
620	char *progress_file = NULL;
621	FILE *progress_out = NULL;
622	int progress_count;
623	int back_count;
624	int count;
625	int fm_flags;
626
627	argc = uu_list_numnodes(args);
628	if (argc < 1)
629		return (-2);
630
631	argv = calloc(argc + 1, sizeof (char *));
632	if (argv == NULL)
633		uu_die(gettext("Out of memory.\n"));
634
635	for (slp = uu_list_first(args), i = 0;
636	    slp != NULL;
637	    slp = uu_list_next(args, slp), ++i)
638		argv[i] = slp->str;
639
640	argv[i] = NULL;
641
642	opterr = 0;
643	optind = 0;				/* Remember, no argv[0]. */
644	for (;;) {
645		o = getopt(argc, argv, "np:V");
646		if (o == -1)
647			break;
648
649		switch (o) {
650		case 'n':
651			flags |= SCI_NOREFRESH;
652			break;
653
654		case 'p':
655			progress_file = optarg;
656			break;
657
658		case 'V':
659			validate = B_TRUE;
660			break;
661
662		case '?':
663			free(argv);
664			return (-2);
665
666		default:
667			bad_error("getopt", o);
668		}
669	}
670
671	argc -= optind;
672	if (argc < 1) {
673		free(argv);
674		return (-2);
675	}
676
677	/* Open device for progress messages */
678	if (progress_file != NULL) {
679		if (strcmp(progress_file, "-") == 0) {
680			progress_out = stdout;
681		} else {
682			progress_out = fopen(progress_file, "w");
683			if (progress_out == NULL) {
684				semerr(gettext("Unable to open %s for "
685				    "progress reporting.  %s\n"),
686				    progress_file, strerror(errno));
687				goto out;
688			}
689			setbuf(progress_out, NULL);
690		}
691	}
692
693	paths = argv+optind;
694	manifest_sets = safe_malloc(argc * sizeof (*manifest_sets));
695
696	/* If we're in interactive mode, force strict validation. */
697	if (est->sc_cmd_flags & SC_CMD_IACTIVE)
698		validate = B_TRUE;
699
700	lscf_prep_hndl();
701
702	/* Determine which manifests must be imported. */
703
704	total_manifests = 0;
705	for (i = 0; i < argc; i++) {
706		file = *(paths + i);
707		fm_flags = CHECKHASH;
708
709		/* Determine if argument is a directory or file. */
710		if (stat(file, &sb) == -1) {
711			semerr(gettext("Unable to stat file %s.  %s\n"), file,
712			    strerror(errno));
713			goto out;
714		}
715		if (sb.st_mode & S_IFDIR) {
716			fm_flags |= CHECKEXT;
717			dirarg = 1;
718			isdir = 1;
719		} else if (sb.st_mode & S_IFREG) {
720			isdir = 0;
721		} else {
722			semerr(gettext("%s is not a directory or regular "
723			    "file\n"), file);
724			goto out;
725		}
726
727		/* Get list of manifests that we should import for this path. */
728		if ((count = find_manifests(file, &manifests, fm_flags)) < 0) {
729			if (isdir) {
730				semerr(gettext("Could not hash directory %s\n"),
731				    file);
732			} else {
733				semerr(gettext("Could not hash file %s\n"),
734				    file);
735			}
736			free_manifest_array(manifests);
737			goto out;
738		}
739		total_manifests += count;
740		manifest_sets[i] = manifests;
741	}
742
743	if (total_manifests == 0) {
744		/* No manifests to process. */
745		if (g_verbose) {
746			warn(gettext("No changes were necessary\n"));
747		}
748		rc = 1;
749		goto out;
750	}
751
752	/*
753	 * If we're processing more than one file, we don't want to exit if
754	 * we encounter an error.  We should go ahead and process all of
755	 * the manifests.
756	 */
757	dont_exit = est->sc_cmd_flags & SC_CMD_DONT_EXIT;
758	if (total_manifests > 1)
759		est->sc_cmd_flags |= SC_CMD_DONT_EXIT;
760
761	if (progress_out != NULL)
762		(void) fprintf(progress_out,
763		    "Loading smf(5) service descriptions: ");
764
765	failed_manifests = 0;
766	progress_count = 0;
767	for (i = 0; i < argc; i++) {
768		manifests = manifest_sets[i];
769		if (manifests == NULL)
770			continue;
771		for (; *manifests != NULL; manifests++) {
772			progress_count++;
773			if (progress_out != NULL) {
774				back_count = fprintf(progress_out, "%d/%d",
775				    progress_count, total_manifests);
776				while (back_count-- > 0) {
777					(void) fputc('\b', progress_out);
778				}
779			}
780			if (import_manifest_file(*manifests, validate,
781			    progress_out, flags) != 0) {
782				failed_manifests++;
783			}
784		}
785	}
786	if (progress_out != NULL)
787		(void) fputc('\n', progress_out);
788
789	if ((total_manifests > 1) && (dont_exit == 0))
790		est->sc_cmd_flags &= ~SC_CMD_DONT_EXIT;
791
792	if (dirarg && total_manifests > 0) {
793		char *msg;
794
795		msg = "Loaded %d smf(5) service descriptions\n";
796		warn(gettext(msg), progress_count);
797
798		if (failed_manifests) {
799			msg = "%d smf(5) service descriptions failed to load\n";
800			warn(gettext(msg), failed_manifests);
801		}
802	}
803
804	if (failed_manifests > 0)
805		goto out;
806
807	if (g_verbose)
808		warn(gettext("Successful import.\n"));
809	rc = 0;
810
811out:
812	if ((progress_out != NULL) && (progress_out != stdout))
813		(void) fclose(progress_out);
814	free(argv);
815	if (manifest_sets != NULL) {
816		for (i = 0; i < argc; i++) {
817			free_manifest_array(manifest_sets[i]);
818		}
819		free(manifest_sets);
820	}
821	return (rc);
822}
823
824/*
825 * Walk each service and get its manifest file.
826 *
827 * If the file exists check instance support, and cleanup any
828 * stale instances.
829 *
830 * If the file doesn't exist tear down the service and/or instances
831 * that are no longer supported by files.
832 */
833int
834engine_cleanup(int flags)
835{
836	boolean_t		activity = B_TRUE;
837	int			r = -1;
838
839	lscf_prep_hndl();
840
841	if (flags == 1) {
842		activity = B_FALSE;
843	}
844
845	if (scf_walk_fmri(g_hndl, 0, NULL, SCF_WALK_SERVICE|SCF_WALK_NOINSTANCE,
846	    lscf_service_cleanup, (void *)activity, NULL,
847	    uu_warn) == SCF_SUCCESS)
848		r = 0;
849
850	(void) lscf_hash_cleanup();
851
852	return (r);
853}
854
855int
856engine_apply(const char *file, int apply_changes)
857{
858	int ret;
859	bundle_t *b;
860	char *pname;
861	uchar_t hash[MHASH_SIZE];
862
863	lscf_prep_hndl();
864
865	ret = mhash_test_file(g_hndl, file, 1, &pname, hash);
866	if (ret != MHASH_NEWFILE)
867		return (ret);
868
869	b = internal_bundle_new();
870
871	if (lxml_get_bundle_file(b, file, SVCCFG_OP_APPLY) != 0) {
872		internal_bundle_free(b);
873		return (-1);
874	}
875
876	if (!apply_changes) {	/* we don't want to apply, just test */
877		internal_bundle_free(b);
878		return (0);
879	}
880
881	if (lscf_bundle_apply(b, file) != 0) {
882		internal_bundle_free(b);
883		return (-1);
884	}
885
886	internal_bundle_free(b);
887
888	if (pname) {
889		apply_action_t apply;
890		char *errstr;
891
892		apply = (est->sc_in_emi == 1) ? APPLY_LATE : APPLY_NONE;
893		if (mhash_store_entry(g_hndl, pname, file, hash, apply,
894		    &errstr)) {
895			semerr(errstr);
896		}
897
898		free(pname);
899	}
900
901	return (0);
902}
903
904int
905engine_restore(const char *file)
906{
907	bundle_t *b;
908
909	lscf_prep_hndl();
910
911	b = internal_bundle_new();
912
913	if (lxml_get_bundle_file(b, file, SVCCFG_OP_RESTORE) != 0) {
914		internal_bundle_free(b);
915		return (-1);
916	}
917
918	if (lscf_bundle_import(b, file, SCI_NOSNAP) != 0) {
919		internal_bundle_free(b);
920		return (-1);
921	}
922
923	internal_bundle_free(b);
924
925	return (0);
926}
927
928int
929engine_set(uu_list_t *args)
930{
931	uu_list_walk_t *walk;
932	string_list_t *slp;
933
934	if (uu_list_first(args) == NULL) {
935		/* Display current options. */
936		if (!g_verbose)
937			(void) fputs("no", stdout);
938		(void) puts("verbose");
939
940		return (0);
941	}
942
943	walk = uu_list_walk_start(args, UU_DEFAULT);
944	if (walk == NULL)
945		uu_die(gettext("Couldn't read arguments"));
946
947	/* Use getopt? */
948	for (slp = uu_list_walk_next(walk);
949	    slp != NULL;
950	    slp = uu_list_walk_next(walk)) {
951		if (slp->str[0] == '-') {
952			char *op;
953
954			for (op = &slp->str[1]; *op != '\0'; ++op) {
955				switch (*op) {
956				case 'v':
957					g_verbose = 1;
958					break;
959
960				case 'V':
961					g_verbose = 0;
962					break;
963
964				default:
965					warn(gettext("Unknown option -%c.\n"),
966					    *op);
967				}
968			}
969		} else {
970			warn(gettext("No non-flag arguments defined.\n"));
971		}
972	}
973
974	return (0);
975}
976
977void
978help(int com)
979{
980	int i;
981
982	if (com == 0) {
983		warn(gettext("General commands:	 help set repository end\n"
984		    "Manifest commands:	 inventory validate import export "
985		    "archive\n"
986		    "Profile commands:	 apply extract\n"
987		    "Entity commands:	 list select unselect add delete "
988		    "describe\n"
989		    "Snapshot commands:	 listsnap selectsnap revert\n"
990		    "Instance commands:	 refresh\n"
991		    "Property group commands: listpg addpg delpg\n"
992		    "Property commands:	 listprop setprop delprop editprop\n"
993		    "Property value commands: addpropvalue delpropvalue "
994		    "setenv unsetenv\n"));
995		return;
996	}
997
998	for (i = 0; help_messages[i].message != NULL; ++i) {
999		if (help_messages[i].token == com) {
1000			warn(gettext("Usage: %s\n"),
1001			    gettext(help_messages[i].message));
1002			return;
1003		}
1004	}
1005
1006	warn(gettext("Unknown command.\n"));
1007}
1008