1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 /*
  27  * startd.c - the master restarter
  28  *
  29  * svc.startd comprises two halves.  The graph engine is based in graph.c and
  30  * maintains the service dependency graph based on the information in the
  31  * repository.  For each service it also tracks the current state and the
  32  * restarter responsible for the service.  Based on the graph, events from the
  33  * repository (mostly administrative requests from svcadm), and messages from
  34  * the restarters, the graph engine makes decisions about how the services
  35  * should be manipulated and sends commands to the appropriate restarters.
  36  * Communication between the graph engine and the restarters is embodied in
  37  * protocol.c.
  38  *
  39  * The second half of svc.startd is the restarter for services managed by
  40  * svc.startd and is primarily contained in restarter.c.  It responds to graph
  41  * engine commands by executing methods, updating the repository, and sending
  42  * feedback (mostly state updates) to the graph engine.
  43  *
  44  * Error handling
  45  *
  46  * In general, when svc.startd runs out of memory it reattempts a few times,
  47  * sleeping inbetween, before giving up and exiting (see startd_alloc_retry()).
  48  * When a repository connection is broken (libscf calls fail with
  49  * SCF_ERROR_CONNECTION_BROKEN, librestart and internal functions return
  50  * ECONNABORTED), svc.startd calls libscf_rebind_handle(), which coordinates
  51  * with the svc.configd-restarting thread, fork_configd_thread(), via
  52  * st->st_configd_live_cv, and rebinds the repository handle.  Doing so resets
  53  * all libscf state associated with that handle, so functions which do this
  54  * should communicate the event to their callers (usually by returning
  55  * ECONNRESET) so they may reset their state appropriately.
  56  *
  57  * External references
  58  *
  59  * svc.configd generates special security audit events for changes to some
  60  * restarter related properties.  See the special_props_list array in
  61  * usr/src/cmd/svc/configd/rc_node.c for the properties that cause these audit
  62  * events.  If you change the semantics of these propereties within startd, you
  63  * will probably need to update rc_node.c
  64  */
  65 
  66 #include <stdio.h>
  67 #include <stdio_ext.h>
  68 #include <sys/mnttab.h>           /* uses FILE * without including stdio.h */
  69 #include <alloca.h>
  70 #include <sys/mount.h>
  71 #include <sys/stat.h>
  72 #include <sys/types.h>
  73 #include <sys/wait.h>
  74 #include <assert.h>
  75 #include <errno.h>
  76 #include <fcntl.h>
  77 #include <ftw.h>
  78 #include <libintl.h>
  79 #include <libscf.h>
  80 #include <libscf_priv.h>
  81 #include <libuutil.h>
  82 #include <locale.h>
  83 #include <poll.h>
  84 #include <pthread.h>
  85 #include <signal.h>
  86 #include <stdarg.h>
  87 #include <stdlib.h>
  88 #include <string.h>
  89 #include <strings.h>
  90 #include <unistd.h>
  91 
  92 #include "startd.h"
  93 #include "protocol.h"
  94 
  95 ssize_t max_scf_name_size;
  96 ssize_t max_scf_fmri_size;
  97 ssize_t max_scf_value_size;
  98 
  99 mode_t fmask;
 100 mode_t dmask;
 101 
 102 graph_update_t *gu;
 103 restarter_update_t *ru;
 104 
 105 startd_state_t *st;
 106 
 107 boolean_t booting_to_single_user = B_FALSE;
 108 
 109 const char * const admin_actions[] = {
 110     SCF_PROPERTY_DEGRADED,
 111     SCF_PROPERTY_MAINT_OFF,
 112     SCF_PROPERTY_MAINT_ON,
 113     SCF_PROPERTY_MAINT_ON_IMMEDIATE,
 114     SCF_PROPERTY_REFRESH,
 115     SCF_PROPERTY_RESTART
 116 };
 117 
 118 const int admin_events[NACTIONS] = {
 119     RESTARTER_EVENT_TYPE_ADMIN_DEGRADED,
 120     RESTARTER_EVENT_TYPE_ADMIN_MAINT_OFF,
 121     RESTARTER_EVENT_TYPE_ADMIN_MAINT_ON,
 122     RESTARTER_EVENT_TYPE_ADMIN_MAINT_ON_IMMEDIATE,
 123     RESTARTER_EVENT_TYPE_ADMIN_REFRESH,
 124     RESTARTER_EVENT_TYPE_ADMIN_RESTART
 125 };
 126 
 127 const char * const instance_state_str[] = {
 128         "none",
 129         "uninitialized",
 130         "maintenance",
 131         "offline",
 132         "disabled",
 133         "online",
 134         "degraded"
 135 };
 136 
 137 static int finished = 0;
 138 static int opt_reconfig = 0;
 139 static uint8_t prop_reconfig = 0;
 140 
 141 #define INITIAL_REBIND_ATTEMPTS 5
 142 #define INITIAL_REBIND_DELAY    3
 143 
 144 pthread_mutexattr_t mutex_attrs;
 145 
 146 const char *
 147 _umem_debug_init(void)
 148 {
 149         return ("default,verbose");     /* UMEM_DEBUG setting */
 150 }
 151 
 152 const char *
 153 _umem_logging_init(void)
 154 {
 155         return ("fail,contents");       /* UMEM_LOGGING setting */
 156 }
 157 
 158 /*
 159  * startd_alloc_retry()
 160  *   Wrapper for allocation functions.  Retries with a decaying time
 161  *   value on failure to allocate, and aborts startd if failure is
 162  *   persistent.
 163  */
 164 void *
 165 startd_alloc_retry(void *f(size_t, int), size_t sz)
 166 {
 167         void *p;
 168         uint_t try, msecs;
 169 
 170         p = f(sz, UMEM_DEFAULT);
 171         if (p != NULL || sz == 0)
 172                 return (p);
 173 
 174         msecs = ALLOC_DELAY;
 175 
 176         for (try = 0; p == NULL && try < ALLOC_RETRY; ++try) {
 177                 (void) poll(NULL, 0, msecs);
 178                 msecs *= ALLOC_DELAY_MULT;
 179                 p = f(sz, UMEM_DEFAULT);
 180                 if (p != NULL)
 181                         return (p);
 182         }
 183 
 184         uu_die("Insufficient memory.\n");
 185         /* NOTREACHED */
 186 }
 187 
 188 void *
 189 safe_realloc(void *p, size_t sz)
 190 {
 191         uint_t try, msecs;
 192 
 193         p = realloc(p, sz);
 194         if (p != NULL || sz == 0)
 195                 return (p);
 196 
 197         msecs = ALLOC_DELAY;
 198 
 199         for (try = 0; errno == EAGAIN && try < ALLOC_RETRY; ++try) {
 200                 (void) poll(NULL, 0, msecs);
 201                 p = realloc(p, sz);
 202                 if (p != NULL)
 203                         return (p);
 204                 msecs *= ALLOC_DELAY_MULT;
 205         }
 206 
 207         uu_die("Insufficient memory.\n");
 208         /* NOTREACHED */
 209 }
 210 
 211 char *
 212 safe_strdup(const char *s)
 213 {
 214         uint_t try, msecs;
 215         char *d;
 216 
 217         d = strdup(s);
 218         if (d != NULL)
 219                 return (d);
 220 
 221         msecs = ALLOC_DELAY;
 222 
 223         for (try = 0;
 224             (errno == EAGAIN || errno == ENOMEM) && try < ALLOC_RETRY;
 225             ++try) {
 226                 (void) poll(NULL, 0, msecs);
 227                 d = strdup(s);
 228                 if (d != NULL)
 229                         return (d);
 230                 msecs *= ALLOC_DELAY_MULT;
 231         }
 232 
 233         uu_die("Insufficient memory.\n");
 234         /* NOTREACHED */
 235 }
 236 
 237 
 238 void
 239 startd_free(void *p, size_t sz)
 240 {
 241         umem_free(p, sz);
 242 }
 243 
 244 /*
 245  * Creates a uu_list_pool_t with the same retry policy as startd_alloc().
 246  * Only returns NULL for UU_ERROR_UNKNOWN_FLAG and UU_ERROR_NOT_SUPPORTED.
 247  */
 248 uu_list_pool_t *
 249 startd_list_pool_create(const char *name, size_t e, size_t o,
 250     uu_compare_fn_t *f, uint32_t flags)
 251 {
 252         uu_list_pool_t *pool;
 253         uint_t try, msecs;
 254 
 255         pool = uu_list_pool_create(name, e, o, f, flags);
 256         if (pool != NULL)
 257                 return (pool);
 258 
 259         msecs = ALLOC_DELAY;
 260 
 261         for (try = 0; uu_error() == UU_ERROR_NO_MEMORY && try < ALLOC_RETRY;
 262             ++try) {
 263                 (void) poll(NULL, 0, msecs);
 264                 pool = uu_list_pool_create(name, e, o, f, flags);
 265                 if (pool != NULL)
 266                         return (pool);
 267                 msecs *= ALLOC_DELAY_MULT;
 268         }
 269 
 270         if (try < ALLOC_RETRY)
 271                 return (NULL);
 272 
 273         uu_die("Insufficient memory.\n");
 274         /* NOTREACHED */
 275 }
 276 
 277 /*
 278  * Creates a uu_list_t with the same retry policy as startd_alloc().  Only
 279  * returns NULL for UU_ERROR_UNKNOWN_FLAG and UU_ERROR_NOT_SUPPORTED.
 280  */
 281 uu_list_t *
 282 startd_list_create(uu_list_pool_t *pool, void *parent, uint32_t flags)
 283 {
 284         uu_list_t *list;
 285         uint_t try, msecs;
 286 
 287         list = uu_list_create(pool, parent, flags);
 288         if (list != NULL)
 289                 return (list);
 290 
 291         msecs = ALLOC_DELAY;
 292 
 293         for (try = 0; uu_error() == UU_ERROR_NO_MEMORY && try < ALLOC_RETRY;
 294             ++try) {
 295                 (void) poll(NULL, 0, msecs);
 296                 list = uu_list_create(pool, parent, flags);
 297                 if (list != NULL)
 298                         return (list);
 299                 msecs *= ALLOC_DELAY_MULT;
 300         }
 301 
 302         if (try < ALLOC_RETRY)
 303                 return (NULL);
 304 
 305         uu_die("Insufficient memory.\n");
 306         /* NOTREACHED */
 307 }
 308 
 309 pthread_t
 310 startd_thread_create(void *(*func)(void *), void *ptr)
 311 {
 312         int err;
 313         pthread_t tid;
 314 
 315         err = pthread_create(&tid, NULL, func, ptr);
 316         if (err != 0) {
 317                 assert(err == EAGAIN);
 318                 uu_die("Could not create thread.\n");
 319         }
 320 
 321         err = pthread_detach(tid);
 322         assert(err == 0);
 323 
 324         return (tid);
 325 }
 326 
 327 extern int info_events_all;
 328 
 329 static int
 330 read_startd_config(void)
 331 {
 332         scf_handle_t *hndl;
 333         scf_instance_t *inst;
 334         scf_propertygroup_t *pg;
 335         scf_property_t *prop;
 336         scf_value_t *val;
 337         scf_iter_t *iter, *piter;
 338         instance_data_t idata;
 339         char *buf, *vbuf;
 340         char *startd_options_fmri = uu_msprintf("%s/:properties/options",
 341             SCF_SERVICE_STARTD);
 342         char *startd_reconfigure_fmri = uu_msprintf(
 343             "%s/:properties/system/reconfigure", SCF_SERVICE_STARTD);
 344         char *env_opts, *lasts, *cp;
 345         int bind_fails = 0;
 346         int ret = 0, r;
 347         uint_t count = 0, msecs = ALLOC_DELAY;
 348         size_t sz;
 349         ctid_t ctid;
 350         uint64_t uint64;
 351 
 352         buf = startd_alloc(max_scf_fmri_size);
 353 
 354         if (startd_options_fmri == NULL || startd_reconfigure_fmri == NULL)
 355                 uu_die("Allocation failure\n");
 356 
 357         st->st_log_prefix = LOG_PREFIX_EARLY;
 358 
 359         if ((st->st_log_file = getenv("STARTD_DEFAULT_LOG")) == NULL) {
 360                 st->st_log_file = startd_alloc(strlen(STARTD_DEFAULT_LOG) + 1);
 361 
 362                 (void) strcpy(st->st_log_file, STARTD_DEFAULT_LOG);
 363         }
 364 
 365         st->st_door_path = getenv("STARTD_ALT_DOOR");
 366 
 367         /*
 368          * Read "options" property group.
 369          */
 370         for (hndl = libscf_handle_create_bound(SCF_VERSION); hndl == NULL;
 371             hndl = libscf_handle_create_bound(SCF_VERSION), bind_fails++) {
 372                 (void) sleep(INITIAL_REBIND_DELAY);
 373 
 374                 if (bind_fails > INITIAL_REBIND_ATTEMPTS) {
 375                         /*
 376                          * In the case that we can't bind to the repository
 377                          * (which should have been started), we need to allow
 378                          * the user into maintenance mode to determine what's
 379                          * failed.
 380                          */
 381                         log_framework(LOG_INFO, "Couldn't fetch "
 382                             "default settings: %s\n",
 383                             scf_strerror(scf_error()));
 384 
 385                         ret = -1;
 386 
 387                         goto noscfout;
 388                 }
 389         }
 390 
 391         idata.i_fmri = SCF_SERVICE_STARTD;
 392         idata.i_state = RESTARTER_STATE_NONE;
 393         idata.i_next_state = RESTARTER_STATE_NONE;
 394 timestamp:
 395         switch (r = _restarter_commit_states(hndl, &idata,
 396             RESTARTER_STATE_ONLINE, RESTARTER_STATE_NONE,
 397             restarter_get_str_short(restarter_str_insert_in_graph))) {
 398         case 0:
 399                 break;
 400 
 401         case ENOMEM:
 402                 ++count;
 403                 if (count < ALLOC_RETRY) {
 404                         (void) poll(NULL, 0, msecs);
 405                         msecs *= ALLOC_DELAY_MULT;
 406                         goto timestamp;
 407                 }
 408 
 409                 uu_die("Insufficient memory.\n");
 410                 /* NOTREACHED */
 411 
 412         case ECONNABORTED:
 413                 libscf_handle_rebind(hndl);
 414                 goto timestamp;
 415 
 416         case ENOENT:
 417         case EPERM:
 418         case EACCES:
 419         case EROFS:
 420                 log_error(LOG_INFO, "Could set state of %s: %s.\n",
 421                     idata.i_fmri, strerror(r));
 422                 break;
 423 
 424         case EINVAL:
 425         default:
 426                 bad_error("_restarter_commit_states", r);
 427         }
 428 
 429         pg = safe_scf_pg_create(hndl);
 430         prop = safe_scf_property_create(hndl);
 431         val = safe_scf_value_create(hndl);
 432         inst = safe_scf_instance_create(hndl);
 433 
 434         /* set startd's restarter properties */
 435         if (scf_handle_decode_fmri(hndl, SCF_SERVICE_STARTD, NULL, NULL, inst,
 436             NULL, NULL, SCF_DECODE_FMRI_EXACT) == 0) {
 437                 (void) libscf_write_start_pid(inst, getpid());
 438                 ctid = proc_get_ctid();
 439                 if (ctid != -1) {
 440                         uint64 = (uint64_t)ctid;
 441                         (void) libscf_inst_set_count_prop(inst,
 442                             SCF_PG_RESTARTER, SCF_PG_RESTARTER_TYPE,
 443                             SCF_PG_RESTARTER_FLAGS, SCF_PROPERTY_CONTRACT,
 444                             uint64);
 445                 }
 446                 (void) libscf_note_method_log(inst, LOG_PREFIX_EARLY,
 447                     STARTD_DEFAULT_LOG);
 448                 (void) libscf_note_method_log(inst, LOG_PREFIX_NORMAL,
 449                     STARTD_DEFAULT_LOG);
 450         }
 451 
 452         /* Read reconfigure property for recovery. */
 453         if (scf_handle_decode_fmri(hndl, startd_reconfigure_fmri, NULL, NULL,
 454             NULL, NULL, prop, NULL) != -1 &&
 455             scf_property_get_value(prop, val) == 0)
 456                 (void) scf_value_get_boolean(val, &prop_reconfig);
 457 
 458         if (scf_handle_decode_fmri(hndl, startd_options_fmri, NULL, NULL, NULL,
 459             pg, NULL, SCF_DECODE_FMRI_TRUNCATE) == -1) {
 460                 /*
 461                  * No configuration options defined.
 462                  */
 463                 if (scf_error() != SCF_ERROR_NOT_FOUND)
 464                         uu_warn("Couldn't read configuration from 'options' "
 465                             "group: %s\n", scf_strerror(scf_error()));
 466                 goto scfout;
 467         }
 468 
 469         /*
 470          * If there is no "options" group defined, then our defaults are fine.
 471          */
 472         if (scf_pg_get_name(pg, NULL, 0) < 0)
 473                 goto scfout;
 474 
 475         /* get info_events_all */
 476         info_events_all = libscf_get_info_events_all(pg);
 477 
 478         /* Iterate through. */
 479         iter = safe_scf_iter_create(hndl);
 480 
 481         (void) scf_iter_pg_properties(iter, pg);
 482 
 483         piter = safe_scf_iter_create(hndl);
 484         vbuf = startd_alloc(max_scf_value_size);
 485 
 486         while ((scf_iter_next_property(iter, prop) == 1)) {
 487                 scf_type_t ty;
 488 
 489                 if (scf_property_get_name(prop, buf, max_scf_fmri_size) < 0)
 490                         continue;
 491 
 492                 if (strcmp(buf, "logging") != 0 &&
 493                     strcmp(buf, "boot_messages") != 0)
 494                         continue;
 495 
 496                 if (scf_property_type(prop, &ty) != 0) {
 497                         switch (scf_error()) {
 498                         case SCF_ERROR_CONNECTION_BROKEN:
 499                         default:
 500                                 libscf_handle_rebind(hndl);
 501                                 continue;
 502 
 503                         case SCF_ERROR_DELETED:
 504                                 continue;
 505 
 506                         case SCF_ERROR_NOT_BOUND:
 507                         case SCF_ERROR_NOT_SET:
 508                                 bad_error("scf_property_type", scf_error());
 509                         }
 510                 }
 511 
 512                 if (ty != SCF_TYPE_ASTRING) {
 513                         uu_warn("property \"options/%s\" is not of type "
 514                             "astring; ignored.\n", buf);
 515                         continue;
 516                 }
 517 
 518                 if (scf_property_get_value(prop, val) != 0) {
 519                         switch (scf_error()) {
 520                         case SCF_ERROR_CONNECTION_BROKEN:
 521                         default:
 522                                 return (ECONNABORTED);
 523 
 524                         case SCF_ERROR_DELETED:
 525                         case SCF_ERROR_NOT_FOUND:
 526                                 return (0);
 527 
 528                         case SCF_ERROR_CONSTRAINT_VIOLATED:
 529                                 uu_warn("property \"options/%s\" has multiple "
 530                                     "values; ignored.\n", buf);
 531                                 continue;
 532 
 533                         case SCF_ERROR_PERMISSION_DENIED:
 534                                 uu_warn("property \"options/%s\" cannot be "
 535                                     "read because startd has insufficient "
 536                                     "permission; ignored.\n", buf);
 537                                 continue;
 538 
 539                         case SCF_ERROR_HANDLE_MISMATCH:
 540                         case SCF_ERROR_NOT_BOUND:
 541                         case SCF_ERROR_NOT_SET:
 542                                 bad_error("scf_property_get_value",
 543                                     scf_error());
 544                         }
 545                 }
 546 
 547                 if (scf_value_get_astring(val, vbuf, max_scf_value_size) < 0)
 548                         bad_error("scf_value_get_astring", scf_error());
 549 
 550                 if (strcmp("logging", buf) == 0) {
 551                         if (strcmp("verbose", vbuf) == 0) {
 552                                 st->st_boot_flags = STARTD_BOOT_VERBOSE;
 553                                 st->st_log_level_min = LOG_INFO;
 554                         } else if (strcmp("debug", vbuf) == 0) {
 555                                 st->st_boot_flags = STARTD_BOOT_VERBOSE;
 556                                 st->st_log_level_min = LOG_DEBUG;
 557                         } else if (strcmp("quiet", vbuf) == 0) {
 558                                 st->st_log_level_min = LOG_NOTICE;
 559                         } else {
 560                                 uu_warn("unknown options/logging "
 561                                     "value '%s' ignored\n", vbuf);
 562                         }
 563 
 564                 } else if (strcmp("boot_messages", buf) == 0) {
 565                         if (strcmp("quiet", vbuf) == 0) {
 566                                 st->st_boot_flags = STARTD_BOOT_QUIET;
 567                         } else if (strcmp("verbose", vbuf) == 0) {
 568                                 st->st_boot_flags = STARTD_BOOT_VERBOSE;
 569                         } else {
 570                                 log_framework(LOG_NOTICE, "unknown "
 571                                     "options/boot_messages value '%s' "
 572                                     "ignored\n", vbuf);
 573                         }
 574 
 575                 }
 576         }
 577 
 578         startd_free(vbuf, max_scf_value_size);
 579         scf_iter_destroy(piter);
 580 
 581         scf_iter_destroy(iter);
 582 
 583 scfout:
 584         scf_value_destroy(val);
 585         scf_pg_destroy(pg);
 586         scf_property_destroy(prop);
 587         scf_instance_destroy(inst);
 588         (void) scf_handle_unbind(hndl);
 589         scf_handle_destroy(hndl);
 590 
 591 noscfout:
 592         startd_free(buf, max_scf_fmri_size);
 593         uu_free(startd_options_fmri);
 594         uu_free(startd_reconfigure_fmri);
 595 
 596         if (booting_to_single_user) {
 597                 st->st_subgraph = startd_alloc(max_scf_fmri_size);
 598                 sz = strlcpy(st->st_subgraph, "milestone/single-user:default",
 599                     max_scf_fmri_size);
 600                 assert(sz < max_scf_fmri_size);
 601         }
 602 
 603         /*
 604          * Options passed in as boot arguments override repository defaults.
 605          */
 606         env_opts = getenv("SMF_OPTIONS");
 607         if (env_opts == NULL)
 608                 return (ret);
 609 
 610         for (cp = strtok_r(env_opts, ",", &lasts); cp != NULL;
 611             cp = strtok_r(NULL, ",", &lasts)) {
 612                 if (strcmp(cp, "debug") == 0) {
 613                         st->st_boot_flags = STARTD_BOOT_VERBOSE;
 614                         st->st_log_level_min = LOG_DEBUG;
 615 
 616                         /* -m debug should send messages to console */
 617                         st->st_log_flags =
 618                             st->st_log_flags | STARTD_LOG_TERMINAL;
 619                 } else if (strcmp(cp, "verbose") == 0) {
 620                         st->st_boot_flags = STARTD_BOOT_VERBOSE;
 621                         st->st_log_level_min = LOG_INFO;
 622                 } else if (strcmp(cp, "seed") == 0) {
 623                         uu_warn("SMF option \"%s\" unimplemented.\n", cp);
 624                 } else if (strcmp(cp, "quiet") == 0) {
 625                         st->st_log_level_min = LOG_NOTICE;
 626                 } else if (strncmp(cp, "milestone=",
 627                     sizeof ("milestone=") - 1) == 0) {
 628                         char *mp = cp + sizeof ("milestone=") - 1;
 629 
 630                         if (booting_to_single_user)
 631                                 continue;
 632 
 633                         if (st->st_subgraph == NULL) {
 634                                 st->st_subgraph =
 635                                     startd_alloc(max_scf_fmri_size);
 636                                 st->st_subgraph[0] = '\0';
 637                         }
 638 
 639                         if (mp[0] == '\0' || strcmp(mp, "all") == 0) {
 640                                 (void) strcpy(st->st_subgraph, "all");
 641                         } else if (strcmp(mp, "su") == 0 ||
 642                             strcmp(mp, "single-user") == 0) {
 643                                 (void) strcpy(st->st_subgraph,
 644                                     "milestone/single-user:default");
 645                         } else if (strcmp(mp, "mu") == 0 ||
 646                             strcmp(mp, "multi-user") == 0) {
 647                                 (void) strcpy(st->st_subgraph,
 648                                     "milestone/multi-user:default");
 649                         } else if (strcmp(mp, "mus") == 0 ||
 650                             strcmp(mp, "multi-user-server") == 0) {
 651                                 (void) strcpy(st->st_subgraph,
 652                                     "milestone/multi-user-server:default");
 653                         } else if (strcmp(mp, "none") == 0) {
 654                                 (void) strcpy(st->st_subgraph, "none");
 655                         } else {
 656                                 log_framework(LOG_NOTICE,
 657                                     "invalid milestone option value "
 658                                     "'%s' ignored\n", mp);
 659                         }
 660                 } else {
 661                         uu_warn("Unknown SMF option \"%s\".\n", cp);
 662                 }
 663         }
 664 
 665         return (ret);
 666 }
 667 
 668 /*
 669  * void set_boot_env()
 670  *
 671  * If -r was passed or /reconfigure exists, this is a reconfig
 672  * reboot.  We need to make sure that this information is given
 673  * to the appropriate services the first time they're started
 674  * by setting the system/reconfigure repository property,
 675  * as well as pass the _INIT_RECONFIG variable on to the rcS
 676  * start method so that legacy services can continue to use it.
 677  *
 678  * This function must never be called before contract_init(), as
 679  * it sets st_initial.  get_startd_config() sets prop_reconfig from
 680  * pre-existing repository state.
 681  */
 682 static void
 683 set_boot_env()
 684 {
 685         struct stat sb;
 686         int r;
 687 
 688         /*
 689          * Check if property still is set -- indicates we didn't get
 690          * far enough previously to unset it.  Otherwise, if this isn't
 691          * the first startup, don't re-process /reconfigure or the
 692          * boot flag.
 693          */
 694         if (prop_reconfig != 1 && st->st_initial != 1)
 695                 return;
 696 
 697         /* If /reconfigure exists, also set opt_reconfig. */
 698         if (stat("/reconfigure", &sb) != -1)
 699                 opt_reconfig = 1;
 700 
 701         /* Nothing to do.  Just return. */
 702         if (opt_reconfig == 0 && prop_reconfig == 0)
 703                 return;
 704 
 705         /*
 706          * Set startd's reconfigure property.  This property is
 707          * then cleared by successful completion of the single-user
 708          * milestone.
 709          */
 710         if (prop_reconfig != 1) {
 711                 r = libscf_set_reconfig(1);
 712                 switch (r) {
 713                 case 0:
 714                         break;
 715 
 716                 case ENOENT:
 717                 case EPERM:
 718                 case EACCES:
 719                 case EROFS:
 720                         log_error(LOG_WARNING, "Could not set reconfiguration "
 721                             "property: %s\n", strerror(r));
 722                         break;
 723 
 724                 default:
 725                         bad_error("libscf_set_reconfig", r);
 726                 }
 727         }
 728 }
 729 
 730 static void
 731 startup(void)
 732 {
 733         ctid_t configd_ctid;
 734         int err;
 735 
 736         /*
 737          * Initialize data structures.
 738          */
 739         gu = startd_zalloc(sizeof (graph_update_t));
 740         ru = startd_zalloc(sizeof (restarter_update_t));
 741 
 742         (void) pthread_cond_init(&st->st_load_cv, NULL);
 743         (void) pthread_cond_init(&st->st_configd_live_cv, NULL);
 744         (void) pthread_cond_init(&gu->gu_cv, NULL);
 745         (void) pthread_cond_init(&gu->gu_freeze_cv, NULL);
 746         (void) pthread_cond_init(&ru->restarter_update_cv, NULL);
 747         (void) pthread_mutex_init(&st->st_load_lock, &mutex_attrs);
 748         (void) pthread_mutex_init(&st->st_configd_live_lock, &mutex_attrs);
 749         (void) pthread_mutex_init(&gu->gu_lock, &mutex_attrs);
 750         (void) pthread_mutex_init(&gu->gu_freeze_lock, &mutex_attrs);
 751         (void) pthread_mutex_init(&ru->restarter_update_lock, &mutex_attrs);
 752 
 753         configd_ctid = contract_init();
 754 
 755         if (configd_ctid != -1)
 756                 log_framework(LOG_DEBUG, "Existing configd contract %ld; not "
 757                     "starting svc.configd\n", configd_ctid);
 758 
 759         (void) startd_thread_create(fork_configd_thread, (void *)configd_ctid);
 760 
 761         /*
 762          * Await, if necessary, configd's initial arrival.
 763          */
 764         MUTEX_LOCK(&st->st_configd_live_lock);
 765         while (!st->st_configd_lives) {
 766                 log_framework(LOG_DEBUG, "Awaiting cv signal on "
 767                     "configd_live_cv\n");
 768                 err = pthread_cond_wait(&st->st_configd_live_cv,
 769                     &st->st_configd_live_lock);
 770                 assert(err == 0);
 771         }
 772         MUTEX_UNLOCK(&st->st_configd_live_lock);
 773 
 774         utmpx_init();
 775         wait_init();
 776 
 777         if (read_startd_config())
 778                 log_framework(LOG_INFO, "svc.configd unable to provide startd "
 779                     "optional settings\n");
 780 
 781         log_init();
 782         dict_init();
 783         timeout_init();
 784         restarter_protocol_init();
 785         restarter_init();
 786 
 787         /*
 788          * svc.configd is started by fork_configd_thread so repository access is
 789          * available, run early manifest import before continuing with starting
 790          * graph engine and the rest of startd.
 791          */
 792         log_framework(LOG_DEBUG, "Calling fork_emi...\n");
 793         fork_emi();
 794 
 795         graph_protocol_init();
 796         graph_init();
 797 
 798         init_env();
 799 
 800         set_boot_env();
 801         restarter_start();
 802         graph_engine_start();
 803 }
 804 
 805 static void
 806 usage(const char *name)
 807 {
 808         uu_warn(gettext("usage: %s [-n]\n"), name);
 809         exit(UU_EXIT_USAGE);
 810 }
 811 
 812 static int
 813 daemonize_start(void)
 814 {
 815         pid_t pid;
 816         int fd;
 817 
 818         if ((pid = fork1()) < 0)
 819                 return (-1);
 820 
 821         if (pid != 0)
 822                 exit(0);
 823 
 824         (void) close(STDIN_FILENO);
 825 
 826         if ((fd = open("/dev/null", O_RDONLY)) == -1) {
 827                 uu_warn(gettext("can't connect stdin to /dev/null"));
 828         } else if (fd != STDIN_FILENO) {
 829                 (void) dup2(fd, STDIN_FILENO);
 830                 startd_close(fd);
 831         }
 832 
 833         closefrom(3);
 834         (void) dup2(STDERR_FILENO, STDOUT_FILENO);
 835 
 836         (void) setsid();
 837         (void) chdir("/");
 838 
 839         /* Use default umask that init handed us, but 022 to create files. */
 840         dmask = umask(022);
 841         fmask = umask(dmask);
 842 
 843         return (0);
 844 }
 845 
 846 /*ARGSUSED*/
 847 static void
 848 die_handler(int sig, siginfo_t *info, void *data)
 849 {
 850         finished = 1;
 851 }
 852 
 853 int
 854 main(int argc, char *argv[])
 855 {
 856         int opt;
 857         int daemonize = 1;
 858         struct sigaction act;
 859         sigset_t nullset;
 860         struct stat sb;
 861 
 862         (void) uu_setpname(argv[0]);
 863 
 864         st = startd_zalloc(sizeof (startd_state_t));
 865 
 866         (void) pthread_mutexattr_init(&mutex_attrs);
 867 #ifndef NDEBUG
 868         (void) pthread_mutexattr_settype(&mutex_attrs,
 869             PTHREAD_MUTEX_ERRORCHECK);
 870 #endif
 871 
 872         max_scf_name_size = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
 873         max_scf_value_size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
 874         max_scf_fmri_size = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
 875 
 876         if (max_scf_name_size == -1 || max_scf_value_size == -1 ||
 877             max_scf_value_size == -1)
 878                 uu_die("Can't determine repository maximum lengths.\n");
 879 
 880         max_scf_name_size++;
 881         max_scf_value_size++;
 882         max_scf_fmri_size++;
 883 
 884         st->st_log_flags = STARTD_LOG_FILE | STARTD_LOG_SYSLOG;
 885         st->st_log_level_min = LOG_NOTICE;
 886 
 887         while ((opt = getopt(argc, argv, "nrs")) != EOF) {
 888                 switch (opt) {
 889                 case 'n':
 890                         daemonize = 0;
 891                         break;
 892                 case 'r':                       /* reconfiguration boot */
 893                         opt_reconfig = 1;
 894                         break;
 895                 case 's':                       /* single-user mode */
 896                         booting_to_single_user = B_TRUE;
 897                         break;
 898                 default:
 899                         usage(argv[0]);         /* exits */
 900                 }
 901         }
 902 
 903         if (optind != argc)
 904                 usage(argv[0]);
 905 
 906         (void) enable_extended_FILE_stdio(-1, -1);
 907 
 908         if (daemonize)
 909                 if (daemonize_start() < 0)
 910                         uu_die("Can't daemonize\n");
 911 
 912         log_init();
 913 
 914         if (stat("/etc/svc/volatile/resetting", &sb) != -1) {
 915                 log_framework(LOG_NOTICE, "Restarter quiesced.\n");
 916 
 917                 for (;;)
 918                         (void) pause();
 919         }
 920 
 921         act.sa_sigaction = &die_handler;
 922         (void) sigfillset(&act.sa_mask);
 923         act.sa_flags = SA_SIGINFO;
 924         (void) sigaction(SIGINT, &act, NULL);
 925         (void) sigaction(SIGTERM, &act, NULL);
 926 
 927         startup();
 928 
 929         (void) sigemptyset(&nullset);
 930         while (!finished) {
 931                 log_framework(LOG_DEBUG, "Main thread paused\n");
 932                 (void) sigsuspend(&nullset);
 933         }
 934 
 935         (void) log_framework(LOG_DEBUG, "Restarter exiting.\n");
 936         return (0);
 937 }