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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26
27 /*
28 * svccfg(1) interpreter and command execution engine.
29 */
30
31 #include <sys/mman.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <assert.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <libintl.h>
38 #include <libtecla.h>
39 #include <md5.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43
44 #include "manifest_find.h"
45 #include "manifest_hash.h"
46 #include "svccfg.h"
47
48 #define MS_PER_US 1000
49
50 engine_state_t *est;
51
52 /*
53 * Replacement lex(1) character retrieval routines.
54 */
55 int
engine_cmd_getc(engine_state_t * E)56 engine_cmd_getc(engine_state_t *E)
57 {
58 if (E->sc_cmd_file != NULL)
59 return (getc(E->sc_cmd_file));
60
61 if (E->sc_cmd_flags & SC_CMD_EOF)
62 return (EOF);
63
64 if (E->sc_cmd_bufoff < E->sc_cmd_bufsz)
65 return (*(E->sc_cmd_buf + E->sc_cmd_bufoff++));
66
67 if (!(E->sc_cmd_flags & SC_CMD_IACTIVE)) {
68 E->sc_cmd_flags |= SC_CMD_EOF;
69
70 return (EOF);
71 } else {
72 #ifdef NATIVE_BUILD
73 return (EOF);
74 #else
75 extern int parens;
76
77 if (parens <= 0) {
78 E->sc_cmd_flags |= SC_CMD_EOF;
79 return (EOF);
80 }
81
82 for (;;) {
83 E->sc_cmd_buf = gl_get_line(E->sc_gl, "> ", NULL, -1);
84 if (E->sc_cmd_buf != NULL)
85 break;
86
87 switch (gl_return_status(E->sc_gl)) {
88 case GLR_SIGNAL:
89 gl_abandon_line(E->sc_gl);
90 continue;
91
92 case GLR_EOF:
93 E->sc_cmd_flags |= SC_CMD_EOF;
94 return (EOF);
95
96 case GLR_ERROR:
97 uu_die(gettext("Error reading terminal: %s.\n"),
98 gl_error_message(E->sc_gl, NULL, 0));
99 /* NOTREACHED */
100
101 default:
102 #ifndef NDEBUG
103 (void) fprintf(stderr, "%s:%d: gl_get_line() "
104 "returned unexpected value %d.\n", __FILE__,
105 __LINE__, gl_return_status(E->sc_gl));
106 #endif
107 abort();
108 }
109 }
110
111 E->sc_cmd_bufsz = strlen(E->sc_cmd_buf);
112 E->sc_cmd_bufoff = 1;
113
114 return (E->sc_cmd_buf[0]);
115 #endif /* NATIVE_BUILD */
116 }
117 }
118
119 int
engine_cmd_ungetc(engine_state_t * E,char c)120 engine_cmd_ungetc(engine_state_t *E, char c)
121 {
122 if (E->sc_cmd_file != NULL)
123 return (ungetc(c, E->sc_cmd_file));
124
125 if (E->sc_cmd_buf != NULL)
126 *(E->sc_cmd_buf + --E->sc_cmd_bufoff) = c;
127
128 return (c);
129 }
130
131 /*ARGSUSED*/
132 void
engine_cmd_nputs(engine_state_t * E,char * c,size_t n)133 engine_cmd_nputs(engine_state_t *E, char *c, size_t n)
134 {
135 /* our lexer shouldn't need this state */
136 exit(11);
137 }
138
139 int
engine_exec(char * cmd)140 engine_exec(char *cmd)
141 {
142 est->sc_cmd_buf = cmd;
143 est->sc_cmd_bufsz = strlen(cmd) + 1;
144 est->sc_cmd_bufoff = 0;
145
146 (void) yyparse();
147
148 return (0);
149 }
150
151 #ifndef NATIVE_BUILD
152 /* ARGSUSED */
153 static
CPL_CHECK_FN(check_xml)154 CPL_CHECK_FN(check_xml)
155 {
156 const char *ext;
157
158 if (strlen(pathname) < 4)
159 return (0);
160
161 ext = pathname + strlen(pathname) - 4;
162
163 return (strcmp(ext, ".xml") == 0 ? 1 : 0);
164 }
165
166 static const char * const whitespace = " \t";
167
168 static
CPL_MATCH_FN(complete_single_xml_file_arg)169 CPL_MATCH_FN(complete_single_xml_file_arg)
170 {
171 const char *arg1 = data;
172 int arg1end_i, ret;
173 CplFileConf *cfc;
174
175 arg1end_i = arg1 + strcspn(arg1, whitespace) - line;
176 if (arg1end_i < word_end)
177 return (0);
178
179 cfc = new_CplFileConf();
180 if (cfc == NULL) {
181 cpl_record_error(cpl, "Out of memory.");
182 return (1);
183 }
184
185 cfc_set_check_fn(cfc, check_xml, NULL);
186
187 ret = cpl_file_completions(cpl, cfc, line, word_end);
188
189 (void) del_CplFileConf(cfc);
190 return (ret);
191 }
192
193 static struct cmd_info {
194 const char *name;
195 uint32_t flags;
196 CplMatchFn *complete_args_f;
197 } cmds[] = {
198 { "validate", CS_GLOBAL, complete_single_xml_file_arg },
199 { "import", CS_GLOBAL, complete_single_xml_file_arg },
200 { "cleanup", CS_GLOBAL, NULL},
201 { "export", CS_GLOBAL, NULL },
202 { "archive", CS_GLOBAL, NULL },
203 { "apply", CS_GLOBAL, complete_single_xml_file_arg },
204 { "extract", CS_GLOBAL, NULL },
205 { "repository", CS_GLOBAL, NULL },
206 { "inventory", CS_GLOBAL, complete_single_xml_file_arg },
207 { "set", CS_GLOBAL, NULL },
208 { "end", CS_GLOBAL, NULL },
209 { "exit", CS_GLOBAL, NULL },
210 { "quit", CS_GLOBAL, NULL },
211 { "help", CS_GLOBAL, NULL },
212 { "delete", CS_GLOBAL, NULL },
213 { "select", CS_GLOBAL, complete_select },
214 { "unselect", CS_SVC | CS_INST | CS_SNAP, NULL },
215 { "list", CS_SCOPE | CS_SVC | CS_SNAP, NULL },
216 { "add", CS_SCOPE | CS_SVC, NULL },
217 { "listpg", CS_SVC | CS_INST | CS_SNAP, NULL },
218 { "addpg", CS_SVC | CS_INST, NULL },
219 { "delpg", CS_SVC | CS_INST, NULL },
220 { "delhash", CS_GLOBAL, complete_single_xml_file_arg },
221 { "listprop", CS_SVC | CS_INST | CS_SNAP, NULL },
222 { "setprop", CS_SVC | CS_INST, NULL },
223 { "delprop", CS_SVC | CS_INST, NULL },
224 { "editprop", CS_SVC | CS_INST, NULL },
225 { "describe", CS_SVC | CS_INST | CS_SNAP, NULL },
226 { "listsnap", CS_INST | CS_SNAP, NULL },
227 { "selectsnap", CS_INST | CS_SNAP, NULL },
228 { "revert", CS_INST | CS_SNAP, NULL },
229 { "refresh", CS_INST, NULL },
230 { NULL }
231 };
232
233 int
add_cmd_matches(WordCompletion * cpl,const char * line,int word_end,uint32_t scope)234 add_cmd_matches(WordCompletion *cpl, const char *line, int word_end,
235 uint32_t scope)
236 {
237 int word_start, err;
238 size_t len;
239 const char *bol;
240 struct cmd_info *cip;
241
242 word_start = strspn(line, whitespace);
243 len = word_end - word_start;
244 bol = line + word_end - len;
245
246 for (cip = cmds; cip->name != NULL; ++cip) {
247 if ((cip->flags & scope) == 0)
248 continue;
249
250 if (strncmp(cip->name, bol, len) == 0) {
251 err = cpl_add_completion(cpl, line, word_start,
252 word_end, cip->name + len, "", " ");
253 if (err != 0)
254 return (err);
255 }
256 }
257
258 return (0);
259 }
260
261 /*
262 * Suggest completions. We must first determine if the cursor is in command
263 * position or in argument position. If the former, complete_command() finds
264 * matching commands. If the latter, we tail-call the command-specific
265 * argument-completion routine in the cmds table.
266 */
267 /* ARGSUSED */
268 static
CPL_MATCH_FN(complete)269 CPL_MATCH_FN(complete)
270 {
271 const char *arg0, *arg1;
272 size_t arg0len;
273 struct cmd_info *cip;
274
275 arg0 = line + strspn(line, whitespace);
276 arg0len = strcspn(arg0, whitespace);
277 if ((arg0 + arg0len) - line >= word_end ||
278 (arg0[arg0len] != ' ' && arg0[arg0len] != '\t'))
279 return (complete_command(cpl, (void *)arg0, line, word_end));
280
281 arg1 = arg0 + arg0len;
282 arg1 += strspn(arg1, whitespace);
283
284 for (cip = cmds; cip->name != NULL; ++cip) {
285 if (strlen(cip->name) != arg0len)
286 continue;
287
288 if (strncmp(cip->name, arg0, arg0len) != 0)
289 continue;
290
291 if (cip->complete_args_f == NULL)
292 break;
293
294 return (cip->complete_args_f(cpl, (void *)arg1, line,
295 word_end));
296 }
297
298 return (0);
299 }
300 #endif /* NATIVE_BUILD */
301
302 int
engine_interp()303 engine_interp()
304 {
305 #ifdef NATIVE_BUILD
306 uu_die("native build does not support interactive mode.");
307 #else
308 char *selfmri;
309 size_t sfsz;
310 int r;
311
312 extern int parens;
313
314 (void) sigset(SIGINT, SIG_IGN);
315
316 est->sc_gl = new_GetLine(512, 8000);
317 if (est->sc_gl == NULL)
318 uu_die(gettext("Out of memory.\n"));
319
320 /* The longest string is "[snapname]fmri[:instname]> ". */
321 sfsz = 1 + max_scf_name_len + 1 + max_scf_fmri_len + 2 +
322 max_scf_name_len + 1 + 2 + 1;
323 selfmri = safe_malloc(sfsz);
324
325 r = gl_customize_completion(est->sc_gl, NULL, complete);
326 assert(r == 0);
327
328 for (;;) {
329 lscf_get_selection_str(selfmri, sfsz - 2);
330 (void) strcat(selfmri, "> ");
331 est->sc_cmd_buf = gl_get_line(est->sc_gl, selfmri, NULL, -1);
332
333 if (est->sc_cmd_buf == NULL) {
334 switch (gl_return_status(est->sc_gl)) {
335 case GLR_SIGNAL:
336 gl_abandon_line(est->sc_gl);
337 continue;
338
339 case GLR_EOF:
340 break;
341
342 case GLR_ERROR:
343 uu_die(gettext("Error reading terminal: %s.\n"),
344 gl_error_message(est->sc_gl, NULL, 0));
345 /* NOTREACHED */
346
347 default:
348 #ifndef NDEBUG
349 (void) fprintf(stderr, "%s:%d: gl_get_line() "
350 "returned unexpected value %d.\n", __FILE__,
351 __LINE__, gl_return_status(est->sc_gl));
352 #endif
353 abort();
354 }
355
356 break;
357 }
358
359 parens = 0;
360 est->sc_cmd_bufsz = strlen(est->sc_cmd_buf);
361 est->sc_cmd_bufoff = 0;
362 est->sc_cmd_flags = SC_CMD_IACTIVE;
363
364 (void) yyparse();
365 }
366
367 free(selfmri);
368 est->sc_gl = del_GetLine(est->sc_gl); /* returns NULL */
369
370 #endif /* NATIVE_BUILD */
371 return (0);
372 }
373
374 int
engine_source(const char * name,boolean_t dont_exit)375 engine_source(const char *name, boolean_t dont_exit)
376 {
377 engine_state_t *old = est;
378 struct stat st;
379 int ret;
380
381 est = uu_zalloc(sizeof (engine_state_t));
382
383 /* first, copy the stuff set up in engine_init */
384 est->sc_repo_pid = old->sc_repo_pid;
385 if (old->sc_repo_filename != NULL)
386 est->sc_repo_filename = safe_strdup(old->sc_repo_filename);
387 if (old->sc_repo_doordir != NULL)
388 est->sc_repo_doordir = safe_strdup(old->sc_repo_doordir);
389 if (old->sc_repo_doorname != NULL)
390 est->sc_repo_doorname = safe_strdup(old->sc_repo_doorname);
391 if (old->sc_repo_server != NULL)
392 est->sc_repo_server = safe_strdup(old->sc_repo_server);
393
394 /* set up the new guy */
395 est->sc_cmd_lineno = 1;
396
397 if (dont_exit)
398 est->sc_cmd_flags |= SC_CMD_DONT_EXIT;
399
400 if (strcmp(name, "-") == 0) {
401 est->sc_cmd_file = stdin;
402 est->sc_cmd_filename = "<stdin>";
403 } else {
404 errno = 0;
405 est->sc_cmd_filename = name;
406 est->sc_cmd_file = fopen(name, "r");
407 if (est->sc_cmd_file == NULL) {
408 if (errno == 0)
409 semerr(gettext("No free stdio streams.\n"));
410 else
411 semerr(gettext("Could not open %s"), name);
412
413 ret = -1;
414 goto fail;
415 }
416
417 do {
418 ret = fstat(fileno(est->sc_cmd_file), &st);
419 } while (ret != 0 && errno == EINTR);
420 if (ret != 0) {
421 (void) fclose(est->sc_cmd_file);
422 est->sc_cmd_file = NULL; /* for semerr() */
423
424 semerr(gettext("Could not stat %s"), name);
425
426 ret = -1;
427 goto fail;
428 }
429
430 if (!S_ISREG(st.st_mode)) {
431 (void) fclose(est->sc_cmd_file);
432 est->sc_cmd_file = NULL; /* for semerr() */
433
434 semerr(gettext("%s is not a regular file.\n"), name);
435
436 ret = -1;
437 goto fail;
438 }
439 }
440
441 (void) yyparse();
442
443 if (est->sc_cmd_file != stdin)
444 (void) fclose(est->sc_cmd_file);
445
446 ret = 0;
447
448 fail:
449 if (est->sc_repo_pid != old->sc_repo_pid)
450 lscf_cleanup(); /* clean up any new repository */
451
452 if (est->sc_repo_filename != NULL)
453 free((void *)est->sc_repo_filename);
454 if (est->sc_repo_doordir != NULL)
455 free((void *)est->sc_repo_doordir);
456 if (est->sc_repo_doorname != NULL)
457 free((void *)est->sc_repo_doorname);
458 if (est->sc_repo_server != NULL)
459 free((void *)est->sc_repo_server);
460 free(est);
461
462 est = old;
463
464 return (ret);
465 }
466
467 /*
468 * Initialize svccfg state. We recognize four environment variables:
469 *
470 * SVCCFG_REPOSITORY Create a private instance of svc.configd(1M) to answer
471 * requests for the specified repository file.
472 * SVCCFG_DOOR_PATH Directory for door creation.
473 *
474 * SVCCFG_DOOR Rendezvous via an alternative repository door.
475 *
476 * SVCCFG_CONFIGD_PATH Resolvable path to alternative svc.configd(1M) binary.
477 */
478 void
engine_init()479 engine_init()
480 {
481 const char *cp;
482
483 est = uu_zalloc(sizeof (engine_state_t));
484
485 est->sc_cmd_lineno = 1;
486 est->sc_repo_pid = -1;
487
488 cp = getenv("SVCCFG_REPOSITORY");
489 est->sc_repo_filename = cp ? safe_strdup(cp) : NULL;
490
491 cp = getenv("SVCCFG_DOOR_PATH");
492 est->sc_repo_doordir = cp ? cp : "/var/run";
493
494 cp = getenv("SVCCFG_DOOR");
495 if (cp != NULL) {
496 if (est->sc_repo_filename != NULL) {
497 uu_warn(gettext("SVCCFG_DOOR unused when "
498 "SVCCFG_REPOSITORY specified\n"));
499 } else {
500 est->sc_repo_doorname = safe_strdup(cp);
501 }
502 }
503
504 cp = getenv("SVCCFG_CONFIGD_PATH");
505 est->sc_repo_server = cp ? cp : "/lib/svc/bin/svc.configd";
506
507 est->sc_miss_type = B_FALSE;
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
519 static int
import_manifest_file(manifest_info_t * info,boolean_t validate,FILE * pout,uint_t flags)520 import_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 */
601 int
engine_import(uu_list_t * args)602 engine_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(g_hndl, file, &manifests,
729 fm_flags)) < 0) {
730 if (isdir) {
731 semerr(gettext("Could not hash directory %s\n"),
732 file);
733 } else {
734 semerr(gettext("Could not hash file %s\n"),
735 file);
736 }
737 free_manifest_array(manifests);
738 goto out;
739 }
740 total_manifests += count;
741 manifest_sets[i] = manifests;
742 }
743
744 if (total_manifests == 0) {
745 /* No manifests to process. */
746 if (g_verbose) {
747 warn(gettext("No changes were necessary\n"));
748 }
749 rc = 1;
750 goto out;
751 }
752
753 /*
754 * If we're processing more than one file, we don't want to exit if
755 * we encounter an error. We should go ahead and process all of
756 * the manifests.
757 */
758 dont_exit = est->sc_cmd_flags & SC_CMD_DONT_EXIT;
759 if (total_manifests > 1)
760 est->sc_cmd_flags |= SC_CMD_DONT_EXIT;
761
762 if (progress_out != NULL)
763 (void) fprintf(progress_out,
764 "Loading smf(5) service descriptions: ");
765
766 failed_manifests = 0;
767 progress_count = 0;
768 for (i = 0; i < argc; i++) {
769 manifests = manifest_sets[i];
770 if (manifests == NULL)
771 continue;
772 for (; *manifests != NULL; manifests++) {
773 progress_count++;
774 if (progress_out != NULL) {
775 back_count = fprintf(progress_out, "%d/%d",
776 progress_count, total_manifests);
777 while (back_count-- > 0) {
778 (void) fputc('\b', progress_out);
779 }
780 }
781 if (import_manifest_file(*manifests, validate,
782 progress_out, flags) != 0) {
783 failed_manifests++;
784 }
785 }
786 }
787 if (progress_out != NULL)
788 (void) fputc('\n', progress_out);
789
790 if ((total_manifests > 1) && (dont_exit == 0))
791 est->sc_cmd_flags &= ~SC_CMD_DONT_EXIT;
792
793 if (dirarg && total_manifests > 0) {
794 char *msg;
795
796 msg = "Loaded %d smf(5) service descriptions\n";
797 warn(gettext(msg), progress_count);
798
799 if (failed_manifests) {
800 msg = "%d smf(5) service descriptions failed to load\n";
801 warn(gettext(msg), failed_manifests);
802 }
803 }
804
805 if (failed_manifests > 0)
806 goto out;
807
808 if (g_verbose)
809 warn(gettext("Successful import.\n"));
810 rc = 0;
811
812 out:
813 if ((progress_out != NULL) && (progress_out != stdout))
814 (void) fclose(progress_out);
815 free(argv);
816 if (manifest_sets != NULL) {
817 for (i = 0; i < argc; i++) {
818 free_manifest_array(manifest_sets[i]);
819 }
820 free(manifest_sets);
821 }
822 return (rc);
823 }
824
825 /*
826 * Walk each service and get its manifest file.
827 *
828 * If the file exists check instance support, and cleanup any
829 * stale instances.
830 *
831 * If the file doesn't exist tear down the service and/or instances
832 * that are no longer supported by files.
833 */
834 int
engine_cleanup(int flags)835 engine_cleanup(int flags)
836 {
837 boolean_t activity = B_TRUE;
838 int r = -1;
839
840 lscf_prep_hndl();
841
842 if (flags == 1) {
843 activity = B_FALSE;
844 }
845
846 if (scf_walk_fmri(g_hndl, 0, NULL, SCF_WALK_SERVICE|SCF_WALK_NOINSTANCE,
847 lscf_service_cleanup, (void *)activity, NULL,
848 uu_warn) == SCF_SUCCESS)
849 r = 0;
850
851 (void) lscf_hash_cleanup();
852
853 return (r);
854 }
855
856 static int
apply_profile(manifest_info_t * info,int apply_changes)857 apply_profile(manifest_info_t *info, int apply_changes)
858 {
859 bundle_t *b = internal_bundle_new();
860
861 if (lxml_get_bundle_file(b, info->mi_path, SVCCFG_OP_APPLY) != 0) {
862 internal_bundle_free(b);
863 return (-1);
864 }
865
866 if (!apply_changes) { /* we don't want to apply, just test */
867 internal_bundle_free(b);
868 return (0);
869 }
870
871 if (lscf_bundle_apply(b, info->mi_path) != 0) {
872 internal_bundle_free(b);
873 return (-1);
874 }
875
876 internal_bundle_free(b);
877
878 if (info->mi_prop) {
879 apply_action_t apply;
880 char *errstr;
881
882 apply = (est->sc_in_emi == 1) ? APPLY_LATE : APPLY_NONE;
883 if (mhash_store_entry(g_hndl, info->mi_prop, info->mi_path,
884 info->mi_hash, apply, &errstr)) {
885 semerr(errstr);
886 }
887 }
888
889 return (0);
890 }
891
892 int
engine_apply(const char * file,int apply_changes)893 engine_apply(const char *file, int apply_changes)
894 {
895 int rc = 0;
896 int isdir;
897 int dont_exit;
898 int profile_count;
899 int fm_flags;
900 struct stat sb;
901 manifest_info_t **profiles = NULL;
902 manifest_info_t **entry;
903 manifest_info_t *pfile;
904
905 lscf_prep_hndl();
906
907 /* Determine which profile(s) must be applied. */
908
909 profile_count = 0;
910 fm_flags = BUNDLE_PROF | CHECKHASH;
911
912 /* Determine if argument is a directory or file. */
913 if (stat(file, &sb) == -1) {
914 semerr(gettext("Unable to stat file %s. %s\n"), file,
915 strerror(errno));
916 rc = -1;
917 goto out;
918 }
919
920 if (sb.st_mode & S_IFDIR) {
921 fm_flags |= CHECKEXT;
922 isdir = 1;
923 } else if (sb.st_mode & S_IFREG) {
924 isdir = 0;
925 } else {
926 semerr(gettext("%s is not a directory or regular "
927 "file\n"), file);
928 rc = -1;
929 goto out;
930 }
931
932 /* Get list of profiles to be applied. */
933 if ((profile_count = find_manifests(g_hndl, file, &profiles,
934 fm_flags)) < 0) {
935
936 if (isdir) {
937 semerr(gettext("Could not hash directory %s\n"), file);
938 } else {
939 semerr(gettext("Could not hash file %s\n"), file);
940 }
941 rc = -1;
942 goto out;
943 }
944
945 if (profile_count == 0) {
946 /* No profiles to process. */
947 if (g_verbose) {
948 warn(gettext("No changes were necessary\n"));
949 }
950 goto out;
951 }
952
953 /*
954 * We don't want to exit if we encounter an error. We should go ahead
955 * and process all of the profiles.
956 */
957 dont_exit = est->sc_cmd_flags & SC_CMD_DONT_EXIT;
958 est->sc_cmd_flags |= SC_CMD_DONT_EXIT;
959
960 for (entry = profiles; *entry != NULL; entry++) {
961 pfile = *entry;
962
963 if (apply_profile(pfile, apply_changes) == 0) {
964 if (g_verbose) {
965 warn(gettext("Successfully applied: %s\n"),
966 pfile->mi_path);
967 }
968 } else {
969 warn(gettext("WARNING: Failed to apply %s\n"),
970 pfile->mi_path);
971 rc = -1;
972 }
973 }
974
975 if (dont_exit == 0)
976 est->sc_cmd_flags &= ~SC_CMD_DONT_EXIT;
977
978 /* exit(1) appropriately if any profile failed to be applied. */
979 if ((rc == -1) &&
980 (est->sc_cmd_flags & (SC_CMD_IACTIVE | SC_CMD_DONT_EXIT)) == 0) {
981 free_manifest_array(profiles);
982 exit(1);
983 }
984
985 out:
986 free_manifest_array(profiles);
987 return (rc);
988 }
989
990 int
engine_restore(const char * file)991 engine_restore(const char *file)
992 {
993 bundle_t *b;
994
995 lscf_prep_hndl();
996
997 b = internal_bundle_new();
998
999 if (lxml_get_bundle_file(b, file, SVCCFG_OP_RESTORE) != 0) {
1000 internal_bundle_free(b);
1001 return (-1);
1002 }
1003
1004 if (lscf_bundle_import(b, file, SCI_NOSNAP) != 0) {
1005 internal_bundle_free(b);
1006 return (-1);
1007 }
1008
1009 internal_bundle_free(b);
1010
1011 return (0);
1012 }
1013
1014 int
engine_set(uu_list_t * args)1015 engine_set(uu_list_t *args)
1016 {
1017 uu_list_walk_t *walk;
1018 string_list_t *slp;
1019
1020 if (uu_list_first(args) == NULL) {
1021 /* Display current options. */
1022 if (!g_verbose)
1023 (void) fputs("no", stdout);
1024 (void) puts("verbose");
1025
1026 return (0);
1027 }
1028
1029 walk = uu_list_walk_start(args, UU_DEFAULT);
1030 if (walk == NULL)
1031 uu_die(gettext("Couldn't read arguments"));
1032
1033 /* Use getopt? */
1034 for (slp = uu_list_walk_next(walk);
1035 slp != NULL;
1036 slp = uu_list_walk_next(walk)) {
1037 if (slp->str[0] == '-') {
1038 char *op;
1039
1040 for (op = &slp->str[1]; *op != '\0'; ++op) {
1041 switch (*op) {
1042 case 'v':
1043 g_verbose = 1;
1044 break;
1045
1046 case 'V':
1047 g_verbose = 0;
1048 break;
1049
1050 default:
1051 warn(gettext("Unknown option -%c.\n"),
1052 *op);
1053 }
1054 }
1055 } else {
1056 warn(gettext("No non-flag arguments defined.\n"));
1057 }
1058 }
1059
1060 return (0);
1061 }
1062
1063 void
help(int com)1064 help(int com)
1065 {
1066 int i;
1067
1068 if (com == 0) {
1069 warn(gettext("General commands: help set repository end\n"
1070 "Manifest commands: inventory validate import export "
1071 "archive\n"
1072 "Profile commands: apply extract\n"
1073 "Entity commands: list select unselect add delete "
1074 "describe\n"
1075 "Snapshot commands: listsnap selectsnap revert\n"
1076 "Instance commands: refresh\n"
1077 "Property group commands: listpg addpg delpg\n"
1078 "Property commands: listprop setprop delprop editprop\n"
1079 "Property value commands: addpropvalue delpropvalue "
1080 "setenv unsetenv\n"
1081 "Notification parameters: "
1082 "listnotify setnotify delnotify\n"));
1083 return;
1084 }
1085
1086 for (i = 0; help_messages[i].message != NULL; ++i) {
1087 if (help_messages[i].token == com) {
1088 warn(gettext("Usage: %s\n"),
1089 gettext(help_messages[i].message));
1090 return;
1091 }
1092 }
1093
1094 warn(gettext("Unknown command.\n"));
1095 }
1096