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