startd.c revision 5777:e3276fcb93e7
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
98ssize_t max_scf_name_size;
99ssize_t max_scf_fmri_size;
100ssize_t max_scf_value_size;
101
102mode_t fmask;
103mode_t dmask;
104
105graph_update_t *gu;
106restarter_update_t *ru;
107
108startd_state_t *st;
109
110boolean_t booting_to_single_user = B_FALSE;
111
112const 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
121const 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
130const char * const instance_state_str[] = {
131	"none",
132	"uninitialized",
133	"maintenance",
134	"offline",
135	"disabled",
136	"online",
137	"degraded"
138};
139
140static int finished = 0;
141static int opt_reconfig = 0;
142static uint8_t prop_reconfig = 0;
143
144#define	INITIAL_REBIND_ATTEMPTS	5
145#define	INITIAL_REBIND_DELAY	3
146
147pthread_mutexattr_t mutex_attrs;
148
149const char *
150_umem_debug_init(void)
151{
152	return ("default,verbose");	/* UMEM_DEBUG setting */
153}
154
155const 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 */
167void *
168startd_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
191void *
192safe_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
214char *
215safe_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
241void
242startd_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 */
251uu_list_pool_t *
252startd_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 */
284uu_list_t *
285startd_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
312pthread_t
313startd_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
331static int
332read_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;
396timestamp:
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
581scfout:
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
589noscfout:
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 */
680static void
681set_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
728static void
729startup(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
794static void
795usage(const char *name)
796{
797	uu_warn(gettext("usage: %s [-n]\n"), name);
798	exit(UU_EXIT_USAGE);
799}
800
801static int
802daemonize_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*/
836static void
837die_handler(int sig, siginfo_t *info, void *data)
838{
839	finished = 1;
840}
841
842int
843main(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