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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * Copyright 2020, Joyent, Inc. All rights reserved.
  28  */
  29 
  30 /*
  31  * svcadm - request adminstrative actions for service instances
  32  */
  33 
  34 #include <locale.h>
  35 #include <libintl.h>
  36 #include <libscf.h>
  37 #include <libscf_priv.h>
  38 #include <libcontract.h>
  39 #include <libcontract_priv.h>
  40 #include <sys/contract/process.h>
  41 #include <libuutil.h>
  42 #include <stddef.h>
  43 #include <stdio.h>
  44 #include <stdlib.h>
  45 #include <string.h>
  46 #include <unistd.h>
  47 #include <fcntl.h>
  48 #include <procfs.h>
  49 #include <assert.h>
  50 #include <errno.h>
  51 #include <zone.h>
  52 
  53 #ifndef TEXT_DOMAIN
  54 #define TEXT_DOMAIN     "SUNW_OST_OSCMD"
  55 #endif /* TEXT_DOMAIN */
  56 
  57 /* Must be a power of two */
  58 #define HT_BUCKETS      64
  59 
  60 /*
  61  * Exit codes for enable and disable -s.
  62  */
  63 #define EXIT_SVC_FAILURE        3
  64 #define EXIT_DEP_FAILURE        4
  65 
  66 #define WALK_FLAGS      (SCF_WALK_UNIPARTIAL | SCF_WALK_MULTIPLE)
  67 
  68 /*
  69  * How long we will wait (in seconds) for a service to change state
  70  * before re-checking its dependencies.
  71  */
  72 #define WAIT_INTERVAL           3
  73 
  74 #define bad_error(func, err)                                            \
  75         uu_panic("%s:%d: %s() failed with unexpected error %d.\n",      \
  76             __FILE__, __LINE__, (func), (err));
  77 
  78 struct ht_elt {
  79         struct ht_elt   *next;
  80         boolean_t       active;
  81         char            str[1];
  82 };
  83 
  84 
  85 /*
  86  * Callback data for enable/disable.
  87  */
  88 #define SET_ENABLED     0x1
  89 #define SET_TEMPORARY   0x2
  90 #define SET_RECURSIVE   0x4
  91 
  92 typedef struct {
  93         char ed_comment[SCF_COMMENT_MAX_LENGTH];
  94         int ed_flags;
  95 } enable_data_t;
  96 
  97 
  98 scf_handle_t *h;
  99 ssize_t max_scf_fmri_sz;
 100 static const char *emsg_permission_denied;
 101 static const char *emsg_nomem;
 102 static const char *emsg_create_pg_perm_denied;
 103 static const char *emsg_pg_perm_denied;
 104 static const char *emsg_prop_perm_denied;
 105 static const char *emsg_no_service;
 106 
 107 static int exit_status = 0;
 108 static int verbose = 0;
 109 static char *scratch_fmri;
 110 static char *g_zonename = NULL;
 111 static char svcstate[80];
 112 static boolean_t svcsearch = B_FALSE;
 113 
 114 static struct ht_elt **visited;
 115 
 116 void do_scfdie(int lineno) __NORETURN;
 117 static void usage_milestone(void) __NORETURN;
 118 static void set_astring_prop(const char *, const char *, const char *,
 119     uint32_t, const char *, const char *);
 120 static void pr_warn(const char *format, ...);
 121 
 122 /*
 123  * Visitors from synch.c, needed for enable -s and disable -s.
 124  */
 125 extern int is_enabled(scf_instance_t *);
 126 extern int has_potential(scf_instance_t *, int);
 127 
 128 void
 129 do_scfdie(int lineno)
 130 {
 131         scf_error_t err;
 132 
 133         switch (err = scf_error()) {
 134         case SCF_ERROR_CONNECTION_BROKEN:
 135                 uu_die(gettext("Connection to repository server broken.  "
 136                     "Exiting.\n"));
 137                 /* NOTREACHED */
 138 
 139         case SCF_ERROR_BACKEND_READONLY:
 140                 uu_die(gettext("Repository is read-only.  Exiting.\n"));
 141                 /* NOTREACHED */
 142 
 143         default:
 144 #ifdef NDEBUG
 145                 uu_die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
 146                     scf_strerror(err));
 147 #else
 148                 uu_die("Unexpected libscf error on line %d: %s.\n", lineno,
 149                     scf_strerror(err));
 150 #endif
 151         }
 152 }
 153 
 154 #define scfdie()        do_scfdie(__LINE__)
 155 
 156 static void
 157 usage()
 158 {
 159         (void) fprintf(stderr, gettext(
 160         "Usage: %1$s [-S <state>] [-v] [-Z | -z zone] [cmd [args ... ]]\n\n"
 161         "\t%1$s enable [-rst] [<service> ...]\t- enable and online service(s)\n"
 162         "\t%1$s disable [-c comment] [-st] [<service> ...] - disable "
 163         "service(s)\n"
 164         "\t%1$s restart [-d] [<service> ...]\t- restart specified service(s)\n"
 165         "\t%1$s refresh [<service> ...]\t\t- re-read service configuration\n"
 166         "\t%1$s mark [-It] <state> [<service> ...] - set maintenance state\n"
 167         "\t%1$s clear [<service> ...]\t\t- clear maintenance state\n"
 168         "\t%1$s milestone [-d] <milestone>\t- advance to a service milestone\n"
 169         "\n\t"
 170         "Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n"
 171         "\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n"
 172         "\n"
 173         "\t%1$s <cmd> svc:/network/smtp:sendmail\n"
 174         "\t%1$s <cmd> network/smtp:sendmail\n"
 175         "\t%1$s <cmd> network/*mail\n"
 176         "\t%1$s <cmd> network/smtp\n"
 177         "\t%1$s <cmd> smtp:sendmail\n"
 178         "\t%1$s <cmd> smtp\n"
 179         "\t%1$s <cmd> sendmail\n"), uu_getpname());
 180 
 181         exit(UU_EXIT_USAGE);
 182 }
 183 
 184 
 185 /*
 186  * FMRI hash table for recursive enable.
 187  */
 188 
 189 static uint32_t
 190 hash_fmri(const char *str)
 191 {
 192         uint32_t h = 0, g;
 193         const char *p;
 194 
 195         /* Generic hash function from uts/common/os/modhash.c . */
 196         for (p = str; *p != '\0'; ++p) {
 197                 h = (h << 4) + *p;
 198                 if ((g = (h & 0xf0000000)) != 0) {
 199                         h ^= (g >> 24);
 200                         h ^= g;
 201                 }
 202         }
 203 
 204         return (h);
 205 }
 206 
 207 /*
 208  * Return 1 if str has been visited, 0 if it has not, and -1 if memory could not
 209  * be allocated.
 210  */
 211 static int
 212 visited_find_or_add(const char *str, struct ht_elt **hep)
 213 {
 214         uint32_t h;
 215         uint_t i;
 216         struct ht_elt *he;
 217 
 218         h = hash_fmri(str);
 219         i = h & (HT_BUCKETS - 1);
 220 
 221         for (he = visited[i]; he != NULL; he = he->next) {
 222                 if (strcmp(he->str, str) == 0) {
 223                         if (hep)
 224                                 *hep = he;
 225                         return (1);
 226                 }
 227         }
 228 
 229         he = malloc(offsetof(struct ht_elt, str) + strlen(str) + 1);
 230         if (he == NULL)
 231                 return (-1);
 232 
 233         (void) strcpy(he->str, str);
 234 
 235         he->next = visited[i];
 236         visited[i] = he;
 237 
 238         if (hep)
 239                 *hep = he;
 240         return (0);
 241 }
 242 
 243 
 244 /*
 245  * Returns 0, ECANCELED if pg is deleted, ENOENT if propname doesn't exist,
 246  * EINVAL if the property is not of boolean type or has no values, and E2BIG
 247  * if it has more than one value.  *bp is set if 0 or E2BIG is returned.
 248  */
 249 int
 250 get_bool_prop(scf_propertygroup_t *pg, const char *propname, uint8_t *bp)
 251 {
 252         scf_property_t *prop;
 253         scf_value_t *val;
 254         int ret;
 255 
 256         if ((prop = scf_property_create(h)) == NULL ||
 257             (val = scf_value_create(h)) == NULL)
 258                 scfdie();
 259 
 260         if (scf_pg_get_property(pg, propname, prop) != 0) {
 261                 switch (scf_error()) {
 262                 case SCF_ERROR_DELETED:
 263                         ret = ECANCELED;
 264                         goto out;
 265 
 266                 case SCF_ERROR_NOT_FOUND:
 267                         ret = ENOENT;
 268                         goto out;
 269 
 270                 case SCF_ERROR_NOT_SET:
 271                         assert(0);
 272                         abort();
 273                         /* NOTREACHED */
 274 
 275                 default:
 276                         scfdie();
 277                 }
 278         }
 279 
 280         if (scf_property_get_value(prop, val) == 0) {
 281                 ret = 0;
 282         } else {
 283                 switch (scf_error()) {
 284                 case SCF_ERROR_DELETED:
 285                         ret = ENOENT;
 286                         goto out;
 287 
 288                 case SCF_ERROR_NOT_FOUND:
 289                         ret = EINVAL;
 290                         goto out;
 291 
 292                 case SCF_ERROR_CONSTRAINT_VIOLATED:
 293                         ret = E2BIG;
 294                         break;
 295 
 296                 case SCF_ERROR_NOT_SET:
 297                         assert(0);
 298                         abort();
 299                         /* NOTREACHED */
 300 
 301                 default:
 302                         scfdie();
 303                 }
 304         }
 305 
 306         if (scf_value_get_boolean(val, bp) != 0) {
 307                 if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
 308                         scfdie();
 309 
 310                 ret = EINVAL;
 311                 goto out;
 312         }
 313 
 314 out:
 315         scf_value_destroy(val);
 316         scf_property_destroy(prop);
 317         return (ret);
 318 }
 319 
 320 /*
 321  * Returns 0, EPERM, or EROFS.
 322  */
 323 static int
 324 set_bool_prop(scf_propertygroup_t *pg, const char *propname, boolean_t b)
 325 {
 326         scf_value_t *v;
 327         scf_transaction_t *tx;
 328         scf_transaction_entry_t *ent;
 329         int ret = 0, r;
 330 
 331         if ((tx = scf_transaction_create(h)) == NULL ||
 332             (ent = scf_entry_create(h)) == NULL ||
 333             (v = scf_value_create(h)) == NULL)
 334                 scfdie();
 335 
 336         scf_value_set_boolean(v, b);
 337 
 338         for (;;) {
 339                 if (scf_transaction_start(tx, pg) == -1) {
 340                         switch (scf_error()) {
 341                         case SCF_ERROR_PERMISSION_DENIED:
 342                                 ret = EPERM;
 343                                 goto out;
 344 
 345                         case SCF_ERROR_BACKEND_READONLY:
 346                                 ret = EROFS;
 347                                 goto out;
 348 
 349                         default:
 350                                 scfdie();
 351                         }
 352                 }
 353 
 354                 if (scf_transaction_property_change_type(tx, ent, propname,
 355                     SCF_TYPE_BOOLEAN) != 0) {
 356                         if (scf_error() != SCF_ERROR_NOT_FOUND)
 357                                 scfdie();
 358 
 359                         if (scf_transaction_property_new(tx, ent, propname,
 360                             SCF_TYPE_BOOLEAN) != 0)
 361                                 scfdie();
 362                 }
 363 
 364                 r = scf_entry_add_value(ent, v);
 365                 assert(r == 0);
 366 
 367                 r = scf_transaction_commit(tx);
 368                 if (r == 1)
 369                         break;
 370 
 371                 scf_transaction_reset(tx);
 372 
 373                 if (r != 0) {
 374                         switch (scf_error()) {
 375                         case SCF_ERROR_PERMISSION_DENIED:
 376                                 ret = EPERM;
 377                                 goto out;
 378 
 379                         case SCF_ERROR_BACKEND_READONLY:
 380                                 ret = EROFS;
 381                                 goto out;
 382 
 383                         default:
 384                                 scfdie();
 385                         }
 386                 }
 387 
 388                 if (scf_pg_update(pg) == -1)
 389                         scfdie();
 390         }
 391 
 392 out:
 393         scf_transaction_destroy(tx);
 394         scf_entry_destroy(ent);
 395         scf_value_destroy(v);
 396         return (ret);
 397 }
 398 
 399 /*
 400  * Gets the single astring value of the propname property of pg.  prop & v are
 401  * scratch space.  Returns the length of the string on success or
 402  *   -ENOENT - pg has no property named propname
 403  *   -E2BIG - property has no values or multiple values
 404  *   -EINVAL - property type is not compatible with astring
 405  */
 406 ssize_t
 407 get_astring_prop(const scf_propertygroup_t *pg, const char *propname,
 408     scf_property_t *prop, scf_value_t *v, char *buf, size_t bufsz)
 409 {
 410         ssize_t sz;
 411 
 412         if (scf_pg_get_property(pg, propname, prop) != 0) {
 413                 if (scf_error() != SCF_ERROR_NOT_FOUND)
 414                         scfdie();
 415 
 416                 return (-ENOENT);
 417         }
 418 
 419         if (scf_property_get_value(prop, v) != 0) {
 420                 switch (scf_error()) {
 421                 case SCF_ERROR_NOT_FOUND:
 422                 case SCF_ERROR_CONSTRAINT_VIOLATED:
 423                         return (-E2BIG);
 424 
 425                 default:
 426                         scfdie();
 427                 }
 428         }
 429 
 430         sz = scf_value_get_astring(v, buf, bufsz);
 431         if (sz < 0) {
 432                 if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
 433                         scfdie();
 434 
 435                 return (-EINVAL);
 436         }
 437 
 438         return (sz);
 439 }
 440 
 441 /*
 442  * Returns 0 or EPERM.
 443  */
 444 static int
 445 pg_get_or_add(const scf_instance_t *inst, const char *pgname,
 446     const char *pgtype, uint32_t pgflags, scf_propertygroup_t *pg)
 447 {
 448 again:
 449         if (scf_instance_get_pg(inst, pgname, pg) == 0)
 450                 return (0);
 451 
 452         if (scf_error() != SCF_ERROR_NOT_FOUND)
 453                 scfdie();
 454 
 455         if (scf_instance_add_pg(inst, pgname, pgtype, pgflags, pg) == 0)
 456                 return (0);
 457 
 458         switch (scf_error()) {
 459         case SCF_ERROR_EXISTS:
 460                 goto again;
 461 
 462         case SCF_ERROR_PERMISSION_DENIED:
 463                 return (EPERM);
 464 
 465         default:
 466                 scfdie();
 467                 /* NOTREACHED */
 468         }
 469 }
 470 
 471 static int
 472 my_ct_name(char *out, size_t len)
 473 {
 474         ct_stathdl_t st;
 475         char *ct_fmri;
 476         ctid_t ct;
 477         int fd, errno, ret;
 478 
 479         if ((ct = getctid()) == -1)
 480                 uu_die(gettext("Could not get contract id for process"));
 481 
 482         fd = contract_open(ct, "process", "status", O_RDONLY);
 483 
 484         if ((errno = ct_status_read(fd, CTD_ALL, &st)) != 0)
 485                 uu_warn(gettext("Could not read status of contract "
 486                     "%ld: %s.\n"), ct, strerror(errno));
 487 
 488         if ((errno = ct_pr_status_get_svc_fmri(st, &ct_fmri)) != 0)
 489                 uu_warn(gettext("Could not get svc_fmri for contract "
 490                     "%ld: %s.\n"), ct, strerror(errno));
 491 
 492         ret = strlcpy(out, ct_fmri, len);
 493 
 494         ct_status_free(st);
 495         (void) close(fd);
 496 
 497         return (ret);
 498 }
 499 
 500 /*
 501  * Set auxiliary_tty and auxiliary_fmri properties in restarter_actions pg to
 502  * communicate whether the action is requested from a tty and the fmri of the
 503  * responsible process.
 504  *
 505  * Returns 0, EPERM, or EROFS
 506  */
 507 static int
 508 restarter_setup(const char *fmri, const scf_instance_t *inst)
 509 {
 510         boolean_t b = B_FALSE;
 511         scf_propertygroup_t *pg = NULL;
 512         int ret = 0;
 513 
 514         if ((pg = scf_pg_create(h)) == NULL)
 515                 scfdie();
 516 
 517         if (pg_get_or_add(inst, SCF_PG_RESTARTER_ACTIONS,
 518             SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
 519             pg) == EPERM) {
 520                 if (!verbose)
 521                         uu_warn(emsg_permission_denied, fmri);
 522                 else
 523                         uu_warn(emsg_create_pg_perm_denied, fmri,
 524                             SCF_PG_RESTARTER_ACTIONS);
 525 
 526                 ret = EPERM;
 527                 goto out;
 528         }
 529 
 530         /* Set auxiliary_tty property */
 531         if (isatty(STDIN_FILENO))
 532                 b = B_TRUE;
 533 
 534         /* Create and set state to disabled */
 535         switch (set_bool_prop(pg, SCF_PROPERTY_AUX_TTY, b)) {
 536         case 0:
 537                 break;
 538 
 539         case EPERM:
 540                 if (!verbose)
 541                         uu_warn(emsg_permission_denied, fmri);
 542                 else
 543                         uu_warn(emsg_prop_perm_denied, fmri,
 544                             SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_TTY);
 545 
 546                 ret = EPERM;
 547                 goto out;
 548                 /* NOTREACHED */
 549 
 550         case EROFS:
 551                 /* Shouldn't happen, but it can. */
 552                 if (!verbose)
 553                         uu_warn(gettext("%s: Repository read-only.\n"), fmri);
 554                 else
 555                         uu_warn(gettext("%s: Could not set %s/%s "
 556                             "(repository read-only).\n"), fmri,
 557                             SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_TTY);
 558 
 559                 ret = EROFS;
 560                 goto out;
 561                 /* NOTREACHED */
 562 
 563         default:
 564                 scfdie();
 565         }
 566 
 567         if (my_ct_name(scratch_fmri, max_scf_fmri_sz) > 0) {
 568                 set_astring_prop(fmri, SCF_PG_RESTARTER_ACTIONS,
 569                     SCF_PG_RESTARTER_ACTIONS_TYPE,
 570                     SCF_PG_RESTARTER_ACTIONS_FLAGS,
 571                     SCF_PROPERTY_AUX_FMRI, scratch_fmri);
 572         } else {
 573                 uu_warn(gettext("%s: Could not set %s/%s: "
 574                     "my_ct_name failed.\n"), fmri,
 575                     SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_FMRI);
 576         }
 577 
 578 out:
 579         scf_pg_destroy(pg);
 580         return (ret);
 581 }
 582 
 583 static int
 584 delete_prop(const char *fmri, scf_instance_t *inst, const char *pgname,
 585     const char *propname)
 586 {
 587         int r = scf_instance_delete_prop(inst, pgname, propname);
 588 
 589         switch (r) {
 590         case 0:
 591                 break;
 592 
 593         case ECANCELED:
 594                 uu_warn(emsg_no_service, fmri);
 595                 break;
 596 
 597         case EACCES:
 598                 uu_warn(gettext("Could not delete %s/%s "
 599                     "property of %s: backend access denied.\n"),
 600                     pgname, propname, fmri);
 601                 break;
 602 
 603         case EROFS:
 604                 uu_warn(gettext("Could not delete %s/%s "
 605                     "property of %s: backend is read-only.\n"),
 606                     pgname, propname, fmri);
 607                 break;
 608 
 609         default:
 610                 bad_error("scf_instance_delete_prop", r);
 611         }
 612 
 613         return (r);
 614 }
 615 
 616 /*
 617  * Returns 0, EPERM, or EROFS.
 618  */
 619 static int
 620 set_enabled_props(scf_propertygroup_t *pg, enable_data_t *ed)
 621 {
 622         scf_transaction_entry_t *ent1;
 623         scf_transaction_entry_t *ent2;
 624         scf_transaction_t *tx;
 625         scf_value_t *v2;
 626         scf_value_t *v1;
 627         int ret = 0, r;
 628 
 629         if ((tx = scf_transaction_create(h)) == NULL ||
 630             (ent1 = scf_entry_create(h)) == NULL ||
 631             (ent2 = scf_entry_create(h)) == NULL ||
 632             (v1 = scf_value_create(h)) == NULL ||
 633             (v2 = scf_value_create(h)) == NULL)
 634                 scfdie();
 635 
 636         for (;;) {
 637                 if (scf_transaction_start(tx, pg) == -1) {
 638                         switch (scf_error()) {
 639                         case SCF_ERROR_PERMISSION_DENIED:
 640                                 ret = EPERM;
 641                                 goto out;
 642 
 643                         case SCF_ERROR_BACKEND_READONLY:
 644                                 ret = EROFS;
 645                                 goto out;
 646 
 647                         default:
 648                                 scfdie();
 649                         }
 650                 }
 651 
 652                 if (scf_transaction_property_change_type(tx, ent1,
 653                     SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN) != 0) {
 654                         if (scf_error() != SCF_ERROR_NOT_FOUND)
 655                                 scfdie();
 656 
 657                         if (scf_transaction_property_new(tx, ent1,
 658                             SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN) != 0)
 659                                 scfdie();
 660                 }
 661 
 662                 scf_value_set_boolean(v1, !!(ed->ed_flags & SET_ENABLED));
 663 
 664                 r = scf_entry_add_value(ent1, v1);
 665                 assert(r == 0);
 666 
 667                 if (scf_transaction_property_change_type(tx, ent2,
 668                     SCF_PROPERTY_COMMENT, SCF_TYPE_ASTRING) != 0) {
 669                         if (scf_error() != SCF_ERROR_NOT_FOUND)
 670                                 scfdie();
 671 
 672                         if (scf_transaction_property_new(tx, ent2,
 673                             SCF_PROPERTY_COMMENT, SCF_TYPE_ASTRING) != 0)
 674                                 scfdie();
 675                 }
 676 
 677                 if (scf_value_set_astring(v2, ed->ed_comment) != SCF_SUCCESS)
 678                         scfdie();
 679 
 680                 if (scf_entry_add_value(ent2, v2) != SCF_SUCCESS)
 681                         scfdie();
 682 
 683                 r = scf_transaction_commit(tx);
 684                 if (r == 1)
 685                         break;
 686 
 687                 scf_transaction_reset(tx);
 688 
 689                 if (r != 0) {
 690                         switch (scf_error()) {
 691                         case SCF_ERROR_PERMISSION_DENIED:
 692                                 ret = EPERM;
 693                                 goto out;
 694 
 695                         case SCF_ERROR_BACKEND_READONLY:
 696                                 ret = EROFS;
 697                                 goto out;
 698 
 699                         default:
 700                                 scfdie();
 701                         }
 702                 }
 703 
 704                 if (scf_pg_update(pg) == -1)
 705                         scfdie();
 706         }
 707 
 708 out:
 709         scf_transaction_destroy(tx);
 710         scf_entry_destroy(ent1);
 711         scf_entry_destroy(ent2);
 712         scf_value_destroy(v1);
 713         scf_value_destroy(v2);
 714         return (ret);
 715 }
 716 
 717 /*
 718  * Enable or disable an instance.  SET_TEMPORARY modifications apply to
 719  * general_ovr/ property group.
 720  */
 721 static void
 722 set_inst_enabled(const char *fmri, scf_instance_t *inst, enable_data_t *ed)
 723 {
 724         scf_propertygroup_t *pg;
 725         uint8_t b;
 726         const char *pgname = NULL;      /* For emsg_pg_perm_denied */
 727 
 728         pg = scf_pg_create(h);
 729         if (pg == NULL)
 730                 scfdie();
 731 
 732         if (restarter_setup(fmri, inst))
 733                 goto out;
 734 
 735         /*
 736          * An instance's configuration is incomplete if general/enabled
 737          * doesn't exist. Create both the property group and property
 738          * here if they don't exist.
 739          */
 740         pgname = SCF_PG_GENERAL;
 741         if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE,
 742             SCF_PG_GENERAL_FLAGS, pg) != 0)
 743                 goto eperm;
 744 
 745         if (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b) != 0) {
 746                 /* Create and set state to disabled */
 747                 switch (set_bool_prop(pg, SCF_PROPERTY_ENABLED, B_FALSE)) {
 748                 case 0:
 749                         break;
 750 
 751                 case EPERM:
 752                         goto eperm;
 753 
 754                 case EROFS:
 755                         /* Shouldn't happen, but it can. */
 756                         if (!verbose)
 757                                 uu_warn(gettext("%s: Repository read-only.\n"),
 758                                     fmri);
 759                         else
 760                                 uu_warn(gettext("%s: Could not set %s/%s "
 761                                     "(repository read-only).\n"), fmri,
 762                                     SCF_PG_GENERAL, SCF_PROPERTY_ENABLED);
 763                         goto out;
 764 
 765                 default:
 766                         assert(0);
 767                         abort();
 768                 }
 769         }
 770 
 771         if (ed->ed_flags & SET_TEMPORARY) {
 772                 pgname = SCF_PG_GENERAL_OVR;
 773                 if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_OVR_TYPE,
 774                     SCF_PG_GENERAL_OVR_FLAGS, pg) != 0)
 775                         goto eperm;
 776 
 777                 switch (set_enabled_props(pg, ed)) {
 778                 case 0:
 779                         break;
 780 
 781                 case EPERM:
 782                         goto eperm;
 783 
 784                 case EROFS:
 785                         /* Shouldn't happen, but it can. */
 786                         if (!verbose)
 787                                 uu_warn(gettext("%s: Repository read-only.\n"),
 788                                     fmri);
 789                         else
 790                                 uu_warn(gettext("%s: Could not set %s/%s "
 791                                     "(repository read-only).\n"), fmri,
 792                                     SCF_PG_GENERAL_OVR, SCF_PROPERTY_ENABLED);
 793                         goto out;
 794 
 795                 default:
 796                         assert(0);
 797                         abort();
 798                 }
 799 
 800                 if (verbose)
 801                         (void) printf((ed->ed_flags & SET_ENABLED) ?
 802                             gettext("%s temporarily enabled.\n") :
 803                             gettext("%s temporarily disabled.\n"), fmri);
 804         } else {
 805 again:
 806                 /*
 807                  * Both pg and property should exist since we created
 808                  * them earlier. However, there's still a chance that
 809                  * someone may have deleted the property out from under
 810                  * us.
 811                  */
 812                 if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE,
 813                     SCF_PG_GENERAL_FLAGS, pg) != 0)
 814                         goto eperm;
 815 
 816                 switch (set_enabled_props(pg, ed)) {
 817                 case 0:
 818                         break;
 819 
 820                 case EPERM:
 821                         goto eperm;
 822 
 823                 case EROFS:
 824                         /*
 825                          * If general/enabled is already set the way we want,
 826                          * proceed.
 827                          */
 828                         switch (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b)) {
 829                         case 0:
 830                                 if (!(b) == !(ed->ed_flags & SET_ENABLED))
 831                                         break;
 832                                 /* FALLTHROUGH */
 833 
 834                         case ENOENT:
 835                         case EINVAL:
 836                         case E2BIG:
 837                                 if (!verbose)
 838                                         uu_warn(gettext("%s: Repository "
 839                                             "read-only.\n"), fmri);
 840                                 else
 841                                         uu_warn(gettext("%s: Could not set "
 842                                             "%s/%s (repository read-only).\n"),
 843                                             fmri, SCF_PG_GENERAL,
 844                                             SCF_PROPERTY_ENABLED);
 845                                 goto out;
 846 
 847                         case ECANCELED:
 848                                 goto again;
 849 
 850                         default:
 851                                 assert(0);
 852                                 abort();
 853                         }
 854                         break;
 855 
 856                 default:
 857                         assert(0);
 858                         abort();
 859                 }
 860 
 861                 switch (delete_prop(fmri, inst, SCF_PG_GENERAL_OVR,
 862                     SCF_PROPERTY_ENABLED)) {
 863                 case 0:
 864                         break;
 865 
 866                 case EPERM:
 867                         goto eperm;
 868 
 869                 default:
 870                         goto out;
 871                 }
 872 
 873                 switch (delete_prop(fmri, inst, SCF_PG_GENERAL_OVR,
 874                     SCF_PROPERTY_COMMENT)) {
 875                 case 0:
 876                         break;
 877 
 878                 case EPERM:
 879                         goto eperm;
 880 
 881                 default:
 882                         goto out;
 883                 }
 884 
 885                 if (verbose) {
 886                         (void) printf((ed->ed_flags & SET_ENABLED) ?
 887                             gettext("%s enabled.\n") :
 888                             gettext("%s disabled.\n"), fmri);
 889                 }
 890         }
 891 
 892         scf_pg_destroy(pg);
 893         return;
 894 
 895 eperm:
 896         assert(pgname != NULL);
 897         if (!verbose)
 898                 uu_warn(emsg_permission_denied, fmri);
 899         else
 900                 uu_warn(emsg_pg_perm_denied, fmri, pgname);
 901 
 902 out:
 903         scf_pg_destroy(pg);
 904         exit_status = 1;
 905 }
 906 
 907 /*
 908  * Set inst to the instance which corresponds to fmri.  If fmri identifies
 909  * a service with a single instance, get that instance.
 910  *
 911  * Fails with
 912  *   ENOTSUP - fmri has an unsupported scheme
 913  *   EINVAL - fmri is invalid
 914  *   ENOTDIR - fmri does not identify a service or instance
 915  *   ENOENT - could not locate instance
 916  *   E2BIG - fmri is a service with multiple instances (warning not printed)
 917  */
 918 static int
 919 get_inst_mult(const char *fmri, scf_instance_t *inst)
 920 {
 921         char *cfmri;
 922         const char *svc_name, *inst_name, *pg_name;
 923         scf_service_t *svc;
 924         scf_instance_t *inst2;
 925         scf_iter_t *iter;
 926         int ret;
 927 
 928         if (strncmp(fmri, "lrc:", sizeof ("lrc:") - 1) == 0) {
 929                 uu_warn(gettext("FMRI \"%s\" is a legacy service.\n"), fmri);
 930                 exit_status = 1;
 931                 return (ENOTSUP);
 932         }
 933 
 934         cfmri = strdup(fmri);
 935         if (cfmri == NULL)
 936                 uu_die(emsg_nomem);
 937 
 938         if (scf_parse_svc_fmri(cfmri, NULL, &svc_name, &inst_name, &pg_name,
 939             NULL) != SCF_SUCCESS) {
 940                 free(cfmri);
 941                 uu_warn(gettext("FMRI \"%s\" is invalid.\n"), fmri);
 942                 exit_status = 1;
 943                 return (EINVAL);
 944         }
 945 
 946         free(cfmri);
 947 
 948         if (svc_name == NULL || pg_name != NULL) {
 949                 uu_warn(gettext(
 950                     "FMRI \"%s\" does not designate a service or instance.\n"),
 951                     fmri);
 952                 exit_status = 1;
 953                 return (ENOTDIR);
 954         }
 955 
 956         if (inst_name != NULL) {
 957                 if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
 958                     NULL, SCF_DECODE_FMRI_EXACT) == 0)
 959                         return (0);
 960 
 961                 if (scf_error() != SCF_ERROR_NOT_FOUND)
 962                         scfdie();
 963 
 964                 uu_warn(gettext("No such instance \"%s\".\n"), fmri);
 965                 exit_status = 1;
 966 
 967                 return (ENOENT);
 968         }
 969 
 970         if ((svc = scf_service_create(h)) == NULL ||
 971             (inst2 = scf_instance_create(h)) == NULL ||
 972             (iter = scf_iter_create(h)) == NULL)
 973                 scfdie();
 974 
 975         if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL,
 976             SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
 977                 if (scf_error() != SCF_ERROR_NOT_FOUND)
 978                         scfdie();
 979 
 980                 uu_warn(emsg_no_service, fmri);
 981                 exit_status = 1;
 982 
 983                 ret = ENOENT;
 984                 goto out;
 985         }
 986 
 987         /* If the service has only one child, use it. */
 988         if (scf_iter_service_instances(iter, svc) != SCF_SUCCESS)
 989                 scfdie();
 990 
 991         ret = scf_iter_next_instance(iter, inst);
 992         if (ret < 0)
 993                 scfdie();
 994         if (ret != 1) {
 995                 uu_warn(gettext("Service \"%s\" has no instances.\n"),
 996                     fmri);
 997                 exit_status = 1;
 998                 ret = ENOENT;
 999                 goto out;
1000         }
1001 
1002         ret = scf_iter_next_instance(iter, inst2);
1003         if (ret < 0)
1004                 scfdie();
1005 
1006         if (ret != 0) {
1007                 ret = E2BIG;
1008                 goto out;
1009         }
1010 
1011         ret = 0;
1012 
1013 out:
1014         scf_iter_destroy(iter);
1015         scf_instance_destroy(inst2);
1016         scf_service_destroy(svc);
1017         return (ret);
1018 }
1019 
1020 /*
1021  * Same as get_inst_mult(), but on E2BIG prints a warning and returns ENOENT.
1022  */
1023 static int
1024 get_inst(const char *fmri, scf_instance_t *inst)
1025 {
1026         int r;
1027 
1028         r = get_inst_mult(fmri, inst);
1029         if (r != E2BIG)
1030                 return (r);
1031 
1032         uu_warn(gettext("operation on service %s is ambiguous; "
1033             "instance specification needed.\n"), fmri);
1034         return (ENOENT);
1035 }
1036 
1037 static char *
1038 inst_get_fmri(const scf_instance_t *inst)
1039 {
1040         ssize_t sz;
1041 
1042         sz = scf_instance_to_fmri(inst, scratch_fmri, max_scf_fmri_sz);
1043         if (sz < 0)
1044                 scfdie();
1045         if (sz >= max_scf_fmri_sz)
1046                 uu_die(gettext("scf_instance_to_fmri() returned unexpectedly "
1047                     "long value.\n"));
1048 
1049         return (scratch_fmri);
1050 }
1051 
1052 static ssize_t
1053 dep_get_astring(const char *fmri, const char *pgname,
1054     const scf_propertygroup_t *pg, const char *propname, scf_property_t *prop,
1055     scf_value_t *v, char *buf, size_t bufsz)
1056 {
1057         ssize_t sz;
1058 
1059         sz = get_astring_prop(pg, propname, prop, v, buf, bufsz);
1060         if (sz >= 0)
1061                 return (sz);
1062 
1063         switch (-sz) {
1064         case ENOENT:
1065                 uu_warn(gettext("\"%s\" is misconfigured (\"%s\" dependency "
1066                     "lacks \"%s\" property.)\n"), fmri, pgname, propname);
1067                 return (-1);
1068 
1069         case E2BIG:
1070                 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" property "
1071                     "is not single-valued.)\n"), fmri, pgname, propname);
1072                 return (-1);
1073 
1074         case EINVAL:
1075                 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" property "
1076                     "is not of astring type.)\n"), fmri, pgname, propname);
1077                 return (-1);
1078 
1079         default:
1080                 assert(0);
1081                 abort();
1082                 /* NOTREACHED */
1083         }
1084 }
1085 
1086 static boolean_t
1087 multiple_instances(scf_iter_t *iter, scf_value_t *v, char *buf)
1088 {
1089         int count = 0, r;
1090         boolean_t ret;
1091         scf_instance_t *inst;
1092 
1093         inst = scf_instance_create(h);
1094         if (inst == NULL)
1095                 scfdie();
1096 
1097         for (;;) {
1098                 r = scf_iter_next_value(iter, v);
1099                 if (r == 0) {
1100                         ret = B_FALSE;
1101                         goto out;
1102                 }
1103                 if (r != 1)
1104                         scfdie();
1105 
1106                 if (scf_value_get_astring(v, buf, max_scf_fmri_sz) < 0)
1107                         scfdie();
1108 
1109                 switch (get_inst_mult(buf, inst)) {
1110                 case 0:
1111                         ++count;
1112                         if (count > 1) {
1113                                 ret = B_TRUE;
1114                                 goto out;
1115                         }
1116                         break;
1117 
1118                 case ENOTSUP:
1119                 case EINVAL:
1120                 case ENOTDIR:
1121                 case ENOENT:
1122                         continue;
1123 
1124                 case E2BIG:
1125                         ret = B_TRUE;
1126                         goto out;
1127 
1128                 default:
1129                         assert(0);
1130                         abort();
1131                 }
1132         }
1133 
1134 out:
1135         scf_instance_destroy(inst);
1136         return (ret);
1137 }
1138 
1139 /*
1140  * Enable the service or instance identified by fmri and its dependencies,
1141  * recursively.  Specifically, call get_inst(fmri), enable the result, and
1142  * recurse on its restarter and the dependencies.  To avoid duplication of
1143  * effort or looping around a dependency cycle, each FMRI is entered into the
1144  * "visited" hash table.  While recursing, the hash table entry is marked
1145  * "active", so that if we come upon it again, we know we've hit a cycle.
1146  * exclude_all and optional_all dependencies are ignored.  require_any
1147  * dependencies are followed only if they comprise a single service; otherwise
1148  * the user is warned.
1149  *
1150  * fmri must point to a writable max_scf_fmri_sz buffer.  Returns EINVAL if fmri
1151  * is invalid, E2BIG if fmri identifies a service with multiple instances, ELOOP
1152  * on cycle detection, or 0 on success.
1153  */
1154 static int
1155 enable_fmri_rec(char *fmri, enable_data_t *ed)
1156 {
1157         scf_instance_t *inst;
1158         scf_snapshot_t *snap;
1159         scf_propertygroup_t *pg;
1160         scf_property_t *prop;
1161         scf_value_t *v;
1162         scf_iter_t *pg_iter, *val_iter;
1163         scf_type_t ty;
1164         char *buf, *pgname;
1165         ssize_t name_sz, len, sz;
1166         int ret;
1167         struct ht_elt *he;
1168 
1169         len = scf_canonify_fmri(fmri, fmri, max_scf_fmri_sz);
1170         if (len < 0) {
1171                 assert(scf_error() == SCF_ERROR_INVALID_ARGUMENT);
1172                 return (EINVAL);
1173         }
1174         assert(len < max_scf_fmri_sz);
1175 
1176         switch (visited_find_or_add(fmri, &he)) {
1177         case 0:
1178                 he->active = B_TRUE;
1179                 break;
1180 
1181         case 1:
1182                 return (he->active ? ELOOP : 0);
1183 
1184         case -1:
1185                 uu_die(emsg_nomem);
1186 
1187         default:
1188                 assert(0);
1189                 abort();
1190         }
1191 
1192         inst = scf_instance_create(h);
1193         if (inst == NULL)
1194                 scfdie();
1195 
1196         switch (get_inst_mult(fmri, inst)) {
1197         case 0:
1198                 break;
1199 
1200         case E2BIG:
1201                 he->active = B_FALSE;
1202                 return (E2BIG);
1203 
1204         default:
1205                 he->active = B_FALSE;
1206                 return (0);
1207         }
1208 
1209         set_inst_enabled(fmri, inst, ed);
1210 
1211         if ((snap = scf_snapshot_create(h)) == NULL ||
1212             (pg = scf_pg_create(h)) == NULL ||
1213             (prop = scf_property_create(h)) == NULL ||
1214             (v = scf_value_create(h)) == NULL ||
1215             (pg_iter = scf_iter_create(h)) == NULL ||
1216             (val_iter = scf_iter_create(h)) == NULL)
1217                 scfdie();
1218 
1219         buf = malloc(max_scf_fmri_sz);
1220         if (buf == NULL)
1221                 uu_die(emsg_nomem);
1222 
1223         name_sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
1224         if (name_sz < 0)
1225                 scfdie();
1226         ++name_sz;
1227         pgname = malloc(name_sz);
1228         if (pgname == NULL)
1229                 uu_die(emsg_nomem);
1230 
1231         if (scf_instance_get_snapshot(inst, "running", snap) != 0) {
1232                 if (scf_error() != SCF_ERROR_NOT_FOUND)
1233                         scfdie();
1234 
1235                 scf_snapshot_destroy(snap);
1236                 snap = NULL;
1237         }
1238 
1239         /* Enable restarter */
1240         if (scf_instance_get_pg_composed(inst, snap, SCF_PG_GENERAL, pg) != 0) {
1241                 if (scf_error() != SCF_ERROR_NOT_FOUND)
1242                         scfdie();
1243 
1244                 uu_warn(gettext("\"%s\" is misconfigured (lacks \"%s\" "
1245                     "property group).\n"), fmri, SCF_PG_GENERAL);
1246                 ret = 0;
1247                 goto out;
1248         }
1249 
1250         sz = get_astring_prop(pg, SCF_PROPERTY_RESTARTER, prop, v, buf,
1251             max_scf_fmri_sz);
1252         if (sz > max_scf_fmri_sz) {
1253                 uu_warn(gettext("\"%s\" is misconfigured (the value of "
1254                     "\"%s/%s\" is too long).\n"), fmri, SCF_PG_GENERAL,
1255                     SCF_PROPERTY_RESTARTER);
1256                 ret = 0;
1257                 goto out;
1258         } else if (sz >= 0) {
1259                 switch (enable_fmri_rec(buf, ed)) {
1260                 case 0:
1261                         break;
1262 
1263                 case EINVAL:
1264                         uu_warn(gettext("Restarter FMRI for \"%s\" is "
1265                             "invalid.\n"), fmri);
1266                         break;
1267 
1268                 case E2BIG:
1269                         uu_warn(gettext("Restarter FMRI for \"%s\" identifies "
1270                             "a service with multiple instances.\n"), fmri);
1271                         break;
1272 
1273                 case ELOOP:
1274                         ret = ELOOP;
1275                         goto out;
1276 
1277                 default:
1278                         assert(0);
1279                         abort();
1280                 }
1281         } else if (sz < 0) {
1282                 switch (-sz) {
1283                 case ENOENT:
1284                         break;
1285 
1286                 case E2BIG:
1287                         uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" "
1288                             "property is not single-valued).\n"), fmri,
1289                             SCF_PG_GENERAL, SCF_PROPERTY_RESTARTER);
1290                         ret = 0;
1291                         goto out;
1292 
1293                 case EINVAL:
1294                         uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" "
1295                             "property is not of astring type).\n"), fmri,
1296                             SCF_PG_GENERAL, SCF_PROPERTY_RESTARTER);
1297                         ret = 0;
1298                         goto out;
1299 
1300                 default:
1301                         assert(0);
1302                         abort();
1303                 }
1304         }
1305 
1306         if (scf_iter_instance_pgs_typed_composed(pg_iter, inst, snap,
1307             SCF_GROUP_DEPENDENCY) == -1)
1308                 scfdie();
1309 
1310         while (scf_iter_next_pg(pg_iter, pg) > 0) {
1311                 len = scf_pg_get_name(pg, pgname, name_sz);
1312                 if (len < 0)
1313                         scfdie();
1314                 assert(len < name_sz);
1315 
1316                 if (dep_get_astring(fmri, pgname, pg, SCF_PROPERTY_TYPE, prop,
1317                     v, buf, max_scf_fmri_sz) < 0)
1318                         continue;
1319 
1320                 if (strcmp(buf, "service") != 0)
1321                         continue;
1322 
1323                 if (dep_get_astring(fmri, pgname, pg, SCF_PROPERTY_GROUPING,
1324                     prop, v, buf, max_scf_fmri_sz) < 0)
1325                         continue;
1326 
1327                 if (strcmp(buf, SCF_DEP_EXCLUDE_ALL) == 0 ||
1328                     strcmp(buf, SCF_DEP_OPTIONAL_ALL) == 0)
1329                         continue;
1330 
1331                 if (strcmp(buf, SCF_DEP_REQUIRE_ALL) != 0 &&
1332                     strcmp(buf, SCF_DEP_REQUIRE_ANY) != 0) {
1333                         uu_warn(gettext("Dependency \"%s\" of \"%s\" has "
1334                             "unknown type \"%s\".\n"), pgname, fmri, buf);
1335                         continue;
1336                 }
1337 
1338                 if (scf_pg_get_property(pg, SCF_PROPERTY_ENTITIES, prop) ==
1339                     -1) {
1340                         if (scf_error() != SCF_ERROR_NOT_FOUND)
1341                                 scfdie();
1342 
1343                         uu_warn(gettext("\"%s\" is misconfigured (\"%s\" "
1344                             "dependency lacks \"%s\" property.)\n"), fmri,
1345                             pgname, SCF_PROPERTY_ENTITIES);
1346                         continue;
1347                 }
1348 
1349                 if (scf_property_type(prop, &ty) != SCF_SUCCESS)
1350                         scfdie();
1351 
1352                 if (ty != SCF_TYPE_FMRI) {
1353                         uu_warn(gettext("\"%s\" is misconfigured (property "
1354                             "\"%s/%s\" is not of fmri type).\n"), fmri, pgname,
1355                             SCF_PROPERTY_ENTITIES);
1356                         continue;
1357                 }
1358 
1359                 if (scf_iter_property_values(val_iter, prop) == -1)
1360                         scfdie();
1361 
1362                 if (strcmp(buf, SCF_DEP_REQUIRE_ANY) == 0) {
1363                         if (multiple_instances(val_iter, v, buf)) {
1364                                 (void) printf(gettext("%s requires one of:\n"),
1365                                     fmri);
1366 
1367                                 if (scf_iter_property_values(val_iter, prop) !=
1368                                     0)
1369                                         scfdie();
1370 
1371                                 for (;;) {
1372                                         int r;
1373 
1374                                         r = scf_iter_next_value(val_iter, v);
1375                                         if (r == 0)
1376                                                 break;
1377                                         if (r != 1)
1378                                                 scfdie();
1379 
1380                                         if (scf_value_get_astring(v, buf,
1381                                             max_scf_fmri_sz) < 0)
1382                                                 scfdie();
1383 
1384                                         (void) fputs("  ", stdout);
1385                                         (void) puts(buf);
1386                                 }
1387 
1388                                 continue;
1389                         }
1390 
1391                         /*
1392                          * Since there's only one instance, we can enable it.
1393                          * Reset val_iter and continue.
1394                          */
1395                         if (scf_iter_property_values(val_iter, prop) != 0)
1396                                 scfdie();
1397                 }
1398 
1399                 for (;;) {
1400                         ret = scf_iter_next_value(val_iter, v);
1401                         if (ret == 0)
1402                                 break;
1403                         if (ret != 1)
1404                                 scfdie();
1405 
1406                         if (scf_value_get_astring(v, buf, max_scf_fmri_sz) ==
1407                             -1)
1408                                 scfdie();
1409 
1410                         switch (enable_fmri_rec(buf, ed)) {
1411                         case 0:
1412                                 break;
1413 
1414                         case EINVAL:
1415                                 uu_warn(gettext("\"%s\" dependency of \"%s\" "
1416                                     "has invalid FMRI \"%s\".\n"), pgname,
1417                                     fmri, buf);
1418                                 break;
1419 
1420                         case E2BIG:
1421                                 uu_warn(gettext("%s depends on %s, which has "
1422                                     "multiple instances.\n"), fmri, buf);
1423                                 break;
1424 
1425                         case ELOOP:
1426                                 ret = ELOOP;
1427                                 goto out;
1428 
1429                         default:
1430                                 assert(0);
1431                                 abort();
1432                         }
1433                 }
1434         }
1435 
1436         ret = 0;
1437 
1438 out:
1439         he->active = B_FALSE;
1440 
1441         free(buf);
1442         free(pgname);
1443 
1444         (void) scf_value_destroy(v);
1445         scf_property_destroy(prop);
1446         scf_pg_destroy(pg);
1447         scf_snapshot_destroy(snap);
1448         scf_iter_destroy(pg_iter);
1449         scf_iter_destroy(val_iter);
1450 
1451         return (ret);
1452 }
1453 
1454 /*
1455  * fmri here is only used for verbose messages.
1456  */
1457 static void
1458 set_inst_action(const char *fmri, const scf_instance_t *inst,
1459     const char *action)
1460 {
1461         scf_transaction_t *tx;
1462         scf_transaction_entry_t *ent;
1463         scf_propertygroup_t *pg;
1464         scf_property_t *prop;
1465         scf_value_t *v;
1466         int ret;
1467         int64_t t;
1468         hrtime_t timestamp;
1469 
1470         const char * const scf_pg_restarter_actions = SCF_PG_RESTARTER_ACTIONS;
1471 
1472         if ((pg = scf_pg_create(h)) == NULL ||
1473             (prop = scf_property_create(h)) == NULL ||
1474             (v = scf_value_create(h)) == NULL ||
1475             (tx = scf_transaction_create(h)) == NULL ||
1476             (ent = scf_entry_create(h)) == NULL)
1477                 scfdie();
1478 
1479         if (restarter_setup(fmri, inst)) {
1480                 exit_status = 1;
1481                 goto out;
1482         }
1483 
1484         if (scf_instance_get_pg(inst, scf_pg_restarter_actions, pg) == -1) {
1485                 if (scf_error() != SCF_ERROR_NOT_FOUND)
1486                         scfdie();
1487 
1488                 /* Try creating the restarter_actions property group. */
1489                 if (scf_instance_add_pg(inst, scf_pg_restarter_actions,
1490                     SCF_PG_RESTARTER_ACTIONS_TYPE,
1491                     SCF_PG_RESTARTER_ACTIONS_FLAGS, pg) == -1) {
1492                         switch (scf_error()) {
1493                         case SCF_ERROR_EXISTS:
1494                                 /* Someone must have added it. */
1495                                 break;
1496 
1497                         case SCF_ERROR_PERMISSION_DENIED:
1498                                 if (!verbose)
1499                                         uu_warn(emsg_permission_denied, fmri);
1500                                 else
1501                                         uu_warn(emsg_create_pg_perm_denied,
1502                                             fmri, scf_pg_restarter_actions);
1503                                 goto out;
1504 
1505                         default:
1506                                 scfdie();
1507                         }
1508                 }
1509         }
1510 
1511         /*
1512          * If we lose the transaction race and need to retry, there are 2
1513          * potential other winners:
1514          *      - another process setting actions
1515          *      - the restarter marking the action complete
1516          * Therefore, re-read the property every time through the loop before
1517          * making any decisions based on their values.
1518          */
1519         do {
1520                 timestamp = gethrtime();
1521 
1522                 if (scf_transaction_start(tx, pg) == -1) {
1523                         if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1524                                 scfdie();
1525 
1526                         if (!verbose)
1527                                 uu_warn(emsg_permission_denied, fmri);
1528                         else
1529                                 uu_warn(emsg_pg_perm_denied, fmri,
1530                                     scf_pg_restarter_actions);
1531                         goto out;
1532                 }
1533 
1534                 if (scf_pg_get_property(pg, action, prop) == -1) {
1535                         if (scf_error() != SCF_ERROR_NOT_FOUND)
1536                                 scfdie();
1537                         if (scf_transaction_property_new(tx, ent,
1538                             action, SCF_TYPE_INTEGER) == -1)
1539                                 scfdie();
1540                         goto action_set;
1541                 } else {
1542                         if (scf_transaction_property_change_type(tx, ent,
1543                             action, SCF_TYPE_INTEGER) == -1)
1544                                 scfdie();
1545                 }
1546 
1547                 if (scf_property_get_value(prop, v) == -1) {
1548                         switch (scf_error()) {
1549                         case SCF_ERROR_CONSTRAINT_VIOLATED:
1550                         case SCF_ERROR_NOT_FOUND:
1551                                 /* Misconfigured, so set anyway. */
1552                                 goto action_set;
1553 
1554                         default:
1555                                 scfdie();
1556                         }
1557                 } else {
1558                         if (scf_value_get_integer(v, &t) == -1) {
1559                                 assert(scf_error() == SCF_ERROR_TYPE_MISMATCH);
1560                                 goto action_set;
1561                         }
1562                         if (t > timestamp)
1563                                 break;
1564                 }
1565 
1566 action_set:
1567                 scf_value_set_integer(v, timestamp);
1568                 if (scf_entry_add_value(ent, v) == -1)
1569                         scfdie();
1570 
1571                 ret = scf_transaction_commit(tx);
1572                 if (ret == -1) {
1573                         if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1574                                 scfdie();
1575 
1576                         if (!verbose)
1577                                 uu_warn(emsg_permission_denied, fmri);
1578                         else
1579                                 uu_warn(emsg_prop_perm_denied, fmri,
1580                                     scf_pg_restarter_actions, action);
1581                         scf_transaction_reset(tx);
1582                         goto out;
1583                 }
1584 
1585                 scf_transaction_reset(tx);
1586 
1587                 if (ret == 0) {
1588                         if (scf_pg_update(pg) == -1)
1589                                 scfdie();
1590                 }
1591         } while (ret == 0);
1592 
1593         if (verbose)
1594                 (void) printf(gettext("Action %s set for %s.\n"), action, fmri);
1595 
1596 out:
1597         scf_value_destroy(v);
1598         scf_entry_destroy(ent);
1599         scf_transaction_destroy(tx);
1600         scf_property_destroy(prop);
1601         scf_pg_destroy(pg);
1602 }
1603 
1604 /*
1605  * Get the state of inst.  state should point to a buffer of
1606  * MAX_SCF_STATE_STRING_SZ bytes.  Returns 0 on success or -1 if
1607  *   no restarter property group
1608  *   no state property
1609  *   state property is misconfigured (wrong type, not single-valued)
1610  *   state value is too long
1611  * In these cases, fmri is used to print a warning.
1612  *
1613  * If pgp is non-NULL, a successful call to inst_get_state will store
1614  * the SCF_PG_RESTARTER property group in *pgp, and the caller will be
1615  * responsible for calling scf_pg_destroy on the property group.
1616  */
1617 int
1618 inst_get_state(scf_instance_t *inst, char *state, const char *fmri,
1619     scf_propertygroup_t **pgp)
1620 {
1621         scf_propertygroup_t *pg;
1622         scf_property_t *prop;
1623         scf_value_t *val;
1624         int ret = -1;
1625         ssize_t szret;
1626 
1627         if ((pg = scf_pg_create(h)) == NULL ||
1628             (prop = scf_property_create(h)) == NULL ||
1629             (val = scf_value_create(h)) == NULL)
1630                 scfdie();
1631 
1632         if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) != SCF_SUCCESS) {
1633                 if (scf_error() != SCF_ERROR_NOT_FOUND)
1634                         scfdie();
1635 
1636                 uu_warn(gettext("%s is misconfigured (lacks \"%s\" property "
1637                     "group).\n"), fmri ? fmri : inst_get_fmri(inst),
1638                     SCF_PG_RESTARTER);
1639                 goto out;
1640         }
1641 
1642         szret = get_astring_prop(pg, SCF_PROPERTY_STATE, prop, val, state,
1643             MAX_SCF_STATE_STRING_SZ);
1644         if (szret < 0) {
1645                 switch (-szret) {
1646                 case ENOENT:
1647                         uu_warn(gettext("%s is misconfigured (\"%s\" property "
1648                             "group lacks \"%s\" property).\n"),
1649                             fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER,
1650                             SCF_PROPERTY_STATE);
1651                         goto out;
1652 
1653                 case E2BIG:
1654                         uu_warn(gettext("%s is misconfigured (\"%s/%s\" "
1655                             "property is not single-valued).\n"),
1656                             fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER,
1657                             SCF_PROPERTY_STATE);
1658                         goto out;
1659 
1660                 case EINVAL:
1661                         uu_warn(gettext("%s is misconfigured (\"%s/%s\" "
1662                             "property is not of type astring).\n"),
1663                             fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER,
1664                             SCF_PROPERTY_STATE);
1665                         goto out;
1666 
1667                 default:
1668                         assert(0);
1669                         abort();
1670                 }
1671         }
1672         if (szret >= MAX_SCF_STATE_STRING_SZ) {
1673                 uu_warn(gettext("%s is misconfigured (\"%s/%s\" property value "
1674                     "is too long).\n"), fmri ? fmri : inst_get_fmri(inst),
1675                     SCF_PG_RESTARTER, SCF_PROPERTY_STATE);
1676                 goto out;
1677         }
1678 
1679         ret = 0;
1680         if (pgp)
1681                 *pgp = pg;
1682 
1683 out:
1684         (void) scf_value_destroy(val);
1685         scf_property_destroy(prop);
1686         if (ret || pgp == NULL)
1687                 scf_pg_destroy(pg);
1688         return (ret);
1689 }
1690 
1691 static void
1692 set_astring_prop(const char *fmri, const char *pgname, const char *pgtype,
1693     uint32_t pgflags, const char *propname, const char *str)
1694 {
1695         scf_instance_t *inst;
1696         scf_propertygroup_t *pg;
1697         scf_property_t *prop;
1698         scf_value_t *val;
1699         scf_transaction_t *tx;
1700         scf_transaction_entry_t *txent;
1701         int ret;
1702 
1703         inst = scf_instance_create(h);
1704         if (inst == NULL)
1705                 scfdie();
1706 
1707         if (get_inst(fmri, inst) != 0)
1708                 return;
1709 
1710         if ((pg = scf_pg_create(h)) == NULL ||
1711             (prop = scf_property_create(h)) == NULL ||
1712             (val = scf_value_create(h)) == NULL ||
1713             (tx = scf_transaction_create(h)) == NULL ||
1714             (txent = scf_entry_create(h)) == NULL)
1715                 scfdie();
1716 
1717         if (scf_instance_get_pg(inst, pgname, pg) != SCF_SUCCESS) {
1718                 if (scf_error() != SCF_ERROR_NOT_FOUND)
1719                         scfdie();
1720 
1721                 if (scf_instance_add_pg(inst, pgname, pgtype, pgflags, pg) !=
1722                     SCF_SUCCESS) {
1723                         switch (scf_error()) {
1724                         case SCF_ERROR_EXISTS:
1725                                 if (scf_instance_get_pg(inst, pgname, pg) !=
1726                                     SCF_SUCCESS) {
1727                                         if (scf_error() != SCF_ERROR_NOT_FOUND)
1728                                                 scfdie();
1729 
1730                                         uu_warn(gettext("Repository write "
1731                                             "contention.\n"));
1732                                         goto out;
1733                                 }
1734                                 break;
1735 
1736                         case SCF_ERROR_PERMISSION_DENIED:
1737                                 if (!verbose)
1738                                         uu_warn(emsg_permission_denied, fmri);
1739                                 else
1740                                         uu_warn(emsg_create_pg_perm_denied,
1741                                             fmri, pgname);
1742                                 goto out;
1743 
1744                         default:
1745                                 scfdie();
1746                         }
1747                 }
1748         }
1749 
1750         do {
1751                 if (scf_transaction_start(tx, pg) != SCF_SUCCESS) {
1752                         if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1753                                 scfdie();
1754 
1755                         if (!verbose)
1756                                 uu_warn(emsg_permission_denied, fmri);
1757                         else
1758                                 uu_warn(emsg_pg_perm_denied, fmri, pgname);
1759                         goto out;
1760                 }
1761 
1762                 if (scf_transaction_property_change_type(tx, txent, propname,
1763                     SCF_TYPE_ASTRING) != 0) {
1764                         if (scf_error() != SCF_ERROR_NOT_FOUND)
1765                                 scfdie();
1766 
1767                         if (scf_transaction_property_new(tx, txent, propname,
1768                             SCF_TYPE_ASTRING) != 0)
1769                                 scfdie();
1770                 }
1771 
1772                 if (scf_value_set_astring(val, str) != SCF_SUCCESS)
1773                         scfdie();
1774 
1775                 if (scf_entry_add_value(txent, val) != SCF_SUCCESS)
1776                         scfdie();
1777 
1778                 ret = scf_transaction_commit(tx);
1779                 if (ret == -1) {
1780                         if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1781                                 scfdie();
1782 
1783                         if (!verbose)
1784                                 uu_warn(emsg_permission_denied, fmri);
1785                         else
1786                                 uu_warn(emsg_prop_perm_denied, fmri, pgname,
1787                                     propname);
1788                         goto out;
1789                 }
1790 
1791                 if (ret == 0) {
1792                         scf_transaction_reset(tx);
1793 
1794                         if (scf_pg_update(pg) == -1)
1795                                 scfdie();
1796                 }
1797         } while (ret == 0);
1798 
1799 out:
1800         scf_transaction_destroy(tx);
1801         scf_entry_destroy(txent);
1802         scf_value_destroy(val);
1803         scf_property_destroy(prop);
1804         scf_pg_destroy(pg);
1805         scf_instance_destroy(inst);
1806 }
1807 
1808 
1809 static int
1810 set_fmri_enabled(void *data, scf_walkinfo_t *wip)
1811 {
1812         enable_data_t *ed = data;
1813 
1814         assert(wip->inst != NULL);
1815         assert(wip->pg == NULL);
1816 
1817         if (svcsearch) {
1818                 char state[MAX_SCF_STATE_STRING_SZ];
1819 
1820                 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
1821                         return (0);
1822                 if (strcmp(state, svcstate) != 0)
1823                         return (0);
1824         }
1825 
1826         if (ed->ed_flags & SET_RECURSIVE) {
1827                 char *fmri_buf = malloc(max_scf_fmri_sz);
1828                 if (fmri_buf == NULL)
1829                         uu_die(emsg_nomem);
1830 
1831                 visited = calloc(HT_BUCKETS, sizeof (*visited));
1832                 if (visited == NULL)
1833                         uu_die(emsg_nomem);
1834 
1835                 /* scf_walk_fmri() guarantees that fmri isn't too long */
1836                 assert(strlen(wip->fmri) <= max_scf_fmri_sz);
1837                 (void) strlcpy(fmri_buf, wip->fmri, max_scf_fmri_sz);
1838 
1839                 switch (enable_fmri_rec(fmri_buf, ed)) {
1840                 case E2BIG:
1841                         uu_warn(gettext("operation on service %s is ambiguous; "
1842                             "instance specification needed.\n"), fmri_buf);
1843                         break;
1844 
1845                 case ELOOP:
1846                         uu_warn(gettext("%s: Dependency cycle detected.\n"),
1847                             fmri_buf);
1848                 }
1849 
1850                 free(visited);
1851                 free(fmri_buf);
1852 
1853         } else {
1854                 set_inst_enabled(wip->fmri, wip->inst, ed);
1855         }
1856 
1857         return (0);
1858 }
1859 
1860 /* ARGSUSED */
1861 static int
1862 wait_fmri_enabled(void *data, scf_walkinfo_t *wip)
1863 {
1864         scf_propertygroup_t *pg = NULL;
1865         char state[MAX_SCF_STATE_STRING_SZ];
1866 
1867         assert(wip->inst != NULL);
1868         assert(wip->pg == NULL);
1869 
1870         do {
1871                 if (pg)
1872                         scf_pg_destroy(pg);
1873                 if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) {
1874                         exit_status = EXIT_SVC_FAILURE;
1875                         return (0);
1876                 }
1877 
1878                 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0 ||
1879                     strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) {
1880                         /*
1881                          * We're done.
1882                          */
1883                         goto out;
1884                 }
1885 
1886                 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) {
1887                         /*
1888                          * The service is ill.
1889                          */
1890                         uu_warn(gettext("Instance \"%s\" is in maintenance"
1891                             " state.\n"), wip->fmri);
1892                         exit_status = EXIT_SVC_FAILURE;
1893                         goto out;
1894                 }
1895 
1896                 if (!is_enabled(wip->inst)) {
1897                         /*
1898                          * Someone stepped in and disabled the service.
1899                          */
1900                         uu_warn(gettext("Instance \"%s\" has been disabled"
1901                             " by another entity.\n"), wip->fmri);
1902                         exit_status = EXIT_SVC_FAILURE;
1903                         goto out;
1904                 }
1905 
1906                 if (!has_potential(wip->inst, B_FALSE)) {
1907                         /*
1908                          * Our dependencies aren't met.  We'll never
1909                          * amount to anything.
1910                          */
1911                         uu_warn(gettext("Instance \"%s\" has unsatisfied"
1912                             " dependencies.\n"), wip->fmri);
1913                         /*
1914                          * EXIT_SVC_FAILURE takes precedence over
1915                          * EXIT_DEP_FAILURE
1916                          */
1917                         if (exit_status == 0)
1918                                 exit_status = EXIT_DEP_FAILURE;
1919                         goto out;
1920                 }
1921         } while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0);
1922         scfdie();
1923         /* NOTREACHED */
1924 
1925 out:
1926         scf_pg_destroy(pg);
1927         return (0);
1928 }
1929 
1930 /* ARGSUSED */
1931 static int
1932 wait_fmri_disabled(void *data, scf_walkinfo_t *wip)
1933 {
1934         scf_propertygroup_t *pg = NULL;
1935         char state[MAX_SCF_STATE_STRING_SZ];
1936 
1937         assert(wip->inst != NULL);
1938         assert(wip->pg == NULL);
1939 
1940         do {
1941                 if (pg)
1942                         scf_pg_destroy(pg);
1943                 if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) {
1944                         exit_status = EXIT_SVC_FAILURE;
1945                         return (0);
1946                 }
1947 
1948                 if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0) {
1949                         /*
1950                          * We're done.
1951                          */
1952                         goto out;
1953                 }
1954 
1955                 if (is_enabled(wip->inst)) {
1956                         /*
1957                          * Someone stepped in and enabled the service.
1958                          */
1959                         uu_warn(gettext("Instance \"%s\" has been enabled"
1960                             " by another entity.\n"), wip->fmri);
1961                         exit_status = EXIT_SVC_FAILURE;
1962                         goto out;
1963                 }
1964 
1965                 if (!has_potential(wip->inst, B_TRUE)) {
1966                         /*
1967                          * Our restarter is hopeless.
1968                          */
1969                         uu_warn(gettext("Restarter for instance \"%s\" is"
1970                             " unavailable.\n"), wip->fmri);
1971                         /*
1972                          * EXIT_SVC_FAILURE takes precedence over
1973                          * EXIT_DEP_FAILURE
1974                          */
1975                         if (exit_status == 0)
1976                                 exit_status = EXIT_DEP_FAILURE;
1977                         goto out;
1978                 }
1979 
1980         } while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0);
1981         scfdie();
1982         /* NOTREACHED */
1983 
1984 out:
1985         scf_pg_destroy(pg);
1986         return (0);
1987 }
1988 
1989 /* ARGSUSED */
1990 static int
1991 clear_instance(void *data, scf_walkinfo_t *wip)
1992 {
1993         char state[MAX_SCF_STATE_STRING_SZ];
1994 
1995         assert(wip->inst != NULL);
1996         assert(wip->pg == NULL);
1997 
1998         if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
1999                 return (0);
2000 
2001         if (svcsearch && strcmp(state, svcstate) != 0)
2002                 return (0);
2003 
2004         if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) {
2005                 set_inst_action(wip->fmri, wip->inst, SCF_PROPERTY_MAINT_OFF);
2006         } else if (strcmp(state, SCF_STATE_STRING_DEGRADED) ==
2007             0) {
2008                 set_inst_action(wip->fmri, wip->inst, SCF_PROPERTY_RESTORE);
2009         } else {
2010                 uu_warn(gettext("Instance \"%s\" is not in a "
2011                     "maintenance or degraded state.\n"), wip->fmri);
2012 
2013                 exit_status = 1;
2014         }
2015 
2016         return (0);
2017 }
2018 
2019 static int
2020 set_fmri_action(void *action, scf_walkinfo_t *wip)
2021 {
2022         assert(wip->inst != NULL && wip->pg == NULL);
2023 
2024         if (svcsearch) {
2025                 char state[MAX_SCF_STATE_STRING_SZ];
2026 
2027                 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
2028                         return (0);
2029                 if (strcmp(state, svcstate) != 0)
2030                         return (0);
2031         }
2032 
2033         set_inst_action(wip->fmri, wip->inst, action);
2034 
2035         return (0);
2036 }
2037 
2038 /*
2039  * Flags to control 'mark' action.
2040  */
2041 #define MARK_IMMEDIATE  0x1
2042 #define MARK_TEMPORARY  0x2
2043 
2044 static int
2045 force_degraded(void *data, scf_walkinfo_t *wip)
2046 {
2047         int flags = (int)data;
2048         char state[MAX_SCF_STATE_STRING_SZ];
2049 
2050         if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0) {
2051                 exit_status = 1;
2052                 return (0);
2053         }
2054 
2055         if (svcsearch && strcmp(state, svcstate) != 0)
2056                 return (0);
2057 
2058         if (strcmp(state, SCF_STATE_STRING_ONLINE) != 0) {
2059                 uu_warn(gettext("Instance \"%s\" is not online.\n"), wip->fmri);
2060                 exit_status = 1;
2061                 return (0);
2062         }
2063 
2064         set_inst_action(wip->fmri, wip->inst, (flags & MARK_IMMEDIATE) ?
2065             SCF_PROPERTY_DEGRADE_IMMEDIATE : SCF_PROPERTY_DEGRADED);
2066 
2067         return (0);
2068 }
2069 
2070 static int
2071 force_maintenance(void *data, scf_walkinfo_t *wip)
2072 {
2073         int flags = (int)data;
2074         const char *prop;
2075 
2076         if (svcsearch) {
2077                 char state[MAX_SCF_STATE_STRING_SZ];
2078 
2079                 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
2080                         return (0);
2081                 if (strcmp(state, svcstate) != 0)
2082                         return (0);
2083         }
2084 
2085         if (flags & MARK_IMMEDIATE) {
2086                 prop = (flags & MARK_TEMPORARY) ?
2087                     SCF_PROPERTY_MAINT_ON_IMMTEMP :
2088                     SCF_PROPERTY_MAINT_ON_IMMEDIATE;
2089         } else {
2090                 prop = (flags & MARK_TEMPORARY) ?
2091                     SCF_PROPERTY_MAINT_ON_TEMPORARY :
2092                     SCF_PROPERTY_MAINT_ON;
2093         }
2094 
2095         set_inst_action(wip->fmri, wip->inst, prop);
2096 
2097         return (0);
2098 }
2099 
2100 static void
2101 set_milestone(const char *fmri, boolean_t temporary)
2102 {
2103         scf_instance_t *inst;
2104         scf_propertygroup_t *pg;
2105 
2106         if (temporary) {
2107                 set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS_OVR,
2108                     SCF_PG_OPTIONS_OVR_TYPE, SCF_PG_OPTIONS_OVR_FLAGS,
2109                     SCF_PROPERTY_MILESTONE, fmri);
2110                 return;
2111         }
2112 
2113         if ((inst = scf_instance_create(h)) == NULL ||
2114             (pg = scf_pg_create(h)) == NULL)
2115                 scfdie();
2116 
2117         if (get_inst(SCF_SERVICE_STARTD, inst) != 0) {
2118                 scf_instance_destroy(inst);
2119                 return;
2120         }
2121 
2122         /*
2123          * Set the persistent milestone before deleting the override so we don't
2124          * glitch.
2125          */
2126         set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS,
2127             SCF_PG_OPTIONS_TYPE, SCF_PG_OPTIONS_FLAGS, SCF_PROPERTY_MILESTONE,
2128             fmri);
2129 
2130         if (delete_prop(SCF_SERVICE_STARTD, inst, SCF_PG_OPTIONS_OVR,
2131             SCF_PROPERTY_MILESTONE) != 0)
2132                 exit_status = 1;
2133 
2134         scf_pg_destroy(pg);
2135         scf_instance_destroy(inst);
2136 }
2137 
2138 static char const *milestones[] = {
2139         SCF_MILESTONE_SINGLE_USER,
2140         SCF_MILESTONE_MULTI_USER,
2141         SCF_MILESTONE_MULTI_USER_SERVER,
2142         NULL
2143 };
2144 
2145 static void
2146 usage_milestone(void)
2147 {
2148         const char **ms;
2149 
2150         (void) fprintf(stderr, gettext(
2151         "Usage: svcadm milestone [-d] <milestone>\n\n"
2152         "\t-d\tmake the specified milestone the default for system boot\n\n"
2153         "\tMilestones can be specified using an FMRI or abbreviation.\n"
2154         "\tThe major milestones are as follows:\n\n"
2155         "\tall\n"
2156         "\tnone\n"));
2157 
2158         for (ms = milestones; *ms != NULL; ms++)
2159                 (void) fprintf(stderr, "\t%s\n", *ms);
2160 
2161         exit(UU_EXIT_USAGE);
2162 }
2163 
2164 static const char *
2165 validate_milestone(const char *milestone)
2166 {
2167         const char **ms;
2168         const char *tmp;
2169         size_t len;
2170 
2171         if (strcmp(milestone, "all") == 0)
2172                 return (milestone);
2173 
2174         if (strcmp(milestone, "none") == 0)
2175                 return (milestone);
2176 
2177         /*
2178          * Determine if this is a full or partial milestone
2179          */
2180         for (ms = milestones; *ms != NULL; ms++) {
2181                 if ((tmp = strstr(*ms, milestone)) != NULL) {
2182                         len = strlen(milestone);
2183 
2184                         /*
2185                          * The beginning of the string must align with the start
2186                          * of a milestone fmri, or on the boundary between
2187                          * elements.  The end of the string must align with the
2188                          * end of the milestone, or at the instance boundary.
2189                          */
2190                         if ((tmp == *ms || tmp[-1] == '/') &&
2191                             (tmp[len] == '\0' || tmp[len] == ':'))
2192                                 return (*ms);
2193                 }
2194         }
2195 
2196         (void) fprintf(stderr,
2197             gettext("\"%s\" is not a valid major milestone.\n"), milestone);
2198 
2199         usage_milestone();
2200         /* NOTREACHED */
2201 }
2202 
2203 /*PRINTFLIKE1*/
2204 static void
2205 pr_warn(const char *format, ...)
2206 {
2207         const char *pname = uu_getpname();
2208         va_list alist;
2209 
2210         va_start(alist, format);
2211 
2212         if (pname != NULL)
2213                 (void) fprintf(stderr, "%s", pname);
2214 
2215         if (g_zonename != NULL)
2216                 (void) fprintf(stderr, " (%s)", g_zonename);
2217 
2218         (void) fprintf(stderr, ": ");
2219 
2220         (void) vfprintf(stderr, format, alist);
2221 
2222         if (strrchr(format, '\n') == NULL)
2223                 (void) fprintf(stderr, ": %s\n", strerror(errno));
2224 
2225         va_end(alist);
2226 }
2227 
2228 /*ARGSUSED*/
2229 static void
2230 quiet(const char *fmt, ...)
2231 {
2232         /* Do nothing */
2233 }
2234 
2235 int
2236 main(int argc, char *argv[])
2237 {
2238         int o;
2239         int err;
2240         int sw_back;
2241         boolean_t do_zones = B_FALSE;
2242         boolean_t do_a_zone = B_FALSE;
2243         char zonename[ZONENAME_MAX];
2244         uint_t nzents = 0, zent = 0;
2245         zoneid_t *zids = NULL;
2246         int orig_optind, orig_argc;
2247         char **orig_argv;
2248 
2249         (void) setlocale(LC_ALL, "");
2250         (void) textdomain(TEXT_DOMAIN);
2251 
2252         (void) uu_setpname(argv[0]);
2253 
2254         if (argc < 2)
2255                 usage();
2256 
2257         max_scf_fmri_sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
2258         if (max_scf_fmri_sz < 0)
2259                 scfdie();
2260         ++max_scf_fmri_sz;
2261 
2262         scratch_fmri = malloc(max_scf_fmri_sz);
2263         if (scratch_fmri == NULL)
2264                 uu_die(emsg_nomem);
2265 
2266         while ((o = getopt(argc, argv, "S:vZz:")) != -1) {
2267                 switch (o) {
2268                 case 'S':
2269                         (void) strlcpy(svcstate, optarg, sizeof (svcstate));
2270                         svcsearch = B_TRUE;
2271                         break;
2272 
2273                 case 'v':
2274                         verbose = 1;
2275                         break;
2276 
2277                 case 'z':
2278                         if (getzoneid() != GLOBAL_ZONEID)
2279                                 uu_die(gettext("svcadm -z may only be used "
2280                                     "from the global zone\n"));
2281                         if (do_zones)
2282                                 usage();
2283 
2284                         (void) strlcpy(zonename, optarg, sizeof (zonename));
2285                         do_a_zone = B_TRUE;
2286                         break;
2287 
2288                 case 'Z':
2289                         if (getzoneid() != GLOBAL_ZONEID)
2290                                 uu_die(gettext("svcadm -Z may only be used "
2291                                     "from the global zone\n"));
2292                         if (do_a_zone)
2293                                 usage();
2294 
2295                         do_zones = B_TRUE;
2296                         break;
2297 
2298                 default:
2299                         usage();
2300                 }
2301         }
2302 
2303         while (do_zones) {
2304                 uint_t found;
2305 
2306                 if (zone_list(NULL, &nzents) != 0)
2307                         uu_die(gettext("could not get number of zones"));
2308 
2309                 if ((zids = malloc(nzents * sizeof (zoneid_t))) == NULL) {
2310                         uu_die(gettext("could not allocate array for "
2311                             "%d zone IDs"), nzents);
2312                 }
2313 
2314                 found = nzents;
2315 
2316                 if (zone_list(zids, &found) != 0)
2317                         uu_die(gettext("could not get zone list"));
2318 
2319                 /*
2320                  * If the number of zones has not changed between our calls to
2321                  * zone_list(), we're done -- otherwise, we must free our array
2322                  * of zone IDs and take another lap.
2323                  */
2324                 if (found == nzents)
2325                         break;
2326 
2327                 free(zids);
2328         }
2329 
2330         emsg_permission_denied = gettext("%s: Permission denied.\n");
2331         emsg_nomem = gettext("Out of memory.\n");
2332         emsg_create_pg_perm_denied = gettext("%s: Couldn't create \"%s\" "
2333             "property group (permission denied).\n");
2334         emsg_pg_perm_denied = gettext("%s: Couldn't modify \"%s\" property "
2335             "group (permission denied).\n");
2336         emsg_prop_perm_denied = gettext("%s: Couldn't modify \"%s/%s\" "
2337             "property (permission denied).\n");
2338         emsg_no_service = gettext("No such service \"%s\".\n");
2339 
2340         orig_optind = optind;
2341         orig_argc = argc;
2342         orig_argv = argv;
2343 
2344 again:
2345         h = scf_handle_create(SCF_VERSION);
2346         if (h == NULL)
2347                 scfdie();
2348 
2349         if (do_zones) {
2350                 zone_status_t status;
2351 
2352                 if (zone_getattr(zids[zent], ZONE_ATTR_STATUS, &status,
2353                     sizeof (status)) < 0 || status != ZONE_IS_RUNNING) {
2354                         /*
2355                          * If this zone is not running or we cannot
2356                          * get its status, we do not want to attempt
2357                          * to bind an SCF handle to it, lest we
2358                          * accidentally interfere with a zone that
2359                          * is not yet running by looking up a door
2360                          * to its svc.configd (which could potentially
2361                          * block a mount with an EBUSY).
2362                          */
2363                         zent++;
2364                         goto nextzone;
2365                 }
2366 
2367                 if (getzonenamebyid(zids[zent++], zonename,
2368                     sizeof (zonename)) < 0) {
2369                         uu_warn(gettext("could not get name for "
2370                             "zone %d; ignoring"), zids[zent - 1]);
2371                         goto nextzone;
2372                 }
2373 
2374                 g_zonename = zonename;
2375         }
2376 
2377         if (do_a_zone || do_zones) {
2378                 scf_value_t *zone;
2379 
2380                 if ((zone = scf_value_create(h)) == NULL)
2381                         scfdie();
2382 
2383                 if (scf_value_set_astring(zone, zonename) != SCF_SUCCESS)
2384                         scfdie();
2385 
2386                 if (scf_handle_decorate(h, "zone", zone) != SCF_SUCCESS) {
2387                         if (do_a_zone) {
2388                                 uu_die(gettext("invalid zone '%s'\n"), optarg);
2389                         } else {
2390                                 scf_value_destroy(zone);
2391                                 goto nextzone;
2392                         }
2393                 }
2394 
2395                 scf_value_destroy(zone);
2396         }
2397 
2398         if (scf_handle_bind(h) == -1) {
2399                 if (do_zones)
2400                         goto nextzone;
2401 
2402                 uu_die(gettext("Couldn't bind to configuration repository: "
2403                     "%s.\n"), scf_strerror(scf_error()));
2404         }
2405 
2406         optind = orig_optind;
2407         argc = orig_argc;
2408         argv = orig_argv;
2409 
2410         if (optind >= argc)
2411                 usage();
2412 
2413         if (strcmp(argv[optind], "enable") == 0) {
2414                 enable_data_t ed = {
2415                         .ed_flags = SET_ENABLED,
2416                         .ed_comment = ""
2417                 };
2418                 int wait = 0;
2419                 int error = 0;
2420 
2421                 ++optind;
2422 
2423                 while ((o = getopt(argc, argv, "rst")) != -1) {
2424                         if (o == 'r')
2425                                 ed.ed_flags |= SET_RECURSIVE;
2426                         else if (o == 't')
2427                                 ed.ed_flags |= SET_TEMPORARY;
2428                         else if (o == 's')
2429                                 wait = 1;
2430                         else if (o == '?')
2431                                 usage();
2432                         else {
2433                                 assert(0);
2434                                 abort();
2435                         }
2436                 }
2437                 argc -= optind;
2438                 argv += optind;
2439 
2440                 if (argc == 0 && !svcsearch)
2441                         usage();
2442 
2443                 if (argc > 0 && svcsearch)
2444                         usage();
2445 
2446                 /*
2447                  * We want to continue with -s processing if we had
2448                  * invalid options, but not if an enable failed.  We
2449                  * squelch output the second time we walk fmris; we saw
2450                  * the errors the first time.
2451                  */
2452                 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2453                     set_fmri_enabled, &ed, &error, pr_warn)) != 0) {
2454 
2455                         pr_warn(gettext("failed to iterate over "
2456                             "instances: %s\n"), scf_strerror(err));
2457                         exit_status = UU_EXIT_FATAL;
2458 
2459                 } else if (wait && exit_status == 0 &&
2460                     (err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2461                     wait_fmri_enabled, NULL, &error, quiet)) != 0) {
2462 
2463                         pr_warn(gettext("failed to iterate over "
2464                             "instances: %s\n"), scf_strerror(err));
2465                         exit_status = UU_EXIT_FATAL;
2466                 }
2467 
2468                 if (error > 0)
2469                         exit_status = error;
2470 
2471         } else if (strcmp(argv[optind], "disable") == 0) {
2472                 enable_data_t ed = {
2473                         .ed_flags = 0,
2474                         .ed_comment = ""
2475                 };
2476                 int wait = 0;
2477                 int error = 0;
2478 
2479                 ++optind;
2480 
2481                 while ((o = getopt(argc, argv, "c:st")) != -1) {
2482                         if (o == 'c') {
2483                                 if (strlcpy(ed.ed_comment, optarg,
2484                                     sizeof (ed.ed_comment)) >=
2485                                     sizeof (ed.ed_comment)) {
2486                                         uu_die(gettext("disable -c comment "
2487                                             "too long.\n"));
2488                                 }
2489                         } else if (o == 't')
2490                                 ed.ed_flags |= SET_TEMPORARY;
2491                         else if (o == 's')
2492                                 wait = 1;
2493                         else if (o == '?')
2494                                 usage();
2495                         else {
2496                                 assert(0);
2497                                 abort();
2498                         }
2499                 }
2500                 argc -= optind;
2501                 argv += optind;
2502 
2503                 if (argc == 0 && !svcsearch)
2504                         usage();
2505 
2506                 if (argc > 0 && svcsearch)
2507                         usage();
2508 
2509                 /*
2510                  * We want to continue with -s processing if we had
2511                  * invalid options, but not if a disable failed.  We
2512                  * squelch output the second time we walk fmris; we saw
2513                  * the errors the first time.
2514                  */
2515                 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2516                     set_fmri_enabled, &ed, &exit_status,
2517                     pr_warn)) != 0) {
2518 
2519                         pr_warn(gettext("failed to iterate over "
2520                             "instances: %s\n"), scf_strerror(err));
2521                         exit_status = UU_EXIT_FATAL;
2522 
2523                 } else if (wait && exit_status == 0 &&
2524                     (err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2525                     wait_fmri_disabled, NULL, &error, quiet)) != 0) {
2526 
2527                         pr_warn(gettext("failed to iterate over "
2528                             "instances: %s\n"), scf_strerror(err));
2529                         exit_status = UU_EXIT_FATAL;
2530                 }
2531 
2532                 if (error > 0)
2533                         exit_status = error;
2534 
2535         } else if (strcmp(argv[optind], "restart") == 0) {
2536                 boolean_t do_dump = B_FALSE;
2537 
2538                 ++optind;
2539 
2540                 while ((o = getopt(argc, argv, "d")) != -1) {
2541                         if (o == 'd')
2542                                 do_dump = B_TRUE;
2543                         else if (o == '?')
2544                                 usage();
2545                         else {
2546                                 assert(0);
2547                                 abort();
2548                         }
2549                 }
2550                 argc -= optind;
2551                 argv += optind;
2552 
2553                 if (argc == 0 && !svcsearch)
2554                         usage();
2555 
2556                 if (argc > 0 && svcsearch)
2557                         usage();
2558 
2559                 if (do_dump) {
2560                         if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2561                             set_fmri_action, (void *)SCF_PROPERTY_DODUMP,
2562                             &exit_status, pr_warn)) != 0) {
2563                                 pr_warn(gettext("failed to iterate over "
2564                                     "instances: %s\n"), scf_strerror(err));
2565                                 exit_status = UU_EXIT_FATAL;
2566                         }
2567                 }
2568 
2569                 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2570                     set_fmri_action, (void *)SCF_PROPERTY_RESTART, &exit_status,
2571                     pr_warn)) != 0) {
2572                         pr_warn(gettext("failed to iterate over "
2573                             "instances: %s\n"), scf_strerror(err));
2574                         exit_status = UU_EXIT_FATAL;
2575                 }
2576 
2577         } else if (strcmp(argv[optind], "refresh") == 0) {
2578                 ++optind;
2579                 argc -= optind;
2580                 argv += optind;
2581 
2582                 if (argc == 0 && !svcsearch)
2583                         usage();
2584 
2585                 if (argc > 0 && svcsearch)
2586                         usage();
2587 
2588                 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2589                     set_fmri_action, (void *)SCF_PROPERTY_REFRESH, &exit_status,
2590                     pr_warn)) != 0) {
2591                         pr_warn(gettext("failed to iterate over "
2592                             "instances: %s\n"), scf_strerror(scf_error()));
2593                         exit_status = UU_EXIT_FATAL;
2594                 }
2595 
2596         } else if (strcmp(argv[optind], "mark") == 0) {
2597                 int flags = 0;
2598                 scf_walk_callback callback;
2599 
2600                 ++optind;
2601 
2602                 while ((o = getopt(argc, argv, "It")) != -1) {
2603                         if (o == 'I')
2604                                 flags |= MARK_IMMEDIATE;
2605                         else if (o == 't')
2606                                 flags |= MARK_TEMPORARY;
2607                         else if (o == '?')
2608                                 usage();
2609                         else {
2610                                 assert(0);
2611                                 abort();
2612                         }
2613                 }
2614 
2615                 if (argc - optind < 2)
2616                         usage();
2617 
2618                 if (strcmp(argv[optind], "degraded") == 0) {
2619                         if (flags & MARK_TEMPORARY)
2620                                 uu_xdie(UU_EXIT_USAGE, gettext("-t may not be "
2621                                     "used with degraded.\n"));
2622                         callback = force_degraded;
2623 
2624                 } else if (strcmp(argv[optind], "maintenance") == 0) {
2625                         callback = force_maintenance;
2626                 } else {
2627                         usage();
2628                 }
2629 
2630                 optind++;
2631                 argc -= optind;
2632                 argv += optind;
2633 
2634                 if (argc == 0 && !svcsearch)
2635                         usage();
2636 
2637                 if (argc > 0 && svcsearch)
2638                         usage();
2639 
2640                 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS, callback,
2641                     NULL, &exit_status, pr_warn)) != 0) {
2642                         pr_warn(gettext("failed to iterate over "
2643                             "instances: %s\n"),
2644                             scf_strerror(err));
2645                         exit_status = UU_EXIT_FATAL;
2646                 }
2647 
2648         } else if (strcmp(argv[optind], "clear") == 0) {
2649                 ++optind;
2650                 argc -= optind;
2651                 argv += optind;
2652 
2653                 if (argc == 0 && !svcsearch)
2654                         usage();
2655 
2656                 if (svcsearch) {
2657                         if (argc > 0)
2658                                 usage();
2659                         if (strcmp(svcstate, SCF_STATE_STRING_MAINT) != 0 &&
2660                             strcmp(svcstate, SCF_STATE_STRING_DEGRADED) != 0)
2661                                 uu_die(gettext("State must be '%s' or '%s'\n"),
2662                                     SCF_STATE_STRING_MAINT,
2663                                     SCF_STATE_STRING_DEGRADED);
2664                 }
2665 
2666                 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2667                     clear_instance, NULL, &exit_status, pr_warn)) != 0) {
2668                         pr_warn(gettext("failed to iterate over "
2669                             "instances: %s\n"), scf_strerror(err));
2670                         exit_status = UU_EXIT_FATAL;
2671                 }
2672 
2673         } else if (strcmp(argv[optind], "milestone") == 0) {
2674                 boolean_t temporary = B_TRUE;
2675                 const char *milestone;
2676 
2677                 ++optind;
2678 
2679                 while ((o = getopt(argc, argv, "d")) != -1) {
2680                         if (o == 'd')
2681                                 temporary = B_FALSE;
2682                         else if (o == '?')
2683                                 usage_milestone();
2684                         else {
2685                                 assert(0);
2686                                 abort();
2687                         }
2688                 }
2689 
2690                 if (optind >= argc)
2691                         usage_milestone();
2692 
2693                 milestone = validate_milestone(argv[optind]);
2694 
2695                 set_milestone(milestone, temporary);
2696         } else if (strcmp(argv[optind], "_smf_backup") == 0) {
2697                 const char *reason = NULL;
2698 
2699                 ++optind;
2700 
2701                 if (optind != argc - 1)
2702                         usage();
2703 
2704                 if ((err = _scf_request_backup(h, argv[optind])) !=
2705                     SCF_SUCCESS) {
2706                         switch (scf_error()) {
2707                         case SCF_ERROR_NOT_BOUND:
2708                         case SCF_ERROR_CONNECTION_BROKEN:
2709                         case SCF_ERROR_BACKEND_READONLY:
2710                                 scfdie();
2711                                 break;
2712 
2713                         case SCF_ERROR_PERMISSION_DENIED:
2714                         case SCF_ERROR_INVALID_ARGUMENT:
2715                                 reason = scf_strerror(scf_error());
2716                                 break;
2717 
2718                         case SCF_ERROR_INTERNAL:
2719                                 reason =
2720                                     "unknown error (see console for details)";
2721                                 break;
2722                         }
2723 
2724                         pr_warn("failed to backup repository: %s\n", reason);
2725                         exit_status = UU_EXIT_FATAL;
2726                 }
2727         } else if (strcmp(argv[optind], "_smf_repository_switch") == 0) {
2728                 const char *reason = NULL;
2729 
2730                 ++optind;
2731 
2732                 /*
2733                  * Check argument and setup scf_switch structure
2734                  */
2735                 if (optind != argc - 1)
2736                         exit(1);
2737 
2738                 if (strcmp(argv[optind], "fast") == 0) {
2739                         sw_back = 0;
2740                 } else if (strcmp(argv[optind], "perm") == 0) {
2741                         sw_back = 1;
2742                 } else {
2743                         exit(UU_EXIT_USAGE);
2744                 }
2745 
2746                 /*
2747                  * Call into switch primitive
2748                  */
2749                 if ((err = _scf_repository_switch(h, sw_back)) !=
2750                     SCF_SUCCESS) {
2751                         /*
2752                          * Retrieve per thread SCF error code
2753                          */
2754                         switch (scf_error()) {
2755                         case SCF_ERROR_NOT_BOUND:
2756                                 abort();
2757                                 /* NOTREACHED */
2758 
2759                         case SCF_ERROR_CONNECTION_BROKEN:
2760                         case SCF_ERROR_BACKEND_READONLY:
2761                                 scfdie();
2762                                 /* NOTREACHED */
2763 
2764                         case SCF_ERROR_PERMISSION_DENIED:
2765                         case SCF_ERROR_INVALID_ARGUMENT:
2766                                 reason = scf_strerror(scf_error());
2767                                 break;
2768 
2769                         case SCF_ERROR_INTERNAL:
2770                                 reason = "File operation error: (see console)";
2771                                 break;
2772 
2773                         default:
2774                                 abort();
2775                                 /* NOTREACHED */
2776                         }
2777 
2778                         pr_warn("failed to switch repository: %s\n", reason);
2779                         exit_status = UU_EXIT_FATAL;
2780                 }
2781         } else {
2782                 usage();
2783         }
2784 
2785         if (scf_handle_unbind(h) == -1)
2786                 scfdie();
2787 nextzone:
2788         scf_handle_destroy(h);
2789         if (do_zones && zent < nzents)
2790                 goto again;
2791 
2792         return (exit_status);
2793 }