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