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