Print this page
12721 would like svcadm disable -c


   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>


  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;


 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 }


 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 */


 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  *


 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 


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);


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();


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);


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;


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"


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 {




   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * Copyright 2020, Joyent, Inc. All rights reserved.
  28  */
  29 
  30 /*
  31  * svcadm - request adminstrative actions for service instances
  32  */
  33 
  34 #include <locale.h>
  35 #include <libintl.h>
  36 #include <libscf.h>
  37 #include <libscf_priv.h>
  38 #include <libcontract.h>
  39 #include <libcontract_priv.h>
  40 #include <sys/contract/process.h>
  41 #include <libuutil.h>
  42 #include <stddef.h>
  43 #include <stdio.h>
  44 #include <stdlib.h>
  45 #include <string.h>
  46 #include <unistd.h>
  47 #include <fcntl.h>


  65 
  66 #define WALK_FLAGS      (SCF_WALK_UNIPARTIAL | SCF_WALK_MULTIPLE)
  67 
  68 /*
  69  * How long we will wait (in seconds) for a service to change state
  70  * before re-checking its dependencies.
  71  */
  72 #define WAIT_INTERVAL           3
  73 
  74 #define bad_error(func, err)                                            \
  75         uu_panic("%s:%d: %s() failed with unexpected error %d.\n",      \
  76             __FILE__, __LINE__, (func), (err));
  77 
  78 struct ht_elt {
  79         struct ht_elt   *next;
  80         boolean_t       active;
  81         char            str[1];
  82 };
  83 
  84 
  85 /*
  86  * Callback data for enable/disable.
  87  */
  88 #define SET_ENABLED     0x1
  89 #define SET_TEMPORARY   0x2
  90 #define SET_RECURSIVE   0x4
  91 
  92 typedef struct {
  93         char ed_comment[SCF_COMMENT_MAX_LENGTH];
  94         int ed_flags;
  95 } enable_data_t;
  96 
  97 
  98 scf_handle_t *h;
  99 ssize_t max_scf_fmri_sz;
 100 static const char *emsg_permission_denied;
 101 static const char *emsg_nomem;
 102 static const char *emsg_create_pg_perm_denied;
 103 static const char *emsg_pg_perm_denied;
 104 static const char *emsg_prop_perm_denied;
 105 static const char *emsg_no_service;
 106 
 107 static int exit_status = 0;
 108 static int verbose = 0;
 109 static char *scratch_fmri;
 110 static char *g_zonename = NULL;
 111 static char svcstate[80];
 112 static boolean_t svcsearch = B_FALSE;
 113 
 114 static struct ht_elt **visited;
 115 
 116 void do_scfdie(int lineno) __NORETURN;
 117 static void usage_milestone(void) __NORETURN;


 142 
 143         default:
 144 #ifdef NDEBUG
 145                 uu_die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
 146                     scf_strerror(err));
 147 #else
 148                 uu_die("Unexpected libscf error on line %d: %s.\n", lineno,
 149                     scf_strerror(err));
 150 #endif
 151         }
 152 }
 153 
 154 #define scfdie()        do_scfdie(__LINE__)
 155 
 156 static void
 157 usage()
 158 {
 159         (void) fprintf(stderr, gettext(
 160         "Usage: %1$s [-S <state>] [-v] [-Z | -z zone] [cmd [args ... ]]\n\n"
 161         "\t%1$s enable [-rst] [<service> ...]\t- enable and online service(s)\n"
 162         "\t%1$s disable [-c comment] [-st] [<service> ...] - disable "
 163         "service(s)\n"
 164         "\t%1$s restart [-d] [<service> ...]\t- restart specified service(s)\n"
 165         "\t%1$s refresh [<service> ...]\t\t- re-read service configuration\n"
 166         "\t%1$s mark [-It] <state> [<service> ...] - set maintenance state\n"
 167         "\t%1$s clear [<service> ...]\t\t- clear maintenance state\n"
 168         "\t%1$s milestone [-d] <milestone>\t- advance to a service milestone\n"
 169         "\n\t"
 170         "Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n"
 171         "\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n"
 172         "\n"
 173         "\t%1$s <cmd> svc:/network/smtp:sendmail\n"
 174         "\t%1$s <cmd> network/smtp:sendmail\n"
 175         "\t%1$s <cmd> network/*mail\n"
 176         "\t%1$s <cmd> network/smtp\n"
 177         "\t%1$s <cmd> smtp:sendmail\n"
 178         "\t%1$s <cmd> smtp\n"
 179         "\t%1$s <cmd> sendmail\n"), uu_getpname());
 180 
 181         exit(UU_EXIT_USAGE);
 182 }


 563         default:
 564                 scfdie();
 565         }
 566 
 567         if (my_ct_name(scratch_fmri, max_scf_fmri_sz) > 0) {
 568                 set_astring_prop(fmri, SCF_PG_RESTARTER_ACTIONS,
 569                     SCF_PG_RESTARTER_ACTIONS_TYPE,
 570                     SCF_PG_RESTARTER_ACTIONS_FLAGS,
 571                     SCF_PROPERTY_AUX_FMRI, scratch_fmri);
 572         } else {
 573                 uu_warn(gettext("%s: Could not set %s/%s: "
 574                     "my_ct_name failed.\n"), fmri,
 575                     SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_FMRI);
 576         }
 577 
 578 out:
 579         scf_pg_destroy(pg);
 580         return (ret);
 581 }
 582 
 583 static int
 584 delete_prop(const char *fmri, scf_instance_t *inst, const char *pgname,
 585     const char *propname)
 586 {
 587         int r = scf_instance_delete_prop(inst, pgname, propname);
 588 
 589         switch (r) {
 590         case 0:
 591                 break;
 592 
 593         case ECANCELED:
 594                 uu_warn(emsg_no_service, fmri);
 595                 break;
 596 
 597         case EACCES:
 598                 uu_warn(gettext("Could not delete %s/%s "
 599                     "property of %s: backend access denied.\n"),
 600                     pgname, propname, fmri);
 601                 break;
 602 
 603         case EROFS:
 604                 uu_warn(gettext("Could not delete %s/%s "
 605                     "property of %s: backend is read-only.\n"),
 606                     pgname, propname, fmri);
 607                 break;
 608 
 609         default:
 610                 bad_error("scf_instance_delete_prop", r);
 611         }
 612 
 613         return (r);
 614 }
 615 
 616 /*
 617  * Returns 0, EPERM, or EROFS.



 618  */
 619 static int
 620 set_enabled_props(scf_propertygroup_t *pg, enable_data_t *ed)
 621 {
 622         scf_transaction_entry_t *ent1;
 623         scf_transaction_entry_t *ent2;
 624         scf_transaction_t *tx;
 625         scf_value_t *v2;
 626         scf_value_t *v1;
 627         int ret = 0, r;
 628 
 629         if ((tx = scf_transaction_create(h)) == NULL ||
 630             (ent1 = scf_entry_create(h)) == NULL ||
 631             (ent2 = scf_entry_create(h)) == NULL ||
 632             (v1 = scf_value_create(h)) == NULL ||
 633             (v2 = scf_value_create(h)) == NULL)
 634                 scfdie();
 635 
 636         for (;;) {
 637                 if (scf_transaction_start(tx, pg) == -1) {
 638                         switch (scf_error()) {
 639                         case SCF_ERROR_PERMISSION_DENIED:
 640                                 ret = EPERM;
 641                                 goto out;
 642 
 643                         case SCF_ERROR_BACKEND_READONLY:
 644                                 ret = EROFS;
 645                                 goto out;
 646 
 647                         default:
 648                                 scfdie();
 649                         }
 650                 }
 651 
 652                 if (scf_transaction_property_change_type(tx, ent1,
 653                     SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN) != 0) {
 654                         if (scf_error() != SCF_ERROR_NOT_FOUND)
 655                                 scfdie();
 656 
 657                         if (scf_transaction_property_new(tx, ent1,
 658                             SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN) != 0)
 659                                 scfdie();
 660                 }
 661 
 662                 scf_value_set_boolean(v1, !!(ed->ed_flags & SET_ENABLED));
 663 
 664                 r = scf_entry_add_value(ent1, v1);
 665                 assert(r == 0);
 666 
 667                 if (scf_transaction_property_change_type(tx, ent2,
 668                     SCF_PROPERTY_COMMENT, SCF_TYPE_ASTRING) != 0) {
 669                         if (scf_error() != SCF_ERROR_NOT_FOUND)
 670                                 scfdie();
 671 
 672                         if (scf_transaction_property_new(tx, ent2,
 673                             SCF_PROPERTY_COMMENT, SCF_TYPE_ASTRING) != 0)
 674                                 scfdie();
 675                 }
 676 
 677                 if (scf_value_set_astring(v2, ed->ed_comment) != SCF_SUCCESS)
 678                         scfdie();
 679 
 680                 if (scf_entry_add_value(ent2, v2) != SCF_SUCCESS)
 681                         scfdie();
 682 
 683                 r = scf_transaction_commit(tx);
 684                 if (r == 1)
 685                         break;
 686 
 687                 scf_transaction_reset(tx);
 688 
 689                 if (r != 0) {
 690                         switch (scf_error()) {
 691                         case SCF_ERROR_PERMISSION_DENIED:
 692                                 ret = EPERM;
 693                                 goto out;
 694 
 695                         case SCF_ERROR_BACKEND_READONLY:
 696                                 ret = EROFS;
 697                                 goto out;
 698 
 699                         default:
 700                                 scfdie();
 701                         }
 702                 }
 703 
 704                 if (scf_pg_update(pg) == -1)
 705                         scfdie();
 706         }
 707 
 708 out:
 709         scf_transaction_destroy(tx);
 710         scf_entry_destroy(ent1);
 711         scf_entry_destroy(ent2);
 712         scf_value_destroy(v1);
 713         scf_value_destroy(v2);
 714         return (ret);
 715 }
 716 
 717 /*
 718  * Enable or disable an instance.  SET_TEMPORARY modifications apply to
 719  * general_ovr/ property group.
 720  */
 721 static void
 722 set_inst_enabled(const char *fmri, scf_instance_t *inst, enable_data_t *ed)

 723 {
 724         scf_propertygroup_t *pg;
 725         uint8_t b;
 726         const char *pgname = NULL;      /* For emsg_pg_perm_denied */

 727 
 728         pg = scf_pg_create(h);
 729         if (pg == NULL)
 730                 scfdie();
 731 
 732         if (restarter_setup(fmri, inst))
 733                 goto out;
 734 
 735         /*
 736          * An instance's configuration is incomplete if general/enabled
 737          * doesn't exist. Create both the property group and property
 738          * here if they don't exist.
 739          */
 740         pgname = SCF_PG_GENERAL;
 741         if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE,
 742             SCF_PG_GENERAL_FLAGS, pg) != 0)
 743                 goto eperm;
 744 
 745         if (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b) != 0) {
 746                 /* Create and set state to disabled */


 751                 case EPERM:
 752                         goto eperm;
 753 
 754                 case EROFS:
 755                         /* Shouldn't happen, but it can. */
 756                         if (!verbose)
 757                                 uu_warn(gettext("%s: Repository read-only.\n"),
 758                                     fmri);
 759                         else
 760                                 uu_warn(gettext("%s: Could not set %s/%s "
 761                                     "(repository read-only).\n"), fmri,
 762                                     SCF_PG_GENERAL, SCF_PROPERTY_ENABLED);
 763                         goto out;
 764 
 765                 default:
 766                         assert(0);
 767                         abort();
 768                 }
 769         }
 770 
 771         if (ed->ed_flags & SET_TEMPORARY) {

 772                 pgname = SCF_PG_GENERAL_OVR;
 773                 if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_OVR_TYPE,
 774                     SCF_PG_GENERAL_OVR_FLAGS, pg) != 0)
 775                         goto eperm;
 776 
 777                 switch (set_enabled_props(pg, ed)) {
 778                 case 0:
 779                         break;
 780 
 781                 case EPERM:
 782                         goto eperm;
 783 
 784                 case EROFS:
 785                         /* Shouldn't happen, but it can. */
 786                         if (!verbose)
 787                                 uu_warn(gettext("%s: Repository read-only.\n"),
 788                                     fmri);
 789                         else
 790                                 uu_warn(gettext("%s: Could not set %s/%s "
 791                                     "(repository read-only).\n"), fmri,
 792                                     SCF_PG_GENERAL_OVR, SCF_PROPERTY_ENABLED);
 793                         goto out;
 794 
 795                 default:
 796                         assert(0);
 797                         abort();
 798                 }
 799 
 800                 if (verbose)
 801                         (void) printf((ed->ed_flags & SET_ENABLED) ?
 802                             gettext("%s temporarily enabled.\n") :
 803                             gettext("%s temporarily disabled.\n"), fmri);
 804         } else {
 805 again:
 806                 /*
 807                  * Both pg and property should exist since we created
 808                  * them earlier. However, there's still a chance that
 809                  * someone may have deleted the property out from under
 810                  * us.
 811                  */
 812                 if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE,
 813                     SCF_PG_GENERAL_FLAGS, pg) != 0)
 814                         goto eperm;
 815 
 816                 switch (set_enabled_props(pg, ed)) {
 817                 case 0:
 818                         break;
 819 
 820                 case EPERM:
 821                         goto eperm;
 822 
 823                 case EROFS:
 824                         /*
 825                          * If general/enabled is already set the way we want,
 826                          * proceed.
 827                          */
 828                         switch (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b)) {
 829                         case 0:
 830                                 if (!(b) == !(ed->ed_flags & SET_ENABLED))
 831                                         break;
 832                                 /* FALLTHROUGH */
 833 
 834                         case ENOENT:
 835                         case EINVAL:
 836                         case E2BIG:
 837                                 if (!verbose)
 838                                         uu_warn(gettext("%s: Repository "
 839                                             "read-only.\n"), fmri);
 840                                 else
 841                                         uu_warn(gettext("%s: Could not set "
 842                                             "%s/%s (repository read-only).\n"),
 843                                             fmri, SCF_PG_GENERAL,
 844                                             SCF_PROPERTY_ENABLED);
 845                                 goto out;
 846 
 847                         case ECANCELED:
 848                                 goto again;
 849 
 850                         default:
 851                                 assert(0);
 852                                 abort();
 853                         }
 854                         break;
 855 
 856                 default:
 857                         assert(0);
 858                         abort();
 859                 }
 860 
 861                 switch (delete_prop(fmri, inst, SCF_PG_GENERAL_OVR,
 862                     SCF_PROPERTY_ENABLED)) {


 863                 case 0:
 864                         break;
 865 




 866                 case EPERM:
 867                         goto eperm;
 868 
 869                 default:



 870                         goto out;
 871                 }
 872 
 873                 switch (delete_prop(fmri, inst, SCF_PG_GENERAL_OVR,
 874                     SCF_PROPERTY_COMMENT)) {
 875                 case 0:
 876                         break;

 877 
 878                 case EPERM:
 879                         goto eperm;
 880 
 881                 default:
 882                         goto out;
 883                 }
 884 
 885                 if (verbose) {
 886                         (void) printf((ed->ed_flags & SET_ENABLED) ?
 887                             gettext("%s enabled.\n") :
 888                             gettext("%s disabled.\n"), fmri);
 889                 }
 890         }
 891 
 892         scf_pg_destroy(pg);
 893         return;
 894 
 895 eperm:
 896         assert(pgname != NULL);
 897         if (!verbose)
 898                 uu_warn(emsg_permission_denied, fmri);
 899         else
 900                 uu_warn(emsg_pg_perm_denied, fmri, pgname);
 901 
 902 out:
 903         scf_pg_destroy(pg);
 904         exit_status = 1;
 905 }
 906 
 907 /*
 908  * Set inst to the instance which corresponds to fmri.  If fmri identifies
 909  * a service with a single instance, get that instance.
 910  *


1135         scf_instance_destroy(inst);
1136         return (ret);
1137 }
1138 
1139 /*
1140  * Enable the service or instance identified by fmri and its dependencies,
1141  * recursively.  Specifically, call get_inst(fmri), enable the result, and
1142  * recurse on its restarter and the dependencies.  To avoid duplication of
1143  * effort or looping around a dependency cycle, each FMRI is entered into the
1144  * "visited" hash table.  While recursing, the hash table entry is marked
1145  * "active", so that if we come upon it again, we know we've hit a cycle.
1146  * exclude_all and optional_all dependencies are ignored.  require_any
1147  * dependencies are followed only if they comprise a single service; otherwise
1148  * the user is warned.
1149  *
1150  * fmri must point to a writable max_scf_fmri_sz buffer.  Returns EINVAL if fmri
1151  * is invalid, E2BIG if fmri identifies a service with multiple instances, ELOOP
1152  * on cycle detection, or 0 on success.
1153  */
1154 static int
1155 enable_fmri_rec(char *fmri, enable_data_t *ed)
1156 {
1157         scf_instance_t *inst;
1158         scf_snapshot_t *snap;
1159         scf_propertygroup_t *pg;
1160         scf_property_t *prop;
1161         scf_value_t *v;
1162         scf_iter_t *pg_iter, *val_iter;
1163         scf_type_t ty;
1164         char *buf, *pgname;
1165         ssize_t name_sz, len, sz;
1166         int ret;
1167         struct ht_elt *he;
1168 
1169         len = scf_canonify_fmri(fmri, fmri, max_scf_fmri_sz);
1170         if (len < 0) {
1171                 assert(scf_error() == SCF_ERROR_INVALID_ARGUMENT);
1172                 return (EINVAL);
1173         }
1174         assert(len < max_scf_fmri_sz);
1175 


1189                 abort();
1190         }
1191 
1192         inst = scf_instance_create(h);
1193         if (inst == NULL)
1194                 scfdie();
1195 
1196         switch (get_inst_mult(fmri, inst)) {
1197         case 0:
1198                 break;
1199 
1200         case E2BIG:
1201                 he->active = B_FALSE;
1202                 return (E2BIG);
1203 
1204         default:
1205                 he->active = B_FALSE;
1206                 return (0);
1207         }
1208 
1209         set_inst_enabled(fmri, inst, ed);
1210 
1211         if ((snap = scf_snapshot_create(h)) == NULL ||
1212             (pg = scf_pg_create(h)) == NULL ||
1213             (prop = scf_property_create(h)) == NULL ||
1214             (v = scf_value_create(h)) == NULL ||
1215             (pg_iter = scf_iter_create(h)) == NULL ||
1216             (val_iter = scf_iter_create(h)) == NULL)
1217                 scfdie();
1218 
1219         buf = malloc(max_scf_fmri_sz);
1220         if (buf == NULL)
1221                 uu_die(emsg_nomem);
1222 
1223         name_sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
1224         if (name_sz < 0)
1225                 scfdie();
1226         ++name_sz;
1227         pgname = malloc(name_sz);
1228         if (pgname == NULL)
1229                 uu_die(emsg_nomem);


1239         /* Enable restarter */
1240         if (scf_instance_get_pg_composed(inst, snap, SCF_PG_GENERAL, pg) != 0) {
1241                 if (scf_error() != SCF_ERROR_NOT_FOUND)
1242                         scfdie();
1243 
1244                 uu_warn(gettext("\"%s\" is misconfigured (lacks \"%s\" "
1245                     "property group).\n"), fmri, SCF_PG_GENERAL);
1246                 ret = 0;
1247                 goto out;
1248         }
1249 
1250         sz = get_astring_prop(pg, SCF_PROPERTY_RESTARTER, prop, v, buf,
1251             max_scf_fmri_sz);
1252         if (sz > max_scf_fmri_sz) {
1253                 uu_warn(gettext("\"%s\" is misconfigured (the value of "
1254                     "\"%s/%s\" is too long).\n"), fmri, SCF_PG_GENERAL,
1255                     SCF_PROPERTY_RESTARTER);
1256                 ret = 0;
1257                 goto out;
1258         } else if (sz >= 0) {
1259                 switch (enable_fmri_rec(buf, ed)) {
1260                 case 0:
1261                         break;
1262 
1263                 case EINVAL:
1264                         uu_warn(gettext("Restarter FMRI for \"%s\" is "
1265                             "invalid.\n"), fmri);
1266                         break;
1267 
1268                 case E2BIG:
1269                         uu_warn(gettext("Restarter FMRI for \"%s\" identifies "
1270                             "a service with multiple instances.\n"), fmri);
1271                         break;
1272 
1273                 case ELOOP:
1274                         ret = ELOOP;
1275                         goto out;
1276 
1277                 default:
1278                         assert(0);
1279                         abort();


1390 
1391                         /*
1392                          * Since there's only one instance, we can enable it.
1393                          * Reset val_iter and continue.
1394                          */
1395                         if (scf_iter_property_values(val_iter, prop) != 0)
1396                                 scfdie();
1397                 }
1398 
1399                 for (;;) {
1400                         ret = scf_iter_next_value(val_iter, v);
1401                         if (ret == 0)
1402                                 break;
1403                         if (ret != 1)
1404                                 scfdie();
1405 
1406                         if (scf_value_get_astring(v, buf, max_scf_fmri_sz) ==
1407                             -1)
1408                                 scfdie();
1409 
1410                         switch (enable_fmri_rec(buf, ed)) {
1411                         case 0:
1412                                 break;
1413 
1414                         case EINVAL:
1415                                 uu_warn(gettext("\"%s\" dependency of \"%s\" "
1416                                     "has invalid FMRI \"%s\".\n"), pgname,
1417                                     fmri, buf);
1418                                 break;
1419 
1420                         case E2BIG:
1421                                 uu_warn(gettext("%s depends on %s, which has "
1422                                     "multiple instances.\n"), fmri, buf);
1423                                 break;
1424 
1425                         case ELOOP:
1426                                 ret = ELOOP;
1427                                 goto out;
1428 
1429                         default:
1430                                 assert(0);


1789                 }
1790 
1791                 if (ret == 0) {
1792                         scf_transaction_reset(tx);
1793 
1794                         if (scf_pg_update(pg) == -1)
1795                                 scfdie();
1796                 }
1797         } while (ret == 0);
1798 
1799 out:
1800         scf_transaction_destroy(tx);
1801         scf_entry_destroy(txent);
1802         scf_value_destroy(val);
1803         scf_property_destroy(prop);
1804         scf_pg_destroy(pg);
1805         scf_instance_destroy(inst);
1806 }
1807 
1808 







1809 static int
1810 set_fmri_enabled(void *data, scf_walkinfo_t *wip)
1811 {
1812         enable_data_t *ed = data;
1813 
1814         assert(wip->inst != NULL);
1815         assert(wip->pg == NULL);
1816 
1817         if (svcsearch) {
1818                 char state[MAX_SCF_STATE_STRING_SZ];
1819 
1820                 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
1821                         return (0);
1822                 if (strcmp(state, svcstate) != 0)
1823                         return (0);
1824         }
1825 
1826         if (ed->ed_flags & SET_RECURSIVE) {
1827                 char *fmri_buf = malloc(max_scf_fmri_sz);
1828                 if (fmri_buf == NULL)
1829                         uu_die(emsg_nomem);
1830 
1831                 visited = calloc(HT_BUCKETS, sizeof (*visited));
1832                 if (visited == NULL)
1833                         uu_die(emsg_nomem);
1834 
1835                 /* scf_walk_fmri() guarantees that fmri isn't too long */
1836                 assert(strlen(wip->fmri) <= max_scf_fmri_sz);
1837                 (void) strlcpy(fmri_buf, wip->fmri, max_scf_fmri_sz);
1838 
1839                 switch (enable_fmri_rec(fmri_buf, ed)) {
1840                 case E2BIG:
1841                         uu_warn(gettext("operation on service %s is ambiguous; "
1842                             "instance specification needed.\n"), fmri_buf);
1843                         break;
1844 
1845                 case ELOOP:
1846                         uu_warn(gettext("%s: Dependency cycle detected.\n"),
1847                             fmri_buf);
1848                 }
1849 
1850                 free(visited);
1851                 free(fmri_buf);
1852 
1853         } else {
1854                 set_inst_enabled(wip->fmri, wip->inst, ed);

1855         }
1856 
1857         return (0);
1858 }
1859 
1860 /* ARGSUSED */
1861 static int
1862 wait_fmri_enabled(void *data, scf_walkinfo_t *wip)
1863 {
1864         scf_propertygroup_t *pg = NULL;
1865         char state[MAX_SCF_STATE_STRING_SZ];
1866 
1867         assert(wip->inst != NULL);
1868         assert(wip->pg == NULL);
1869 
1870         do {
1871                 if (pg)
1872                         scf_pg_destroy(pg);
1873                 if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) {
1874                         exit_status = EXIT_SVC_FAILURE;


2085         if (flags & MARK_IMMEDIATE) {
2086                 prop = (flags & MARK_TEMPORARY) ?
2087                     SCF_PROPERTY_MAINT_ON_IMMTEMP :
2088                     SCF_PROPERTY_MAINT_ON_IMMEDIATE;
2089         } else {
2090                 prop = (flags & MARK_TEMPORARY) ?
2091                     SCF_PROPERTY_MAINT_ON_TEMPORARY :
2092                     SCF_PROPERTY_MAINT_ON;
2093         }
2094 
2095         set_inst_action(wip->fmri, wip->inst, prop);
2096 
2097         return (0);
2098 }
2099 
2100 static void
2101 set_milestone(const char *fmri, boolean_t temporary)
2102 {
2103         scf_instance_t *inst;
2104         scf_propertygroup_t *pg;

2105 
2106         if (temporary) {
2107                 set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS_OVR,
2108                     SCF_PG_OPTIONS_OVR_TYPE, SCF_PG_OPTIONS_OVR_FLAGS,
2109                     SCF_PROPERTY_MILESTONE, fmri);
2110                 return;
2111         }
2112 
2113         if ((inst = scf_instance_create(h)) == NULL ||
2114             (pg = scf_pg_create(h)) == NULL)
2115                 scfdie();
2116 
2117         if (get_inst(SCF_SERVICE_STARTD, inst) != 0) {
2118                 scf_instance_destroy(inst);
2119                 return;
2120         }
2121 
2122         /*
2123          * Set the persistent milestone before deleting the override so we don't
2124          * glitch.
2125          */
2126         set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS,
2127             SCF_PG_OPTIONS_TYPE, SCF_PG_OPTIONS_FLAGS, SCF_PROPERTY_MILESTONE,
2128             fmri);
2129 
2130         if (delete_prop(SCF_SERVICE_STARTD, inst, SCF_PG_OPTIONS_OVR,
2131             SCF_PROPERTY_MILESTONE) != 0)






2132                 exit_status = 1;

2133 


























2134         scf_pg_destroy(pg);
2135         scf_instance_destroy(inst);
2136 }
2137 
2138 static char const *milestones[] = {
2139         SCF_MILESTONE_SINGLE_USER,
2140         SCF_MILESTONE_MULTI_USER,
2141         SCF_MILESTONE_MULTI_USER_SERVER,
2142         NULL
2143 };
2144 
2145 static void
2146 usage_milestone(void)
2147 {
2148         const char **ms;
2149 
2150         (void) fprintf(stderr, gettext(
2151         "Usage: svcadm milestone [-d] <milestone>\n\n"
2152         "\t-d\tmake the specified milestone the default for system boot\n\n"
2153         "\tMilestones can be specified using an FMRI or abbreviation.\n"


2394 
2395                 scf_value_destroy(zone);
2396         }
2397 
2398         if (scf_handle_bind(h) == -1) {
2399                 if (do_zones)
2400                         goto nextzone;
2401 
2402                 uu_die(gettext("Couldn't bind to configuration repository: "
2403                     "%s.\n"), scf_strerror(scf_error()));
2404         }
2405 
2406         optind = orig_optind;
2407         argc = orig_argc;
2408         argv = orig_argv;
2409 
2410         if (optind >= argc)
2411                 usage();
2412 
2413         if (strcmp(argv[optind], "enable") == 0) {
2414                 enable_data_t ed = {
2415                         .ed_flags = SET_ENABLED,
2416                         .ed_comment = ""
2417                 };
2418                 int wait = 0;
2419                 int error = 0;
2420 
2421                 ++optind;
2422 
2423                 while ((o = getopt(argc, argv, "rst")) != -1) {
2424                         if (o == 'r')
2425                                 ed.ed_flags |= SET_RECURSIVE;
2426                         else if (o == 't')
2427                                 ed.ed_flags |= SET_TEMPORARY;
2428                         else if (o == 's')
2429                                 wait = 1;
2430                         else if (o == '?')
2431                                 usage();
2432                         else {
2433                                 assert(0);
2434                                 abort();
2435                         }
2436                 }
2437                 argc -= optind;
2438                 argv += optind;
2439 
2440                 if (argc == 0 && !svcsearch)
2441                         usage();
2442 
2443                 if (argc > 0 && svcsearch)
2444                         usage();
2445 
2446                 /*
2447                  * We want to continue with -s processing if we had
2448                  * invalid options, but not if an enable failed.  We
2449                  * squelch output the second time we walk fmris; we saw
2450                  * the errors the first time.
2451                  */
2452                 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2453                     set_fmri_enabled, &ed, &error, pr_warn)) != 0) {
2454 
2455                         pr_warn(gettext("failed to iterate over "
2456                             "instances: %s\n"), scf_strerror(err));
2457                         exit_status = UU_EXIT_FATAL;
2458 
2459                 } else if (wait && exit_status == 0 &&
2460                     (err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2461                     wait_fmri_enabled, NULL, &error, quiet)) != 0) {
2462 
2463                         pr_warn(gettext("failed to iterate over "
2464                             "instances: %s\n"), scf_strerror(err));
2465                         exit_status = UU_EXIT_FATAL;
2466                 }
2467 
2468                 if (error > 0)
2469                         exit_status = error;
2470 
2471         } else if (strcmp(argv[optind], "disable") == 0) {
2472                 enable_data_t ed = {
2473                         .ed_flags = 0,
2474                         .ed_comment = ""
2475                 };
2476                 int wait = 0;
2477                 int error = 0;
2478 
2479                 ++optind;
2480 
2481                 while ((o = getopt(argc, argv, "c:st")) != -1) {
2482                         if (o == 'c') {
2483                                 if (strlcpy(ed.ed_comment, optarg,
2484                                     sizeof (ed.ed_comment)) >=
2485                                     sizeof (ed.ed_comment)) {
2486                                         uu_die(gettext("disable -c comment "
2487                                             "too long.\n"));
2488                                 }
2489                         } else if (o == 't')
2490                                 ed.ed_flags |= SET_TEMPORARY;
2491                         else if (o == 's')
2492                                 wait = 1;
2493                         else if (o == '?')
2494                                 usage();
2495                         else {
2496                                 assert(0);
2497                                 abort();
2498                         }
2499                 }
2500                 argc -= optind;
2501                 argv += optind;
2502 
2503                 if (argc == 0 && !svcsearch)
2504                         usage();
2505 
2506                 if (argc > 0 && svcsearch)
2507                         usage();
2508 
2509                 /*
2510                  * We want to continue with -s processing if we had
2511                  * invalid options, but not if a disable failed.  We
2512                  * squelch output the second time we walk fmris; we saw
2513                  * the errors the first time.
2514                  */
2515                 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2516                     set_fmri_enabled, &ed, &exit_status,
2517                     pr_warn)) != 0) {
2518 
2519                         pr_warn(gettext("failed to iterate over "
2520                             "instances: %s\n"), scf_strerror(err));
2521                         exit_status = UU_EXIT_FATAL;
2522 
2523                 } else if (wait && exit_status == 0 &&
2524                     (err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2525                     wait_fmri_disabled, NULL, &error, quiet)) != 0) {
2526 
2527                         pr_warn(gettext("failed to iterate over "
2528                             "instances: %s\n"), scf_strerror(err));
2529                         exit_status = UU_EXIT_FATAL;
2530                 }
2531 
2532                 if (error > 0)
2533                         exit_status = error;
2534 
2535         } else if (strcmp(argv[optind], "restart") == 0) {
2536                 boolean_t do_dump = B_FALSE;
2537 
2538                 ++optind;
2539 
2540                 while ((o = getopt(argc, argv, "d")) != -1) {
2541                         if (o == 'd')
2542                                 do_dump = B_TRUE;
2543                         else if (o == '?')
2544                                 usage();
2545                         else {