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