7711 SMF: Finish implementing support for degraded state

   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  * Copyright 2015, Joyent, Inc.

  25  */
  26 
  27 /*
  28  * startd.c - the master restarter
  29  *
  30  * svc.startd comprises two halves.  The graph engine is based in graph.c and
  31  * maintains the service dependency graph based on the information in the
  32  * repository.  For each service it also tracks the current state and the
  33  * restarter responsible for the service.  Based on the graph, events from the
  34  * repository (mostly administrative requests from svcadm), and messages from
  35  * the restarters, the graph engine makes decisions about how the services
  36  * should be manipulated and sends commands to the appropriate restarters.
  37  * Communication between the graph engine and the restarters is embodied in
  38  * protocol.c.
  39  *
  40  * The second half of svc.startd is the restarter for services managed by
  41  * svc.startd and is primarily contained in restarter.c.  It responds to graph
  42  * engine commands by executing methods, updating the repository, and sending
  43  * feedback (mostly state updates) to the graph engine.
  44  *
  45  * Overview of the SMF Architecture
  46  *
  47  * There are a few different components that make up SMF and are responsible
  48  * for different pieces of functionality that are used:
  49  *
  50  * svc.startd(1M): A daemon that is in charge of starting, stopping, and
  51  *     restarting services and instances.
  52  * svc.configd(1M): A daemon that manages the repository that stores
  53  *     information, property groups, and state of the different services and
  54  *     instances.
  55  * libscf(3LIB): A C library that provides the glue for communicating,
  56  *     accessing, and updating information about services and instances.
  57  * svccfg(1M): A utility to add and remove services as well as change the
  58  *     properties associated with different services and instances.
  59  * svcadm(1M): A utility to control the different instance of a service. You
  60  *     can use this to enable and disable them among some other useful things.
  61  * svcs(1): A utility that reports on the status of various services on the
  62  *     system.
  63  *
  64  * The following block diagram explains how these components communicate:
  65  *
  66  * The SMF Block Diagram
  67  *                                                       Repository
  68  *   This attempts to show       +---------+             +--------+
  69  *   the relations between       |         |     SQL     |        |
  70  *   the different pieces        | configd |<----------->| SQLite |
  71  *   that make SMF work and      |         | Transaction |        |
  72  *   users/administrators        +---------+             +--------+
  73  *   call into.                   ^      ^
  74  *                                |      |
  75  *                   door_call(3C)|      | door_call(3C)
  76  *                                |      |
  77  *                                v      v
  78  *      +----------+     +--------+      +--------+      +----------+
  79  *      |          |     |        |      |        |      |  svccfg  |
  80  *      |  startd  |<--->| libscf |      | libscf |<---->|  svcadm  |
  81  *      |          |     | (3LIB) |      | (3LIB) |      |   svcs   |
  82  *      +----------+     +--------+      +--------+      +----------+
  83  *        ^      ^
  84  *        |      | fork(2)/exec(2)
  85  *        |      | libcontract(3LIB)
  86  *        v      v                           Various System/User services
  87  *       +-------------------------------------------------------------------+
  88  *       | system/filesystem/local:default      system/coreadm:default       |
  89  *       | network/loopback:default             system/zones:default         |
  90  *       | milestone/multi-user:default         system/cron:default          |
  91  *       | system/console-login:default         network/ssh:default          |
  92  *       | system/pfexec:default                system/svc/restarter:default |
  93  *       +-------------------------------------------------------------------+
  94  *
  95  * Chatting with Configd and Sharing Repository Information
  96  *
  97  * As you run commands with svcs, svccfg, and svcadm, they are all creating a
  98  * libscf handle to communicate with configd. As calls are made via libscf they
  99  * ultimately go and talk to configd to get information. However, how we
 100  * actually are talking to configd is not as straightforward as it appears.
 101  *
 102  * When configd starts up it creates a door located at
 103  * /etc/svc/volatile/repository_door. This door runs the routine called
 104  * main_switcher() from usr/src/cmd/svc/configd/maindoor.c. When you first
 105  * invoke svc(cfg|s|adm), one of the first things that occurs is creating a
 106  * scf_handle_t and binding it to configd by calling scf_handle_bind(). This
 107  * function makes a door call to configd and gets returned a new file
 108  * descriptor. This file descriptor is itself another door which calls into
 109  * configd's client_switcher(). This is the door that is actually used when
 110  * getting and fetching properties, and many other useful things.
 111  *
 112  * svc.startd needs a way to notice the changes that occur to the repository.
 113  * For example, if you enabled a service that was not previously running, it's
 114  * up to startd to notice that this has happened, check dependencies, and
 115  * eventually start up the service. The way it gets these notifications is via
 116  * a thread who's sole purpose in life is to call _scf_notify_wait(). This
 117  * function acts like poll(2) but for changes that occur in the repository.
 118  * Once this thread gets the event, it dispatches the event appropriately.
 119  *
 120  * The Events of svc.startd
 121  *
 122  * svc.startd has to handle a lot of complexity. Understanding how you go from
 123  * getting the notification that a service was enabled to actually enabling it
 124  * is not obvious from a cursory glance. The first thing to keep in mind is
 125  * that startd maintains a graph of all the related services and instances so
 126  * it can keep track of what is enabled, what dependencies exist, etc. all so
 127  * that it can answer the question of what is affected by a change. Internally
 128  * there are a lot of different queues for events, threads to process these
 129  * queues, and different paths to have events enter these queues. What follows
 130  * is a diagram that attempts to explain some of those paths, though it's
 131  * important to note that for some of these pieces, such as the graph and
 132  * vertex events, there are many additional ways and code paths these threads
 133  * and functions can take. And yes, restarter_event_enqueue() is not the same
 134  * thing as restarter_queue_event().
 135  *
 136  *   Threads/Functions                 Queues                  Threads/Functions
 137  *
 138  * called by various
 139  *     +----------------+             +-------+                  +-------------+
 140  * --->| graph_protocol | graph_event | graph |   graph_event_   | graph_event |
 141  * --->| _send_event()  |------------>| event |----------------->| _thread     |
 142  *     +----------------+ _enqueue()  | queue |   dequeue()      +-------------+
 143  *                                    +-------+                         |
 144  *  _scf_notify_wait()                               vertex_send_event()|
 145  *  |                                                                   v
 146  *  |  +------------------+                              +--------------------+
 147  *  +->| repository_event | vertex_send_event()          | restarter_protocol |
 148  *     | _thread          |----------------------------->| _send_event()      |
 149  *     +------------------+                              +--------------------+
 150  *                                                          |    | out to other
 151  *                restarter_                     restarter_ |    | restarters
 152  *                event_dequeue() +-----------+  event_     |    | not startd
 153  *               +----------------| restarter |<------------+    +------------->
 154  *               v                |   event   |  enqueue()
 155  *      +-----------------+       |   queue   |             +------------------>
 156  *      | restarter_event |       +-----------+             |+----------------->
 157  *      | _thread         |                                 ||+---------------->
 158  *      +-----------------+                                 ||| start/stop inst
 159  *               |               +--------------+       +--------------------+
 160  *               |               |   instance   |       | restarter_process_ |
 161  *               +-------------->|    event     |------>| events             |
 162  *                restarter_     |    queue     |       | per-instance lwp   |
 163  *                queue_event()  +--------------+       +--------------------+
 164  *                                                          ||| various funcs
 165  *                                                          ||| controlling
 166  *                                                          ||| instance state
 167  *                                                          ||+--------------->
 168  *                                                          |+---------------->
 169  *                                                          +----------------->
 170  *
 171  * What's important to take away is that there is a queue for each instance on
 172  * the system that handles events related to dealing directly with that
 173  * instance and that events can be added to it because of changes to properties
 174  * that are made to configd and acted upon asynchronously by startd.
 175  *
 176  * Error handling
 177  *
 178  * In general, when svc.startd runs out of memory it reattempts a few times,
 179  * sleeping inbetween, before giving up and exiting (see startd_alloc_retry()).
 180  * When a repository connection is broken (libscf calls fail with
 181  * SCF_ERROR_CONNECTION_BROKEN, librestart and internal functions return
 182  * ECONNABORTED), svc.startd calls libscf_rebind_handle(), which coordinates
 183  * with the svc.configd-restarting thread, fork_configd_thread(), via
 184  * st->st_configd_live_cv, and rebinds the repository handle.  Doing so resets
 185  * all libscf state associated with that handle, so functions which do this
 186  * should communicate the event to their callers (usually by returning
 187  * ECONNRESET) so they may reset their state appropriately.
 188  *
 189  * External references
 190  *
 191  * svc.configd generates special security audit events for changes to some
 192  * restarter related properties.  See the special_props_list array in
 193  * usr/src/cmd/svc/configd/rc_node.c for the properties that cause these audit
 194  * events.  If you change the semantics of these propereties within startd, you
 195  * will probably need to update rc_node.c
 196  */
 197 
 198 #include <stdio.h>
 199 #include <stdio_ext.h>
 200 #include <sys/mnttab.h>           /* uses FILE * without including stdio.h */
 201 #include <alloca.h>
 202 #include <sys/mount.h>
 203 #include <sys/stat.h>
 204 #include <sys/types.h>
 205 #include <sys/wait.h>
 206 #include <assert.h>
 207 #include <errno.h>
 208 #include <fcntl.h>
 209 #include <ftw.h>
 210 #include <libintl.h>
 211 #include <libscf.h>
 212 #include <libscf_priv.h>
 213 #include <libuutil.h>
 214 #include <locale.h>
 215 #include <poll.h>
 216 #include <pthread.h>
 217 #include <signal.h>
 218 #include <stdarg.h>
 219 #include <stdlib.h>
 220 #include <string.h>
 221 #include <strings.h>
 222 #include <unistd.h>
 223 
 224 #include "startd.h"
 225 #include "protocol.h"
 226 
 227 ssize_t max_scf_name_size;
 228 ssize_t max_scf_fmri_size;
 229 ssize_t max_scf_value_size;
 230 
 231 mode_t fmask;
 232 mode_t dmask;
 233 
 234 graph_update_t *gu;
 235 restarter_update_t *ru;
 236 
 237 startd_state_t *st;
 238 
 239 boolean_t booting_to_single_user = B_FALSE;
 240 
 241 const char * const admin_actions[] = {

 242     SCF_PROPERTY_DEGRADED,

 243     SCF_PROPERTY_MAINT_OFF,
 244     SCF_PROPERTY_MAINT_ON,
 245     SCF_PROPERTY_MAINT_ON_IMMEDIATE,
 246     SCF_PROPERTY_REFRESH,
 247     SCF_PROPERTY_RESTART
 248 };
 249 
 250 const int admin_events[NACTIONS] = {

 251     RESTARTER_EVENT_TYPE_ADMIN_DEGRADED,

 252     RESTARTER_EVENT_TYPE_ADMIN_MAINT_OFF,
 253     RESTARTER_EVENT_TYPE_ADMIN_MAINT_ON,
 254     RESTARTER_EVENT_TYPE_ADMIN_MAINT_ON_IMMEDIATE,
 255     RESTARTER_EVENT_TYPE_ADMIN_REFRESH,
 256     RESTARTER_EVENT_TYPE_ADMIN_RESTART
 257 };
 258 
 259 const char * const instance_state_str[] = {
 260         "none",
 261         "uninitialized",
 262         "maintenance",
 263         "offline",
 264         "disabled",
 265         "online",
 266         "degraded"
 267 };
 268 
 269 static int finished = 0;
 270 static int opt_reconfig = 0;
 271 static uint8_t prop_reconfig = 0;
 272 
 273 #define INITIAL_REBIND_ATTEMPTS 5
 274 #define INITIAL_REBIND_DELAY    3
 275 
 276 pthread_mutexattr_t mutex_attrs;
 277 
 278 #ifdef DEBUG
 279 const char *
 280 _umem_debug_init(void)
 281 {
 282         return ("default,verbose");     /* UMEM_DEBUG setting */
 283 }
 284 
 285 const char *
 286 _umem_logging_init(void)
 287 {
 288         return ("fail,contents");       /* UMEM_LOGGING setting */
 289 }
 290 #endif
 291 
 292 const char *
 293 _umem_options_init(void)
 294 {
 295         /*
 296          * To reduce our memory footprint, we set our UMEM_OPTIONS to indicate
 297          * that we do not wish to have per-CPU magazines -- if svc.startd is so
 298          * hot on CPU such that this becomes a scalability problem, there are
 299          * likely deeper things amiss...
 300          */
 301         return ("nomagazines");         /* UMEM_OPTIONS setting */
 302 }
 303 
 304 /*
 305  * startd_alloc_retry()
 306  *   Wrapper for allocation functions.  Retries with a decaying time
 307  *   value on failure to allocate, and aborts startd if failure is
 308  *   persistent.
 309  */
 310 void *
 311 startd_alloc_retry(void *f(size_t, int), size_t sz)
 312 {
 313         void *p;
 314         uint_t try, msecs;
 315 
 316         p = f(sz, UMEM_DEFAULT);
 317         if (p != NULL || sz == 0)
 318                 return (p);
 319 
 320         msecs = ALLOC_DELAY;
 321 
 322         for (try = 0; p == NULL && try < ALLOC_RETRY; ++try) {
 323                 (void) poll(NULL, 0, msecs);
 324                 msecs *= ALLOC_DELAY_MULT;
 325                 p = f(sz, UMEM_DEFAULT);
 326                 if (p != NULL)
 327                         return (p);
 328         }
 329 
 330         uu_die("Insufficient memory.\n");
 331         /* NOTREACHED */
 332 }
 333 
 334 void *
 335 safe_realloc(void *p, size_t sz)
 336 {
 337         uint_t try, msecs;
 338 
 339         p = realloc(p, sz);
 340         if (p != NULL || sz == 0)
 341                 return (p);
 342 
 343         msecs = ALLOC_DELAY;
 344 
 345         for (try = 0; errno == EAGAIN && try < ALLOC_RETRY; ++try) {
 346                 (void) poll(NULL, 0, msecs);
 347                 p = realloc(p, sz);
 348                 if (p != NULL)
 349                         return (p);
 350                 msecs *= ALLOC_DELAY_MULT;
 351         }
 352 
 353         uu_die("Insufficient memory.\n");
 354         /* NOTREACHED */
 355 }
 356 
 357 char *
 358 safe_strdup(const char *s)
 359 {
 360         uint_t try, msecs;
 361         char *d;
 362 
 363         d = strdup(s);
 364         if (d != NULL)
 365                 return (d);
 366 
 367         msecs = ALLOC_DELAY;
 368 
 369         for (try = 0;
 370             (errno == EAGAIN || errno == ENOMEM) && try < ALLOC_RETRY;
 371             ++try) {
 372                 (void) poll(NULL, 0, msecs);
 373                 d = strdup(s);
 374                 if (d != NULL)
 375                         return (d);
 376                 msecs *= ALLOC_DELAY_MULT;
 377         }
 378 
 379         uu_die("Insufficient memory.\n");
 380         /* NOTREACHED */
 381 }
 382 
 383 
 384 void
 385 startd_free(void *p, size_t sz)
 386 {
 387         umem_free(p, sz);
 388 }
 389 
 390 /*
 391  * Creates a uu_list_pool_t with the same retry policy as startd_alloc().
 392  * Only returns NULL for UU_ERROR_UNKNOWN_FLAG and UU_ERROR_NOT_SUPPORTED.
 393  */
 394 uu_list_pool_t *
 395 startd_list_pool_create(const char *name, size_t e, size_t o,
 396     uu_compare_fn_t *f, uint32_t flags)
 397 {
 398         uu_list_pool_t *pool;
 399         uint_t try, msecs;
 400 
 401         pool = uu_list_pool_create(name, e, o, f, flags);
 402         if (pool != NULL)
 403                 return (pool);
 404 
 405         msecs = ALLOC_DELAY;
 406 
 407         for (try = 0; uu_error() == UU_ERROR_NO_MEMORY && try < ALLOC_RETRY;
 408             ++try) {
 409                 (void) poll(NULL, 0, msecs);
 410                 pool = uu_list_pool_create(name, e, o, f, flags);
 411                 if (pool != NULL)
 412                         return (pool);
 413                 msecs *= ALLOC_DELAY_MULT;
 414         }
 415 
 416         if (try < ALLOC_RETRY)
 417                 return (NULL);
 418 
 419         uu_die("Insufficient memory.\n");
 420         /* NOTREACHED */
 421 }
 422 
 423 /*
 424  * Creates a uu_list_t with the same retry policy as startd_alloc().  Only
 425  * returns NULL for UU_ERROR_UNKNOWN_FLAG and UU_ERROR_NOT_SUPPORTED.
 426  */
 427 uu_list_t *
 428 startd_list_create(uu_list_pool_t *pool, void *parent, uint32_t flags)
 429 {
 430         uu_list_t *list;
 431         uint_t try, msecs;
 432 
 433         list = uu_list_create(pool, parent, flags);
 434         if (list != NULL)
 435                 return (list);
 436 
 437         msecs = ALLOC_DELAY;
 438 
 439         for (try = 0; uu_error() == UU_ERROR_NO_MEMORY && try < ALLOC_RETRY;
 440             ++try) {
 441                 (void) poll(NULL, 0, msecs);
 442                 list = uu_list_create(pool, parent, flags);
 443                 if (list != NULL)
 444                         return (list);
 445                 msecs *= ALLOC_DELAY_MULT;
 446         }
 447 
 448         if (try < ALLOC_RETRY)
 449                 return (NULL);
 450 
 451         uu_die("Insufficient memory.\n");
 452         /* NOTREACHED */
 453 }
 454 
 455 pthread_t
 456 startd_thread_create(void *(*func)(void *), void *ptr)
 457 {
 458         int err;
 459         pthread_t tid;
 460 
 461         err = pthread_create(&tid, NULL, func, ptr);
 462         if (err != 0) {
 463                 assert(err == EAGAIN);
 464                 uu_die("Could not create thread.\n");
 465         }
 466 
 467         err = pthread_detach(tid);
 468         assert(err == 0);
 469 
 470         return (tid);
 471 }
 472 
 473 extern int info_events_all;
 474 
 475 static int
 476 read_startd_config(void)
 477 {
 478         scf_handle_t *hndl;
 479         scf_instance_t *inst;
 480         scf_propertygroup_t *pg;
 481         scf_property_t *prop;
 482         scf_value_t *val;
 483         scf_iter_t *iter, *piter;
 484         instance_data_t idata;
 485         char *buf, *vbuf;
 486         char *startd_options_fmri = uu_msprintf("%s/:properties/options",
 487             SCF_SERVICE_STARTD);
 488         char *startd_reconfigure_fmri = uu_msprintf(
 489             "%s/:properties/system/reconfigure", SCF_SERVICE_STARTD);
 490         char *env_opts, *lasts, *cp;
 491         int bind_fails = 0;
 492         int ret = 0, r;
 493         uint_t count = 0, msecs = ALLOC_DELAY;
 494         size_t sz;
 495         ctid_t ctid;
 496         uint64_t uint64;
 497 
 498         buf = startd_alloc(max_scf_fmri_size);
 499 
 500         if (startd_options_fmri == NULL || startd_reconfigure_fmri == NULL)
 501                 uu_die("Allocation failure\n");
 502 
 503         st->st_log_prefix = LOG_PREFIX_EARLY;
 504 
 505         if ((st->st_log_file = getenv("STARTD_DEFAULT_LOG")) == NULL) {
 506                 st->st_log_file = startd_alloc(strlen(STARTD_DEFAULT_LOG) + 1);
 507 
 508                 (void) strcpy(st->st_log_file, STARTD_DEFAULT_LOG);
 509         }
 510 
 511         st->st_door_path = getenv("STARTD_ALT_DOOR");
 512 
 513         /*
 514          * Read "options" property group.
 515          */
 516         for (hndl = libscf_handle_create_bound(SCF_VERSION); hndl == NULL;
 517             hndl = libscf_handle_create_bound(SCF_VERSION), bind_fails++) {
 518                 (void) sleep(INITIAL_REBIND_DELAY);
 519 
 520                 if (bind_fails > INITIAL_REBIND_ATTEMPTS) {
 521                         /*
 522                          * In the case that we can't bind to the repository
 523                          * (which should have been started), we need to allow
 524                          * the user into maintenance mode to determine what's
 525                          * failed.
 526                          */
 527                         log_framework(LOG_INFO, "Couldn't fetch "
 528                             "default settings: %s\n",
 529                             scf_strerror(scf_error()));
 530 
 531                         ret = -1;
 532 
 533                         goto noscfout;
 534                 }
 535         }
 536 
 537         idata.i_fmri = SCF_SERVICE_STARTD;
 538         idata.i_state = RESTARTER_STATE_NONE;
 539         idata.i_next_state = RESTARTER_STATE_NONE;
 540 timestamp:
 541         switch (r = _restarter_commit_states(hndl, &idata,
 542             RESTARTER_STATE_ONLINE, RESTARTER_STATE_NONE,
 543             restarter_get_str_short(restarter_str_insert_in_graph))) {
 544         case 0:
 545                 break;
 546 
 547         case ENOMEM:
 548                 ++count;
 549                 if (count < ALLOC_RETRY) {
 550                         (void) poll(NULL, 0, msecs);
 551                         msecs *= ALLOC_DELAY_MULT;
 552                         goto timestamp;
 553                 }
 554 
 555                 uu_die("Insufficient memory.\n");
 556                 /* NOTREACHED */
 557 
 558         case ECONNABORTED:
 559                 libscf_handle_rebind(hndl);
 560                 goto timestamp;
 561 
 562         case ENOENT:
 563         case EPERM:
 564         case EACCES:
 565         case EROFS:
 566                 log_error(LOG_INFO, "Could set state of %s: %s.\n",
 567                     idata.i_fmri, strerror(r));
 568                 break;
 569 
 570         case EINVAL:
 571         default:
 572                 bad_error("_restarter_commit_states", r);
 573         }
 574 
 575         pg = safe_scf_pg_create(hndl);
 576         prop = safe_scf_property_create(hndl);
 577         val = safe_scf_value_create(hndl);
 578         inst = safe_scf_instance_create(hndl);
 579 
 580         /* set startd's restarter properties */
 581         if (scf_handle_decode_fmri(hndl, SCF_SERVICE_STARTD, NULL, NULL, inst,
 582             NULL, NULL, SCF_DECODE_FMRI_EXACT) == 0) {
 583                 (void) libscf_write_start_pid(inst, getpid());
 584                 ctid = proc_get_ctid();
 585                 if (ctid != -1) {
 586                         uint64 = (uint64_t)ctid;
 587                         (void) libscf_inst_set_count_prop(inst,
 588                             SCF_PG_RESTARTER, SCF_PG_RESTARTER_TYPE,
 589                             SCF_PG_RESTARTER_FLAGS, SCF_PROPERTY_CONTRACT,
 590                             uint64);
 591                 }
 592                 (void) libscf_note_method_log(inst, LOG_PREFIX_EARLY,
 593                     STARTD_DEFAULT_LOG);
 594                 (void) libscf_note_method_log(inst, LOG_PREFIX_NORMAL,
 595                     STARTD_DEFAULT_LOG);
 596         }
 597 
 598         /* Read reconfigure property for recovery. */
 599         if (scf_handle_decode_fmri(hndl, startd_reconfigure_fmri, NULL, NULL,
 600             NULL, NULL, prop, NULL) != -1 &&
 601             scf_property_get_value(prop, val) == 0)
 602                 (void) scf_value_get_boolean(val, &prop_reconfig);
 603 
 604         if (scf_handle_decode_fmri(hndl, startd_options_fmri, NULL, NULL, NULL,
 605             pg, NULL, SCF_DECODE_FMRI_TRUNCATE) == -1) {
 606                 /*
 607                  * No configuration options defined.
 608                  */
 609                 if (scf_error() != SCF_ERROR_NOT_FOUND)
 610                         uu_warn("Couldn't read configuration from 'options' "
 611                             "group: %s\n", scf_strerror(scf_error()));
 612                 goto scfout;
 613         }
 614 
 615         /*
 616          * If there is no "options" group defined, then our defaults are fine.
 617          */
 618         if (scf_pg_get_name(pg, NULL, 0) < 0)
 619                 goto scfout;
 620 
 621         /* get info_events_all */
 622         info_events_all = libscf_get_info_events_all(pg);
 623 
 624         /* Iterate through. */
 625         iter = safe_scf_iter_create(hndl);
 626 
 627         (void) scf_iter_pg_properties(iter, pg);
 628 
 629         piter = safe_scf_iter_create(hndl);
 630         vbuf = startd_alloc(max_scf_value_size);
 631 
 632         while ((scf_iter_next_property(iter, prop) == 1)) {
 633                 scf_type_t ty;
 634 
 635                 if (scf_property_get_name(prop, buf, max_scf_fmri_size) < 0)
 636                         continue;
 637 
 638                 if (strcmp(buf, "logging") != 0 &&
 639                     strcmp(buf, "boot_messages") != 0)
 640                         continue;
 641 
 642                 if (scf_property_type(prop, &ty) != 0) {
 643                         switch (scf_error()) {
 644                         case SCF_ERROR_CONNECTION_BROKEN:
 645                         default:
 646                                 libscf_handle_rebind(hndl);
 647                                 continue;
 648 
 649                         case SCF_ERROR_DELETED:
 650                                 continue;
 651 
 652                         case SCF_ERROR_NOT_BOUND:
 653                         case SCF_ERROR_NOT_SET:
 654                                 bad_error("scf_property_type", scf_error());
 655                         }
 656                 }
 657 
 658                 if (ty != SCF_TYPE_ASTRING) {
 659                         uu_warn("property \"options/%s\" is not of type "
 660                             "astring; ignored.\n", buf);
 661                         continue;
 662                 }
 663 
 664                 if (scf_property_get_value(prop, val) != 0) {
 665                         switch (scf_error()) {
 666                         case SCF_ERROR_CONNECTION_BROKEN:
 667                         default:
 668                                 return (ECONNABORTED);
 669 
 670                         case SCF_ERROR_DELETED:
 671                         case SCF_ERROR_NOT_FOUND:
 672                                 return (0);
 673 
 674                         case SCF_ERROR_CONSTRAINT_VIOLATED:
 675                                 uu_warn("property \"options/%s\" has multiple "
 676                                     "values; ignored.\n", buf);
 677                                 continue;
 678 
 679                         case SCF_ERROR_PERMISSION_DENIED:
 680                                 uu_warn("property \"options/%s\" cannot be "
 681                                     "read because startd has insufficient "
 682                                     "permission; ignored.\n", buf);
 683                                 continue;
 684 
 685                         case SCF_ERROR_HANDLE_MISMATCH:
 686                         case SCF_ERROR_NOT_BOUND:
 687                         case SCF_ERROR_NOT_SET:
 688                                 bad_error("scf_property_get_value",
 689                                     scf_error());
 690                         }
 691                 }
 692 
 693                 if (scf_value_get_astring(val, vbuf, max_scf_value_size) < 0)
 694                         bad_error("scf_value_get_astring", scf_error());
 695 
 696                 if (strcmp("logging", buf) == 0) {
 697                         if (strcmp("verbose", vbuf) == 0) {
 698                                 st->st_boot_flags = STARTD_BOOT_VERBOSE;
 699                                 st->st_log_level_min = LOG_INFO;
 700                         } else if (strcmp("debug", vbuf) == 0) {
 701                                 st->st_boot_flags = STARTD_BOOT_VERBOSE;
 702                                 st->st_log_level_min = LOG_DEBUG;
 703                         } else if (strcmp("quiet", vbuf) == 0) {
 704                                 st->st_log_level_min = LOG_NOTICE;
 705                         } else {
 706                                 uu_warn("unknown options/logging "
 707                                     "value '%s' ignored\n", vbuf);
 708                         }
 709 
 710                 } else if (strcmp("boot_messages", buf) == 0) {
 711                         if (strcmp("quiet", vbuf) == 0) {
 712                                 st->st_boot_flags = STARTD_BOOT_QUIET;
 713                         } else if (strcmp("verbose", vbuf) == 0) {
 714                                 st->st_boot_flags = STARTD_BOOT_VERBOSE;
 715                         } else {
 716                                 log_framework(LOG_NOTICE, "unknown "
 717                                     "options/boot_messages value '%s' "
 718                                     "ignored\n", vbuf);
 719                         }
 720 
 721                 }
 722         }
 723 
 724         startd_free(vbuf, max_scf_value_size);
 725         scf_iter_destroy(piter);
 726 
 727         scf_iter_destroy(iter);
 728 
 729 scfout:
 730         scf_value_destroy(val);
 731         scf_pg_destroy(pg);
 732         scf_property_destroy(prop);
 733         scf_instance_destroy(inst);
 734         (void) scf_handle_unbind(hndl);
 735         scf_handle_destroy(hndl);
 736 
 737 noscfout:
 738         startd_free(buf, max_scf_fmri_size);
 739         uu_free(startd_options_fmri);
 740         uu_free(startd_reconfigure_fmri);
 741 
 742         if (booting_to_single_user) {
 743                 st->st_subgraph = startd_alloc(max_scf_fmri_size);
 744                 sz = strlcpy(st->st_subgraph, "milestone/single-user:default",
 745                     max_scf_fmri_size);
 746                 assert(sz < max_scf_fmri_size);
 747         }
 748 
 749         /*
 750          * Options passed in as boot arguments override repository defaults.
 751          */
 752         env_opts = getenv("SMF_OPTIONS");
 753         if (env_opts == NULL)
 754                 return (ret);
 755 
 756         for (cp = strtok_r(env_opts, ",", &lasts); cp != NULL;
 757             cp = strtok_r(NULL, ",", &lasts)) {
 758                 if (strcmp(cp, "debug") == 0) {
 759                         st->st_boot_flags = STARTD_BOOT_VERBOSE;
 760                         st->st_log_level_min = LOG_DEBUG;
 761 
 762                         /* -m debug should send messages to console */
 763                         st->st_log_flags =
 764                             st->st_log_flags | STARTD_LOG_TERMINAL;
 765                 } else if (strcmp(cp, "verbose") == 0) {
 766                         st->st_boot_flags = STARTD_BOOT_VERBOSE;
 767                         st->st_log_level_min = LOG_INFO;
 768                 } else if (strcmp(cp, "seed") == 0) {
 769                         uu_warn("SMF option \"%s\" unimplemented.\n", cp);
 770                 } else if (strcmp(cp, "quiet") == 0) {
 771                         st->st_log_level_min = LOG_NOTICE;
 772                 } else if (strncmp(cp, "milestone=",
 773                     sizeof ("milestone=") - 1) == 0) {
 774                         char *mp = cp + sizeof ("milestone=") - 1;
 775 
 776                         if (booting_to_single_user)
 777                                 continue;
 778 
 779                         if (st->st_subgraph == NULL) {
 780                                 st->st_subgraph =
 781                                     startd_alloc(max_scf_fmri_size);
 782                                 st->st_subgraph[0] = '\0';
 783                         }
 784 
 785                         if (mp[0] == '\0' || strcmp(mp, "all") == 0) {
 786                                 (void) strcpy(st->st_subgraph, "all");
 787                         } else if (strcmp(mp, "su") == 0 ||
 788                             strcmp(mp, "single-user") == 0) {
 789                                 (void) strcpy(st->st_subgraph,
 790                                     "milestone/single-user:default");
 791                         } else if (strcmp(mp, "mu") == 0 ||
 792                             strcmp(mp, "multi-user") == 0) {
 793                                 (void) strcpy(st->st_subgraph,
 794                                     "milestone/multi-user:default");
 795                         } else if (strcmp(mp, "mus") == 0 ||
 796                             strcmp(mp, "multi-user-server") == 0) {
 797                                 (void) strcpy(st->st_subgraph,
 798                                     "milestone/multi-user-server:default");
 799                         } else if (strcmp(mp, "none") == 0) {
 800                                 (void) strcpy(st->st_subgraph, "none");
 801                         } else {
 802                                 log_framework(LOG_NOTICE,
 803                                     "invalid milestone option value "
 804                                     "'%s' ignored\n", mp);
 805                         }
 806                 } else {
 807                         uu_warn("Unknown SMF option \"%s\".\n", cp);
 808                 }
 809         }
 810 
 811         return (ret);
 812 }
 813 
 814 /*
 815  * void set_boot_env()
 816  *
 817  * If -r was passed or /reconfigure exists, this is a reconfig
 818  * reboot.  We need to make sure that this information is given
 819  * to the appropriate services the first time they're started
 820  * by setting the system/reconfigure repository property,
 821  * as well as pass the _INIT_RECONFIG variable on to the rcS
 822  * start method so that legacy services can continue to use it.
 823  *
 824  * This function must never be called before contract_init(), as
 825  * it sets st_initial.  get_startd_config() sets prop_reconfig from
 826  * pre-existing repository state.
 827  */
 828 static void
 829 set_boot_env()
 830 {
 831         struct stat sb;
 832         int r;
 833 
 834         /*
 835          * Check if property still is set -- indicates we didn't get
 836          * far enough previously to unset it.  Otherwise, if this isn't
 837          * the first startup, don't re-process /reconfigure or the
 838          * boot flag.
 839          */
 840         if (prop_reconfig != 1 && st->st_initial != 1)
 841                 return;
 842 
 843         /* If /reconfigure exists, also set opt_reconfig. */
 844         if (stat("/reconfigure", &sb) != -1)
 845                 opt_reconfig = 1;
 846 
 847         /* Nothing to do.  Just return. */
 848         if (opt_reconfig == 0 && prop_reconfig == 0)
 849                 return;
 850 
 851         /*
 852          * Set startd's reconfigure property.  This property is
 853          * then cleared by successful completion of the single-user
 854          * milestone.
 855          */
 856         if (prop_reconfig != 1) {
 857                 r = libscf_set_reconfig(1);
 858                 switch (r) {
 859                 case 0:
 860                         break;
 861 
 862                 case ENOENT:
 863                 case EPERM:
 864                 case EACCES:
 865                 case EROFS:
 866                         log_error(LOG_WARNING, "Could not set reconfiguration "
 867                             "property: %s\n", strerror(r));
 868                         break;
 869 
 870                 default:
 871                         bad_error("libscf_set_reconfig", r);
 872                 }
 873         }
 874 }
 875 
 876 static void
 877 startup(void)
 878 {
 879         ctid_t configd_ctid;
 880         int err;
 881 
 882         /*
 883          * Initialize data structures.
 884          */
 885         gu = startd_zalloc(sizeof (graph_update_t));
 886         ru = startd_zalloc(sizeof (restarter_update_t));
 887 
 888         (void) pthread_cond_init(&st->st_load_cv, NULL);
 889         (void) pthread_cond_init(&st->st_configd_live_cv, NULL);
 890         (void) pthread_cond_init(&gu->gu_cv, NULL);
 891         (void) pthread_cond_init(&gu->gu_freeze_cv, NULL);
 892         (void) pthread_cond_init(&ru->restarter_update_cv, NULL);
 893         (void) pthread_mutex_init(&st->st_load_lock, &mutex_attrs);
 894         (void) pthread_mutex_init(&st->st_configd_live_lock, &mutex_attrs);
 895         (void) pthread_mutex_init(&gu->gu_lock, &mutex_attrs);
 896         (void) pthread_mutex_init(&gu->gu_freeze_lock, &mutex_attrs);
 897         (void) pthread_mutex_init(&ru->restarter_update_lock, &mutex_attrs);
 898 
 899         configd_ctid = contract_init();
 900 
 901         if (configd_ctid != -1)
 902                 log_framework(LOG_DEBUG, "Existing configd contract %ld; not "
 903                     "starting svc.configd\n", configd_ctid);
 904 
 905         /*
 906          * Call utmpx_init() before creating the fork_configd() thread.
 907          */
 908         utmpx_init();
 909 
 910         (void) startd_thread_create(fork_configd_thread, (void *)configd_ctid);
 911 
 912         /*
 913          * Await, if necessary, configd's initial arrival.
 914          */
 915         MUTEX_LOCK(&st->st_configd_live_lock);
 916         while (!st->st_configd_lives) {
 917                 log_framework(LOG_DEBUG, "Awaiting cv signal on "
 918                     "configd_live_cv\n");
 919                 err = pthread_cond_wait(&st->st_configd_live_cv,
 920                     &st->st_configd_live_lock);
 921                 assert(err == 0);
 922         }
 923         MUTEX_UNLOCK(&st->st_configd_live_lock);
 924 
 925         wait_init();
 926 
 927         if (read_startd_config())
 928                 log_framework(LOG_INFO, "svc.configd unable to provide startd "
 929                     "optional settings\n");
 930 
 931         log_init();
 932         dict_init();
 933         timeout_init();
 934         restarter_protocol_init();
 935         restarter_init();
 936 
 937         /*
 938          * svc.configd is started by fork_configd_thread so repository access is
 939          * available, run early manifest import before continuing with starting
 940          * graph engine and the rest of startd.
 941          */
 942         log_framework(LOG_DEBUG, "Calling fork_emi...\n");
 943         fork_emi();
 944 
 945         graph_protocol_init();
 946         graph_init();
 947 
 948         init_env();
 949 
 950         set_boot_env();
 951         restarter_start();
 952         graph_engine_start();
 953 }
 954 
 955 static void
 956 usage(const char *name)
 957 {
 958         uu_warn(gettext("usage: %s [-n]\n"), name);
 959         exit(UU_EXIT_USAGE);
 960 }
 961 
 962 static int
 963 daemonize_start(void)
 964 {
 965         pid_t pid;
 966         int fd;
 967 
 968         if ((pid = fork1()) < 0)
 969                 return (-1);
 970 
 971         if (pid != 0)
 972                 exit(0);
 973 
 974         (void) close(STDIN_FILENO);
 975 
 976         if ((fd = open("/dev/null", O_RDONLY)) == -1) {
 977                 uu_warn(gettext("can't connect stdin to /dev/null"));
 978         } else if (fd != STDIN_FILENO) {
 979                 (void) dup2(fd, STDIN_FILENO);
 980                 startd_close(fd);
 981         }
 982 
 983         closefrom(3);
 984         (void) dup2(STDERR_FILENO, STDOUT_FILENO);
 985 
 986         (void) setsid();
 987         (void) chdir("/");
 988 
 989         /* Use default umask that init handed us, but 022 to create files. */
 990         dmask = umask(022);
 991         fmask = umask(dmask);
 992 
 993         return (0);
 994 }
 995 
 996 /*ARGSUSED*/
 997 static void
 998 die_handler(int sig, siginfo_t *info, void *data)
 999 {
1000         finished = 1;
1001 }
1002 
1003 int
1004 main(int argc, char *argv[])
1005 {
1006         int opt;
1007         int daemonize = 1;
1008         struct sigaction act;
1009         sigset_t nullset;
1010         struct stat sb;
1011 
1012         (void) uu_setpname(argv[0]);
1013 
1014         st = startd_zalloc(sizeof (startd_state_t));
1015 
1016         (void) pthread_mutexattr_init(&mutex_attrs);
1017 #ifndef NDEBUG
1018         (void) pthread_mutexattr_settype(&mutex_attrs,
1019             PTHREAD_MUTEX_ERRORCHECK);
1020 #endif
1021 
1022         max_scf_name_size = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
1023         max_scf_value_size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1024         max_scf_fmri_size = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
1025 
1026         if (max_scf_name_size == -1 || max_scf_value_size == -1 ||
1027             max_scf_value_size == -1)
1028                 uu_die("Can't determine repository maximum lengths.\n");
1029 
1030         max_scf_name_size++;
1031         max_scf_value_size++;
1032         max_scf_fmri_size++;
1033 
1034         st->st_log_flags = STARTD_LOG_FILE | STARTD_LOG_SYSLOG;
1035         st->st_log_level_min = LOG_NOTICE;
1036 
1037         while ((opt = getopt(argc, argv, "nrs")) != EOF) {
1038                 switch (opt) {
1039                 case 'n':
1040                         daemonize = 0;
1041                         break;
1042                 case 'r':                       /* reconfiguration boot */
1043                         opt_reconfig = 1;
1044                         break;
1045                 case 's':                       /* single-user mode */
1046                         booting_to_single_user = B_TRUE;
1047                         break;
1048                 default:
1049                         usage(argv[0]);         /* exits */
1050                 }
1051         }
1052 
1053         if (optind != argc)
1054                 usage(argv[0]);
1055 
1056         (void) enable_extended_FILE_stdio(-1, -1);
1057 
1058         if (daemonize)
1059                 if (daemonize_start() < 0)
1060                         uu_die("Can't daemonize\n");
1061 
1062         log_init();
1063 
1064         if (stat("/etc/svc/volatile/resetting", &sb) != -1) {
1065                 log_framework(LOG_NOTICE, "Restarter quiesced.\n");
1066 
1067                 for (;;)
1068                         (void) pause();
1069         }
1070 
1071         act.sa_sigaction = &die_handler;
1072         (void) sigfillset(&act.sa_mask);
1073         act.sa_flags = SA_SIGINFO;
1074         (void) sigaction(SIGINT, &act, NULL);
1075         (void) sigaction(SIGTERM, &act, NULL);
1076 
1077         startup();
1078 
1079         (void) sigemptyset(&nullset);
1080         while (!finished) {
1081                 log_framework(LOG_DEBUG, "Main thread paused\n");
1082                 (void) sigsuspend(&nullset);
1083         }
1084 
1085         (void) log_framework(LOG_DEBUG, "Restarter exiting.\n");
1086         return (0);
1087 }
--- EOF ---