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
  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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
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
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 }