libscf.c revision 5040:ff6ebd8761a6
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#include <sys/contract/process.h>
30#include <assert.h>
31#include <errno.h>
32#include <libscf.h>
33#include <libscf_priv.h>
34#include <poll.h>
35#include <stdlib.h>
36#include <string.h>
37#include <unistd.h>
38
39#include "startd.h"
40
41#define	SMF_SNAPSHOT_RUNNING	"running"
42
43char *
44inst_fmri_to_svc_fmri(const char *fmri)
45{
46	char *buf, *sfmri;
47	const char *scope, *svc;
48	int r;
49	boolean_t local;
50
51	buf = startd_alloc(max_scf_fmri_size);
52	sfmri = startd_alloc(max_scf_fmri_size);
53
54	(void) strcpy(buf, fmri);
55
56	r = scf_parse_svc_fmri(buf, &scope, &svc, NULL, NULL, NULL);
57	assert(r == 0);
58
59	local = strcmp(scope, SCF_SCOPE_LOCAL) == 0;
60
61	(void) snprintf(sfmri, max_scf_fmri_size, "svc:%s%s/%s",
62	    local ? "" : "//", local ? "" : scope, svc);
63
64	startd_free(buf, max_scf_fmri_size);
65
66	return (sfmri);
67}
68
69/*
70 * Wrapper for the scf_*_create() functions.  On SCF_ERROR_NO_MEMORY and
71 * SCF_ERROR_NO_RESOURCES, retries or dies.  So this can only fail with
72 * SCF_ERROR_INVALID_ARGUMENT, if h is NULL.
73 */
74void *
75libscf_object_create(void *f(scf_handle_t *), scf_handle_t *h)
76{
77	void *o;
78	uint_t try, msecs;
79	scf_error_t err;
80
81	o = f(h);
82	if (o != NULL)
83		return (o);
84	err = scf_error();
85	if (err != SCF_ERROR_NO_MEMORY && err != SCF_ERROR_NO_RESOURCES)
86		return (NULL);
87
88	msecs = ALLOC_DELAY;
89
90	for (try = 0; try < ALLOC_RETRY; ++try) {
91		(void) poll(NULL, 0, msecs);
92		msecs *= ALLOC_DELAY_MULT;
93		o = f(h);
94		if (o != NULL)
95			return (o);
96		err = scf_error();
97		if (err != SCF_ERROR_NO_MEMORY && err != SCF_ERROR_NO_RESOURCES)
98			return (NULL);
99	}
100
101	uu_die("Insufficient memory.\n");
102	/* NOTREACHED */
103}
104
105scf_snapshot_t *
106libscf_get_running_snapshot(scf_instance_t *inst)
107{
108	scf_handle_t *h;
109	scf_snapshot_t *snap;
110
111	h = scf_instance_handle(inst);
112	if (h == NULL)
113		return (NULL);
114
115	snap = scf_snapshot_create(h);
116	if (snap == NULL)
117		return (NULL);
118
119	if (scf_instance_get_snapshot(inst, SMF_SNAPSHOT_RUNNING, snap) == 0)
120		return (snap);
121
122	scf_snapshot_destroy(snap);
123	return (NULL);
124}
125
126/*
127 * Make sure a service has a "running" snapshot.  If it doesn't, make one from
128 * the editing configuration.
129 */
130scf_snapshot_t *
131libscf_get_or_make_running_snapshot(scf_instance_t *inst, const char *fmri,
132    boolean_t retake)
133{
134	scf_handle_t *h;
135	scf_snapshot_t *snap;
136
137	h = scf_instance_handle(inst);
138
139	snap = scf_snapshot_create(h);
140	if (snap == NULL)
141		goto err;
142
143	if (scf_instance_get_snapshot(inst, SMF_SNAPSHOT_RUNNING, snap) == 0)
144		return (snap);
145
146	switch (scf_error()) {
147	case SCF_ERROR_NOT_FOUND:
148		break;
149
150	case SCF_ERROR_DELETED:
151		scf_snapshot_destroy(snap);
152		return (NULL);
153
154	default:
155err:
156		log_error(LOG_NOTICE,
157		    "Could not check for running snapshot of %s (%s).\n", fmri,
158		    scf_strerror(scf_error()));
159		scf_snapshot_destroy(snap);
160		return (NULL);
161	}
162
163	if (_scf_snapshot_take_new(inst, SMF_SNAPSHOT_RUNNING, snap) == 0) {
164		log_framework(LOG_DEBUG, "Took running snapshot for %s.\n",
165		    fmri);
166	} else {
167		if (retake && scf_error() == SCF_ERROR_BACKEND_READONLY)
168			restarter_mark_pending_snapshot(fmri,
169			    RINST_RETAKE_RUNNING);
170		else
171			log_error(LOG_DEBUG,
172			    "Could not create running snapshot for %s "
173			    "(%s).\n", fmri, scf_strerror(scf_error()));
174
175		scf_snapshot_destroy(snap);
176		snap = NULL;
177	}
178
179	return (snap);
180}
181
182/*
183 * When a service comes up, point the "start" snapshot at the "running"
184 * snapshot.  Returns 0 on success, ENOTSUP if fmri designates something other
185 * than an instance, ECONNABORTED, ENOENT if the instance does not exist, or
186 * EACCES.
187 */
188int
189libscf_snapshots_poststart(scf_handle_t *h, const char *fmri, boolean_t retake)
190{
191	scf_instance_t *inst = NULL;
192	scf_snapshot_t *running, *start = NULL;
193	int ret = 0, r;
194
195	r = libscf_fmri_get_instance(h, fmri, &inst);
196	switch (r) {
197	case 0:
198		break;
199
200	case ENOTSUP:
201	case ECONNABORTED:
202	case ENOENT:
203		return (r);
204
205	case EINVAL:
206	default:
207		assert(0);
208		abort();
209	}
210
211	start = safe_scf_snapshot_create(h);
212
213again:
214	running = libscf_get_or_make_running_snapshot(inst, fmri, retake);
215	if (running == NULL) {
216		ret = 0;
217		goto out;
218	}
219
220lookup:
221	if (scf_instance_get_snapshot(inst, "start", start) != 0) {
222		switch (scf_error()) {
223		case SCF_ERROR_CONNECTION_BROKEN:
224		default:
225			ret = ECONNABORTED;
226			goto out;
227
228		case SCF_ERROR_NOT_FOUND:
229			if (_scf_snapshot_take_new(inst, "start", start) != 0) {
230				switch (scf_error()) {
231				case SCF_ERROR_CONNECTION_BROKEN:
232				default:
233					ret = ECONNABORTED;
234					goto out;
235
236				case SCF_ERROR_DELETED:
237					ret = ENOENT;
238					goto out;
239
240				case SCF_ERROR_EXISTS:
241					goto lookup;
242
243				case SCF_ERROR_NO_RESOURCES:
244					uu_die("Repository server out of "
245					    "resources.\n");
246					/* NOTREACHED */
247
248				case SCF_ERROR_BACKEND_READONLY:
249					goto readonly;
250
251				case SCF_ERROR_PERMISSION_DENIED:
252					uu_die("Insufficient privileges.\n");
253					/* NOTREACHED */
254
255				case SCF_ERROR_BACKEND_ACCESS:
256					ret = EACCES;
257					goto out;
258
259				case SCF_ERROR_HANDLE_MISMATCH:
260				case SCF_ERROR_INVALID_ARGUMENT:
261				case SCF_ERROR_NOT_SET:
262					bad_error("_scf_snapshot_take_new",
263					    scf_error());
264				}
265			}
266			break;
267
268		case SCF_ERROR_DELETED:
269			ret = ENOENT;
270			goto out;
271
272		case SCF_ERROR_HANDLE_MISMATCH:
273		case SCF_ERROR_NOT_SET:
274		case SCF_ERROR_INVALID_ARGUMENT:
275			bad_error("scf_instance_get_snapshot", scf_error());
276		}
277	}
278
279	if (_scf_snapshot_attach(running, start) == 0) {
280		log_framework(LOG_DEBUG, "Updated \"start\" snapshot for %s.\n",
281		    fmri);
282	} else {
283		switch (scf_error()) {
284		case SCF_ERROR_CONNECTION_BROKEN:
285		default:
286			ret = ECONNABORTED;
287			goto out;
288
289		case SCF_ERROR_DELETED:
290			scf_snapshot_destroy(running);
291			goto again;
292
293		case SCF_ERROR_NO_RESOURCES:
294			uu_die("Repository server out of resources.\n");
295			/* NOTREACHED */
296
297		case SCF_ERROR_PERMISSION_DENIED:
298			uu_die("Insufficient privileges.\n");
299			/* NOTREACHED */
300
301		case SCF_ERROR_BACKEND_ACCESS:
302			ret = EACCES;
303			goto out;
304
305		case SCF_ERROR_BACKEND_READONLY:
306readonly:
307			if (retake)
308				restarter_mark_pending_snapshot(fmri,
309				    RINST_RETAKE_START);
310			break;
311
312		case SCF_ERROR_HANDLE_MISMATCH:
313		case SCF_ERROR_NOT_SET:
314			bad_error("_scf_snapshot_attach", scf_error());
315		}
316	}
317
318out:
319	scf_snapshot_destroy(start);
320	scf_snapshot_destroy(running);
321	scf_instance_destroy(inst);
322
323	return (ret);
324}
325
326/*
327 * Before a refresh, update the "running" snapshot from the editing
328 * configuration.
329 *
330 * Returns 0 on success and -1 on failure.
331 */
332int
333libscf_snapshots_refresh(scf_instance_t *inst, const char *fmri)
334{
335	scf_handle_t *h;
336	scf_snapshot_t *snap;
337	boolean_t err = 1;
338
339	h = scf_instance_handle(inst);
340	if (h == NULL)
341		goto out;
342
343	snap = scf_snapshot_create(h);
344	if (snap == NULL)
345		goto out;
346
347	if (scf_instance_get_snapshot(inst, SMF_SNAPSHOT_RUNNING, snap) == 0) {
348		if (_scf_snapshot_take_attach(inst, snap) == 0)
349			err = 0;
350	} else {
351		switch (scf_error()) {
352		case SCF_ERROR_DELETED:
353			err = 0;
354			goto out;
355
356		case SCF_ERROR_NOT_FOUND:
357			break;
358
359		case SCF_ERROR_NOT_SET:
360			assert(0);
361			abort();
362			/* NOTREACHED */
363
364		default:
365			goto out;
366		}
367
368		log_error(LOG_DEBUG,
369		    "Service %s has no %s snapshot; creating one.\n", fmri,
370		    SMF_SNAPSHOT_RUNNING);
371
372		if (_scf_snapshot_take_new(inst, SMF_SNAPSHOT_RUNNING,
373		    snap) == 0)
374			err = 0;
375	}
376
377out:
378	scf_snapshot_destroy(snap);
379
380	if (!err)
381		return (0);
382
383	log_error(LOG_WARNING,
384	    "Could not update \"running\" snapshot for refresh of %s.\n", fmri);
385	return (-1);
386}
387
388/*
389 * int libscf_read_single_astring()
390 *   Reads a single astring value of the requested property into the
391 *   pre-allocated buffer (conventionally of size max_scf_value_size).
392 *   Multiple values constitute an error.
393 *
394 * Returns 0 on success or LIBSCF_PROPERTY_ABSENT or LIBSCF_PROPERTY_ERROR.
395 */
396static int
397libscf_read_single_astring(scf_handle_t *h, scf_property_t *prop, char **ret)
398{
399	scf_value_t *val = safe_scf_value_create(h);
400	int r = 0;
401
402	if (scf_property_get_value(prop, val) == -1) {
403		if (scf_error() == SCF_ERROR_NOT_FOUND)
404			r = LIBSCF_PROPERTY_ABSENT;
405		else
406			r = LIBSCF_PROPERTY_ERROR;
407		goto read_single_astring_fail;
408	}
409
410	if (scf_value_get_astring(val, *ret, max_scf_value_size) <= 0) {
411		r = LIBSCF_PROPERTY_ERROR;
412		goto read_single_astring_fail;
413	}
414
415read_single_astring_fail:
416	scf_value_destroy(val);
417	return (r);
418}
419
420static int
421libscf_read_state(const scf_propertygroup_t *pg, const char *prop_name,
422    restarter_instance_state_t *state)
423{
424	scf_handle_t *h;
425	scf_property_t *prop;
426	char *char_state = startd_alloc(max_scf_value_size);
427	int ret = 0;
428
429	h = scf_pg_handle(pg);
430	prop = safe_scf_property_create(h);
431
432	if (scf_pg_get_property(pg, prop_name, prop) == -1) {
433		if (scf_error() == SCF_ERROR_NOT_FOUND)
434			ret = LIBSCF_PROPERTY_ABSENT;
435		else
436			ret = LIBSCF_PROPERTY_ERROR;
437	} else {
438		ret = libscf_read_single_astring(h, prop, &char_state);
439		if (ret != 0) {
440			if (ret != LIBSCF_PROPERTY_ABSENT)
441				ret = LIBSCF_PROPERTY_ERROR;
442		} else {
443			*state = restarter_string_to_state(char_state);
444			ret = 0;
445		}
446	}
447
448	startd_free(char_state, max_scf_value_size);
449	scf_property_destroy(prop);
450	return (ret);
451}
452
453/*
454 * int libscf_read_states(const scf_propertygroup_t *,
455 *   restarter_instance_state_t *, restarter_instance_state_t *)
456 *
457 *   Set the current state and next_state values for the given service instance.
458 *   Returns 0 on success, or a libscf error code on failure.
459 */
460int
461libscf_read_states(const scf_propertygroup_t *pg,
462    restarter_instance_state_t *state, restarter_instance_state_t *next_state)
463{
464	int state_ret, next_state_ret, ret;
465
466	state_ret = libscf_read_state(pg, SCF_PROPERTY_STATE, state);
467	next_state_ret = libscf_read_state(pg, SCF_PROPERTY_NEXT_STATE,
468	    next_state);
469
470	if (state_ret == LIBSCF_PROPERTY_ERROR ||
471	    next_state_ret == LIBSCF_PROPERTY_ERROR) {
472		ret = LIBSCF_PROPERTY_ERROR;
473	} else if (state_ret == 0 && next_state_ret == 0) {
474		ret = 0;
475	} else if (state_ret == LIBSCF_PROPERTY_ABSENT &&
476	    next_state_ret == LIBSCF_PROPERTY_ABSENT) {
477		*state = RESTARTER_STATE_UNINIT;
478		*next_state = RESTARTER_STATE_NONE;
479		ret = 0;
480	} else if (state_ret == LIBSCF_PROPERTY_ABSENT ||
481	    next_state_ret == LIBSCF_PROPERTY_ABSENT) {
482		log_framework(LOG_DEBUG,
483		    "Only one repository state exists, setting "
484		    "restarter states to MAINTENANCE and NONE\n");
485		*state = RESTARTER_STATE_MAINT;
486		*next_state = RESTARTER_STATE_NONE;
487		ret = 0;
488	} else {
489		ret = LIBSCF_PROPERTY_ERROR;
490	}
491
492read_states_out:
493	return (ret);
494}
495
496/*
497 * depgroup_empty()
498 *
499 * Returns 0 if not empty.
500 * Returns 1 if empty.
501 * Returns -1 on error (check scf_error()).
502 */
503int
504depgroup_empty(scf_handle_t *h, scf_propertygroup_t *pg)
505{
506	int empty = 1;
507	scf_iter_t *iter;
508	scf_property_t *prop;
509	int ret;
510
511	iter = safe_scf_iter_create(h);
512	prop = safe_scf_property_create(h);
513
514	if (scf_iter_pg_properties(iter, pg) != SCF_SUCCESS) {
515		scf_property_destroy(prop);
516		scf_iter_destroy(iter);
517		return (-1);
518	}
519
520	ret = scf_iter_next_property(iter, prop);
521	if (ret < 0) {
522		scf_property_destroy(prop);
523		scf_iter_destroy(iter);
524		return (-1);
525	}
526
527	if (ret == 1)
528		empty = 0;
529
530	scf_property_destroy(prop);
531	scf_iter_destroy(iter);
532
533	return (empty);
534}
535
536gv_type_t
537depgroup_read_scheme(scf_handle_t *h, scf_propertygroup_t *pg)
538{
539	scf_property_t *prop;
540	char *scheme = startd_alloc(max_scf_value_size);
541	gv_type_t ret;
542
543	prop = safe_scf_property_create(h);
544
545	if (scf_pg_get_property(pg, SCF_PROPERTY_TYPE, prop) == -1 ||
546	    libscf_read_single_astring(h, prop, &scheme) != 0) {
547		scf_property_destroy(prop);
548		startd_free(scheme, max_scf_value_size);
549		return (GVT_UNSUPPORTED);
550	}
551
552	if (strcmp(scheme, "service") == 0)
553		ret = GVT_INST;
554	else if (strcmp(scheme, "path") == 0)
555		ret = GVT_FILE;
556	else
557		ret = GVT_UNSUPPORTED;
558
559	startd_free(scheme, max_scf_value_size);
560	scf_property_destroy(prop);
561	return (ret);
562}
563
564depgroup_type_t
565depgroup_read_grouping(scf_handle_t *h, scf_propertygroup_t *pg)
566{
567	char *grouping = startd_alloc(max_scf_value_size);
568	depgroup_type_t ret;
569	scf_property_t *prop = safe_scf_property_create(h);
570
571	if (scf_pg_get_property(pg, SCF_PROPERTY_GROUPING, prop) == -1 ||
572	    libscf_read_single_astring(h, prop, &grouping) != 0) {
573		scf_property_destroy(prop);
574		startd_free(grouping, max_scf_value_size);
575		return (DEPGRP_UNSUPPORTED);
576	}
577
578	if (strcmp(grouping, SCF_DEP_REQUIRE_ANY) == 0)
579		ret = DEPGRP_REQUIRE_ANY;
580	else if (strcmp(grouping, SCF_DEP_REQUIRE_ALL) == 0)
581		ret = DEPGRP_REQUIRE_ALL;
582	else if (strcmp(grouping, SCF_DEP_OPTIONAL_ALL) == 0)
583		ret = DEPGRP_OPTIONAL_ALL;
584	else if (strcmp(grouping, SCF_DEP_EXCLUDE_ALL) == 0)
585		ret = DEPGRP_EXCLUDE_ALL;
586	else {
587		ret = DEPGRP_UNSUPPORTED;
588	}
589	startd_free(grouping, max_scf_value_size);
590	scf_property_destroy(prop);
591	return (ret);
592}
593
594restarter_error_t
595depgroup_read_restart(scf_handle_t *h, scf_propertygroup_t *pg)
596{
597	scf_property_t *prop = safe_scf_property_create(h);
598	char *restart_on = startd_alloc(max_scf_value_size);
599	restarter_error_t ret;
600
601	if (scf_pg_get_property(pg, SCF_PROPERTY_RESTART_ON, prop) == -1 ||
602	    libscf_read_single_astring(h, prop, &restart_on) != 0) {
603		startd_free(restart_on, max_scf_value_size);
604		scf_property_destroy(prop);
605		return (RERR_UNSUPPORTED);
606	}
607
608	if (strcmp(restart_on, SCF_DEP_RESET_ON_ERROR) == 0)
609		ret = RERR_FAULT;
610	else if (strcmp(restart_on, SCF_DEP_RESET_ON_RESTART) == 0)
611		ret = RERR_RESTART;
612	else if (strcmp(restart_on, SCF_DEP_RESET_ON_REFRESH) == 0)
613		ret = RERR_REFRESH;
614	else if (strcmp(restart_on, SCF_DEP_RESET_ON_NONE) == 0)
615		ret = RERR_NONE;
616	else
617		ret = RERR_UNSUPPORTED;
618
619	startd_free(restart_on, max_scf_value_size);
620	scf_property_destroy(prop);
621	return (ret);
622}
623
624/*
625 * int get_boolean()
626 *   Fetches the value of a boolean property of the given property group.
627 *   Returns
628 *     0 - success
629 *     ECONNABORTED - repository connection broken
630 *     ECANCELED - pg was deleted
631 *     ENOENT - the property doesn't exist or has no values
632 *     EINVAL - the property has the wrong type
633 *		the property is not single-valued
634 *     EACCES - the current user does not have permission to read the value
635 */
636static int
637get_boolean(scf_propertygroup_t *pg, const char *propname, uint8_t *valuep)
638{
639	scf_handle_t *h;
640	scf_property_t *prop;
641	scf_value_t *val;
642	int ret = 0, r;
643	scf_type_t type;
644
645	h = scf_pg_handle(pg);
646	prop = safe_scf_property_create(h);
647	val = safe_scf_value_create(h);
648
649	if (scf_pg_get_property(pg, propname, prop) != 0) {
650		switch (scf_error()) {
651		case SCF_ERROR_CONNECTION_BROKEN:
652		default:
653			ret = ECONNABORTED;
654			goto out;
655
656		case SCF_ERROR_DELETED:
657			ret = ECANCELED;
658			goto out;
659
660		case SCF_ERROR_NOT_FOUND:
661			ret = ENOENT;
662			goto out;
663
664		case SCF_ERROR_HANDLE_MISMATCH:
665		case SCF_ERROR_INVALID_ARGUMENT:
666		case SCF_ERROR_NOT_SET:
667			bad_error("scf_pg_get_property", scf_error());
668		}
669	}
670
671	if (scf_property_type(prop, &type) != 0) {
672		switch (scf_error()) {
673		case SCF_ERROR_CONNECTION_BROKEN:
674		default:
675			ret = ECONNABORTED;
676			goto out;
677
678		case SCF_ERROR_DELETED:
679			ret = ENOENT;
680			goto out;
681
682		case SCF_ERROR_NOT_SET:
683			bad_error("scf_property_type", scf_error());
684		}
685	}
686
687	if (type != SCF_TYPE_BOOLEAN) {
688		ret = EINVAL;
689		goto out;
690	}
691
692	if (scf_property_get_value(prop, val) != 0) {
693		switch (scf_error()) {
694		case SCF_ERROR_CONNECTION_BROKEN:
695		default:
696			ret = ECONNABORTED;
697			goto out;
698
699		case SCF_ERROR_DELETED:
700		case SCF_ERROR_NOT_FOUND:
701			ret = ENOENT;
702			goto out;
703
704		case SCF_ERROR_CONSTRAINT_VIOLATED:
705			ret = EINVAL;
706			goto out;
707
708		case SCF_ERROR_PERMISSION_DENIED:
709			ret = EACCES;
710			goto out;
711
712		case SCF_ERROR_NOT_SET:
713			bad_error("scf_property_get_value", scf_error());
714		}
715	}
716
717	r = scf_value_get_boolean(val, valuep);
718	assert(r == 0);
719
720out:
721	scf_value_destroy(val);
722	scf_property_destroy(prop);
723	return (ret);
724}
725
726/*
727 * int get_count()
728 *   Fetches the value of a count property of the given property group.
729 *   Returns
730 *     0 - success
731 *     ECONNABORTED - repository connection broken
732 *                    unknown libscf error
733 *     ECANCELED - pg was deleted
734 *     ENOENT - the property doesn't exist or has no values
735 *     EINVAL - the property has the wrong type
736 *              the property is not single-valued
737 *     EACCES - the current user does not have permission to read the value
738 */
739static int
740get_count(scf_propertygroup_t *pg, const char *propname, uint64_t *valuep)
741{
742	scf_handle_t *h;
743	scf_property_t *prop;
744	scf_value_t *val;
745	int ret = 0, r;
746
747	h = scf_pg_handle(pg);
748	prop = safe_scf_property_create(h);
749	val = safe_scf_value_create(h);
750
751	if (scf_pg_get_property(pg, propname, prop) != 0) {
752		switch (scf_error()) {
753		case SCF_ERROR_CONNECTION_BROKEN:
754		default:
755			ret = ECONNABORTED;
756			goto out;
757
758		case SCF_ERROR_DELETED:
759			ret = ECANCELED;
760			goto out;
761
762		case SCF_ERROR_NOT_FOUND:
763			ret = ENOENT;
764			goto out;
765
766		case SCF_ERROR_HANDLE_MISMATCH:
767		case SCF_ERROR_INVALID_ARGUMENT:
768		case SCF_ERROR_NOT_SET:
769			bad_error("scf_pg_get_property", scf_error());
770		}
771	}
772
773	if (scf_property_is_type(prop, SCF_TYPE_COUNT) != 0) {
774		switch (scf_error()) {
775		case SCF_ERROR_CONNECTION_BROKEN:
776		default:
777			ret = ECONNABORTED;
778			goto out;
779
780		case SCF_ERROR_TYPE_MISMATCH:
781			ret = EINVAL;
782			goto out;
783
784		case SCF_ERROR_DELETED:
785			ret = ECANCELED;
786			goto out;
787
788		case SCF_ERROR_INVALID_ARGUMENT:
789		case SCF_ERROR_NOT_BOUND:
790		case SCF_ERROR_NOT_SET:
791			bad_error("scf_property_is_type", scf_error());
792		}
793	}
794
795	if (scf_property_get_value(prop, val) != 0) {
796		switch (scf_error()) {
797		case SCF_ERROR_CONNECTION_BROKEN:
798		default:
799			ret = ECONNABORTED;
800			goto out;
801
802		case SCF_ERROR_DELETED:
803			ret = ECANCELED;
804			goto out;
805
806		case SCF_ERROR_NOT_FOUND:
807			ret = ENOENT;
808			goto out;
809
810		case SCF_ERROR_CONSTRAINT_VIOLATED:
811			ret = EINVAL;
812			goto out;
813
814		case SCF_ERROR_PERMISSION_DENIED:
815			ret = EACCES;
816			goto out;
817
818		case SCF_ERROR_NOT_SET:
819			bad_error("scf_property_get_value", scf_error());
820		}
821	}
822
823	r = scf_value_get_count(val, valuep);
824	assert(r == 0);
825
826out:
827	scf_value_destroy(val);
828	scf_property_destroy(prop);
829	return (ret);
830}
831
832
833static void
834get_restarter(scf_handle_t *h, scf_propertygroup_t *pg, char **restarter)
835{
836	scf_property_t *prop = safe_scf_property_create(h);
837
838	if (scf_pg_get_property(pg, SCF_PROPERTY_RESTARTER, prop) == -1 ||
839	    libscf_read_single_astring(h, prop, restarter) != 0)
840		*restarter[0] = '\0';
841
842	scf_property_destroy(prop);
843}
844
845/*
846 * int libscf_instance_get_fmri(scf_instance_t *, char **)
847 *   Give a valid SCF instance, return its FMRI.  Returns 0 on success,
848 *   ECONNABORTED, or ECANCELED if inst is deleted.
849 */
850int
851libscf_instance_get_fmri(scf_instance_t *inst, char **retp)
852{
853	char *inst_fmri = startd_alloc(max_scf_fmri_size);
854
855	inst_fmri[0] = 0;
856	if (scf_instance_to_fmri(inst, inst_fmri, max_scf_fmri_size) <= 0) {
857		startd_free(inst_fmri, max_scf_fmri_size);
858		switch (scf_error()) {
859		case SCF_ERROR_CONNECTION_BROKEN:
860		default:
861			return (ECONNABORTED);
862
863		case SCF_ERROR_DELETED:
864			return (ECANCELED);
865
866		case SCF_ERROR_NOT_SET:
867			assert(0);
868			abort();
869		}
870	}
871
872	*retp = inst_fmri;
873	return (0);
874}
875
876/*
877 * int libscf_fmri_get_instance(scf_handle_t *, const char *,
878 *	scf_instance_t **)
879 *   Given a valid SCF handle and an FMRI, return the SCF instance that matches
880 *   exactly.  The instance must be released using scf_instance_destroy().
881 *   Returns 0 on success, EINVAL if the FMRI is invalid, ENOTSUP if the FMRI
882 *   is valid but designates something other than an instance, ECONNABORTED if
883 *   the repository connection is broken, or ENOENT if the instance does not
884 *   exist.
885 */
886int
887libscf_fmri_get_instance(scf_handle_t *h, const char *fmri,
888    scf_instance_t **instp)
889{
890	scf_instance_t *inst;
891	int r;
892
893	inst = safe_scf_instance_create(h);
894
895	r = libscf_lookup_instance(fmri, inst);
896
897	if (r == 0)
898		*instp = inst;
899	else
900		scf_instance_destroy(inst);
901
902	return (r);
903}
904
905int
906libscf_lookup_instance(const char *fmri, scf_instance_t *inst)
907{
908	if (scf_handle_decode_fmri(scf_instance_handle(inst), fmri, NULL, NULL,
909	    inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
910		switch (scf_error()) {
911		case SCF_ERROR_INVALID_ARGUMENT:
912			return (EINVAL);
913
914		case SCF_ERROR_CONSTRAINT_VIOLATED:
915			return (ENOTSUP);
916
917		case SCF_ERROR_CONNECTION_BROKEN:
918			return (ECONNABORTED);
919
920		case SCF_ERROR_NOT_FOUND:
921			return (ENOENT);
922
923		case SCF_ERROR_HANDLE_MISMATCH:
924		default:
925			bad_error("scf_handle_decode_fmri", scf_error());
926		}
927	}
928
929	return (0);
930}
931
932/*
933 * void libscf_get_basic_instance_data()
934 *   Read enabled, enabled_ovr, and restarter_fmri (into an allocated
935 *   buffer) for inst.  Returns 0, ECONNABORTED if the connection to the
936 *   repository is broken, ECANCELED if inst is deleted, or ENOENT if inst
937 *   has no general property group.
938 *
939 *   On success, restarter_fmri may be NULL.  If general/enabled was missing
940 *   or invalid, *enabledp will be -1 and a debug message is logged.
941 */
942int
943libscf_get_basic_instance_data(scf_handle_t *h, scf_instance_t *inst,
944    const char *fmri, int *enabledp, int *enabled_ovrp, char **restarter_fmri)
945{
946	scf_propertygroup_t *pg;
947	int r;
948	uint8_t enabled_8;
949
950	pg = safe_scf_pg_create(h);
951
952	if (enabled_ovrp == NULL)
953		goto enabled;
954
955	if (scf_instance_get_pg_composed(inst, NULL, SCF_PG_GENERAL_OVR, pg) !=
956	    0) {
957		switch (scf_error()) {
958		case SCF_ERROR_CONNECTION_BROKEN:
959		default:
960			scf_pg_destroy(pg);
961			return (ECONNABORTED);
962
963		case SCF_ERROR_DELETED:
964			scf_pg_destroy(pg);
965			return (ECANCELED);
966
967		case SCF_ERROR_NOT_FOUND:
968			*enabled_ovrp = -1;
969			break;
970
971		case SCF_ERROR_HANDLE_MISMATCH:
972		case SCF_ERROR_INVALID_ARGUMENT:
973		case SCF_ERROR_NOT_SET:
974			bad_error("scf_instance_get_pg_composed", scf_error());
975		}
976	} else {
977		switch (r = get_boolean(pg, SCF_PROPERTY_ENABLED, &enabled_8)) {
978		case 0:
979			*enabled_ovrp = enabled_8;
980			break;
981
982		case ECONNABORTED:
983		case ECANCELED:
984			scf_pg_destroy(pg);
985			return (r);
986
987		case ENOENT:
988		case EINVAL:
989			*enabled_ovrp = -1;
990			break;
991
992		case EACCES:
993		default:
994			bad_error("get_boolean", r);
995		}
996	}
997
998enabled:
999	/*
1000	 * Since general/restarter can be at the service level, we must do
1001	 * a composed lookup.  These properties are immediate, though, so we
1002	 * must use the "editing" snapshot.  Technically enabled shouldn't be
1003	 * at the service level, but looking it up composed, too, doesn't
1004	 * hurt.
1005	 */
1006	if (scf_instance_get_pg_composed(inst, NULL, SCF_PG_GENERAL, pg) != 0) {
1007		scf_pg_destroy(pg);
1008		switch (scf_error()) {
1009		case SCF_ERROR_CONNECTION_BROKEN:
1010		default:
1011			return (ECONNABORTED);
1012
1013		case SCF_ERROR_DELETED:
1014			return (ECANCELED);
1015
1016		case SCF_ERROR_NOT_FOUND:
1017			return (ENOENT);
1018
1019		case SCF_ERROR_NOT_SET:
1020			bad_error("scf_instance_get_pg_composed", scf_error());
1021		}
1022	}
1023
1024	switch (r = get_boolean(pg, SCF_PROPERTY_ENABLED, &enabled_8)) {
1025	case 0:
1026		*enabledp = enabled_8;
1027		break;
1028
1029	case ECONNABORTED:
1030	case ECANCELED:
1031		scf_pg_destroy(pg);
1032		return (r);
1033
1034	case ENOENT:
1035		/*
1036		 * DEBUG because this happens when svccfg import creates
1037		 * a temporary service.
1038		 */
1039		log_framework(LOG_DEBUG,
1040		    "general/enabled property of %s is missing.\n", fmri);
1041		*enabledp = -1;
1042		break;
1043
1044	case EINVAL:
1045		log_framework(LOG_ERR,
1046		    "general/enabled property of %s is invalid.\n", fmri);
1047		*enabledp = -1;
1048		break;
1049
1050	case EACCES:
1051	default:
1052		bad_error("get_boolean", r);
1053	}
1054
1055	if (restarter_fmri != NULL)
1056		get_restarter(h, pg, restarter_fmri);
1057
1058	scf_pg_destroy(pg);
1059
1060	return (0);
1061}
1062
1063
1064/*
1065 * Sets pg to the name property group of s_inst.  If it doesn't exist, it is
1066 * added.
1067 *
1068 * Fails with
1069 *   ECONNABORTED - repository disconnection or unknown libscf error
1070 *   ECANCELED - inst is deleted
1071 *   EPERM - permission is denied
1072 *   EACCES - backend denied access
1073 *   EROFS - backend readonly
1074 */
1075int
1076libscf_inst_get_or_add_pg(scf_instance_t *inst, const char *name,
1077    const char *type, uint32_t flags, scf_propertygroup_t *pg)
1078{
1079	uint32_t f;
1080
1081again:
1082	if (scf_instance_get_pg(inst, name, pg) == 0) {
1083		if (scf_pg_get_flags(pg, &f) != 0) {
1084			switch (scf_error()) {
1085			case SCF_ERROR_CONNECTION_BROKEN:
1086			default:
1087				return (ECONNABORTED);
1088
1089			case SCF_ERROR_DELETED:
1090				goto add;
1091
1092			case SCF_ERROR_NOT_SET:
1093				bad_error("scf_pg_get_flags", scf_error());
1094			}
1095		}
1096
1097		if (f == flags)
1098			return (0);
1099
1100		if (scf_pg_delete(pg) != 0) {
1101			switch (scf_error()) {
1102			case SCF_ERROR_CONNECTION_BROKEN:
1103			default:
1104				return (ECONNABORTED);
1105
1106			case SCF_ERROR_DELETED:
1107				break;
1108
1109			case SCF_ERROR_PERMISSION_DENIED:
1110				return (EPERM);
1111
1112			case SCF_ERROR_BACKEND_ACCESS:
1113				return (EACCES);
1114
1115			case SCF_ERROR_BACKEND_READONLY:
1116				return (EROFS);
1117
1118			case SCF_ERROR_NOT_SET:
1119				bad_error("scf_pg_delete", scf_error());
1120			}
1121		}
1122	} else {
1123		switch (scf_error()) {
1124		case SCF_ERROR_CONNECTION_BROKEN:
1125		default:
1126			return (ECONNABORTED);
1127
1128		case SCF_ERROR_DELETED:
1129			return (ECANCELED);
1130
1131		case SCF_ERROR_NOT_FOUND:
1132			break;
1133
1134		case SCF_ERROR_HANDLE_MISMATCH:
1135		case SCF_ERROR_INVALID_ARGUMENT:
1136		case SCF_ERROR_NOT_SET:
1137			bad_error("scf_instance_get_pg", scf_error());
1138		}
1139	}
1140
1141add:
1142	if (scf_instance_add_pg(inst, name, type, flags, pg) == 0)
1143		return (0);
1144
1145	switch (scf_error()) {
1146	case SCF_ERROR_CONNECTION_BROKEN:
1147	default:
1148		return (ECONNABORTED);
1149
1150	case SCF_ERROR_DELETED:
1151		return (ECANCELED);
1152
1153	case SCF_ERROR_EXISTS:
1154		goto again;
1155
1156	case SCF_ERROR_PERMISSION_DENIED:
1157		return (EPERM);
1158
1159	case SCF_ERROR_BACKEND_ACCESS:
1160		return (EACCES);
1161
1162	case SCF_ERROR_BACKEND_READONLY:
1163		return (EROFS);
1164
1165	case SCF_ERROR_HANDLE_MISMATCH:
1166	case SCF_ERROR_INVALID_ARGUMENT:
1167	case SCF_ERROR_NOT_SET:
1168		bad_error("scf_instance_add_pg", scf_error());
1169		/* NOTREACHED */
1170	}
1171}
1172
1173/*
1174 * Returns
1175 *   0 - success
1176 *   ECONNABORTED - repository connection broken
1177 *		  - unknown libscf error
1178 *   ECANCELED
1179 */
1180static scf_error_t
1181transaction_add_set(scf_transaction_t *tx, scf_transaction_entry_t *ent,
1182    const char *pname, scf_type_t ty)
1183{
1184	for (;;) {
1185		if (scf_transaction_property_change_type(tx, ent, pname,
1186		    ty) == 0)
1187			return (0);
1188
1189		switch (scf_error()) {
1190		case SCF_ERROR_CONNECTION_BROKEN:
1191		default:
1192			return (ECONNABORTED);
1193
1194		case SCF_ERROR_DELETED:
1195			return (ECANCELED);
1196
1197		case SCF_ERROR_NOT_FOUND:
1198			break;
1199
1200		case SCF_ERROR_HANDLE_MISMATCH:
1201		case SCF_ERROR_INVALID_ARGUMENT:
1202		case SCF_ERROR_IN_USE:
1203		case SCF_ERROR_NOT_SET:
1204			bad_error("scf_transaction_property_change_type",
1205			    scf_error());
1206		}
1207
1208		if (scf_transaction_property_new(tx, ent, pname, ty) == 0)
1209			return (0);
1210
1211		switch (scf_error()) {
1212		case SCF_ERROR_CONNECTION_BROKEN:
1213		default:
1214			return (ECONNABORTED);
1215
1216		case SCF_ERROR_DELETED:
1217			return (ECANCELED);
1218
1219		case SCF_ERROR_EXISTS:
1220			break;
1221
1222		case SCF_ERROR_HANDLE_MISMATCH:
1223		case SCF_ERROR_INVALID_ARGUMENT:
1224		case SCF_ERROR_IN_USE:
1225		case SCF_ERROR_NOT_SET:
1226			bad_error("scf_transaction_property_new", scf_error());
1227			/* NOTREACHED */
1228		}
1229	}
1230}
1231
1232/*
1233 * Returns
1234 *   0 - success
1235 *   ECONNABORTED - repository connection broken
1236 *		  - unknown libscf error
1237 *   ECANCELED - pg was deleted
1238 *   EPERM
1239 *   EACCES
1240 *   EROFS
1241 */
1242static int
1243pg_set_prop_value(scf_propertygroup_t *pg, const char *pname, scf_value_t *v)
1244{
1245	scf_handle_t *h;
1246	scf_transaction_t *tx;
1247	scf_transaction_entry_t *e;
1248	scf_type_t ty;
1249	scf_error_t scfe;
1250	int ret, r;
1251
1252	h = scf_pg_handle(pg);
1253	tx = safe_scf_transaction_create(h);
1254	e = safe_scf_entry_create(h);
1255
1256	ty = scf_value_type(v);
1257	assert(ty != SCF_TYPE_INVALID);
1258
1259	for (;;) {
1260		if (scf_transaction_start(tx, pg) != 0) {
1261			switch (scf_error()) {
1262			case SCF_ERROR_CONNECTION_BROKEN:
1263			default:
1264				ret = ECONNABORTED;
1265				goto out;
1266
1267			case SCF_ERROR_DELETED:
1268				ret = ECANCELED;
1269				goto out;
1270
1271			case SCF_ERROR_PERMISSION_DENIED:
1272				ret = EPERM;
1273				goto out;
1274
1275			case SCF_ERROR_BACKEND_ACCESS:
1276				ret = EACCES;
1277				goto out;
1278
1279			case SCF_ERROR_BACKEND_READONLY:
1280				ret = EROFS;
1281				goto out;
1282
1283			case SCF_ERROR_NOT_SET:
1284				bad_error("scf_transaction_start", ret);
1285			}
1286		}
1287
1288		ret = transaction_add_set(tx, e, pname, ty);
1289		switch (ret) {
1290		case 0:
1291			break;
1292
1293		case ECONNABORTED:
1294		case ECANCELED:
1295			goto out;
1296
1297		default:
1298			bad_error("transaction_add_set", ret);
1299		}
1300
1301		r = scf_entry_add_value(e, v);
1302		assert(r == 0);
1303
1304		r = scf_transaction_commit(tx);
1305		if (r == 1)
1306			break;
1307		if (r != 0) {
1308			scfe = scf_error();
1309			scf_transaction_reset(tx);
1310			switch (scfe) {
1311			case SCF_ERROR_CONNECTION_BROKEN:
1312			default:
1313				ret = ECONNABORTED;
1314				goto out;
1315
1316			case SCF_ERROR_DELETED:
1317				ret = ECANCELED;
1318				goto out;
1319
1320			case SCF_ERROR_PERMISSION_DENIED:
1321				ret = EPERM;
1322				goto out;
1323
1324			case SCF_ERROR_BACKEND_ACCESS:
1325				ret = EACCES;
1326				goto out;
1327
1328			case SCF_ERROR_BACKEND_READONLY:
1329				ret = EROFS;
1330				goto out;
1331
1332			case SCF_ERROR_NOT_SET:
1333				bad_error("scf_transaction_commit", scfe);
1334			}
1335		}
1336
1337		scf_transaction_reset(tx);
1338
1339		if (scf_pg_update(pg) == -1) {
1340			switch (scf_error()) {
1341			case SCF_ERROR_CONNECTION_BROKEN:
1342			default:
1343				ret = ECONNABORTED;
1344				goto out;
1345
1346			case SCF_ERROR_DELETED:
1347				ret = ECANCELED;
1348				goto out;
1349
1350			case SCF_ERROR_NOT_SET:
1351				bad_error("scf_pg_update", scf_error());
1352			}
1353		}
1354	}
1355
1356	ret = 0;
1357
1358out:
1359	scf_transaction_destroy(tx);
1360	scf_entry_destroy(e);
1361	return (ret);
1362}
1363
1364/*
1365 * Returns
1366 *   0 - success
1367 *   ECONNABORTED - repository connection broken
1368 *		  - unknown libscf error
1369 *   ECANCELED - inst was deleted
1370 *   EPERM
1371 *   EACCES
1372 *   EROFS
1373 */
1374int
1375libscf_inst_set_boolean_prop(scf_instance_t *inst, const char *pgname,
1376    const char *pgtype, uint32_t pgflags, const char *pname, int val)
1377{
1378	scf_handle_t *h;
1379	scf_propertygroup_t *pg = NULL;
1380	scf_value_t *v;
1381	int ret = 0;
1382
1383	h = scf_instance_handle(inst);
1384	pg = safe_scf_pg_create(h);
1385	v = safe_scf_value_create(h);
1386
1387	ret = libscf_inst_get_or_add_pg(inst, pgname, pgtype, pgflags, pg);
1388	switch (ret) {
1389	case 0:
1390		break;
1391
1392	case ECONNABORTED:
1393	case ECANCELED:
1394	case EPERM:
1395	case EACCES:
1396	case EROFS:
1397		goto out;
1398
1399	default:
1400		bad_error("libscf_inst_get_or_add_pg", ret);
1401	}
1402
1403	scf_value_set_boolean(v, val);
1404
1405	ret = pg_set_prop_value(pg, pname, v);
1406	switch (ret) {
1407	case 0:
1408	case ECONNABORTED:
1409	case ECANCELED:
1410	case EPERM:
1411	case EACCES:
1412	case EROFS:
1413		break;
1414
1415	default:
1416		bad_error("pg_set_prop_value", ret);
1417	}
1418
1419out:
1420	scf_pg_destroy(pg);
1421	scf_value_destroy(v);
1422	return (ret);
1423}
1424
1425/*
1426 * Returns
1427 *   0 - success
1428 *   ECONNABORTED - repository connection broken
1429 *		  - unknown libscf error
1430 *   ECANCELED - inst was deleted
1431 *   EPERM
1432 *   EACCES
1433 *   EROFS
1434 */
1435int
1436libscf_inst_set_count_prop(scf_instance_t *inst, const char *pgname,
1437    const char *pgtype, uint32_t pgflags, const char *pname, uint64_t count)
1438{
1439	scf_handle_t *h;
1440	scf_propertygroup_t *pg = NULL;
1441	scf_value_t *v;
1442	int ret = 0;
1443
1444	h = scf_instance_handle(inst);
1445	pg = safe_scf_pg_create(h);
1446	v = safe_scf_value_create(h);
1447
1448	ret = libscf_inst_get_or_add_pg(inst, pgname, pgtype, pgflags, pg);
1449	switch (ret) {
1450	case 0:
1451		break;
1452
1453	case ECONNABORTED:
1454	case ECANCELED:
1455	case EPERM:
1456	case EACCES:
1457	case EROFS:
1458		goto out;
1459
1460	default:
1461		bad_error("libscf_inst_get_or_add_pg", ret);
1462	}
1463
1464	scf_value_set_count(v, count);
1465
1466	ret = pg_set_prop_value(pg, pname, v);
1467	switch (ret) {
1468	case 0:
1469	case ECONNABORTED:
1470	case ECANCELED:
1471	case EPERM:
1472	case EACCES:
1473	case EROFS:
1474		break;
1475
1476	default:
1477		bad_error("pg_set_prop_value", ret);
1478	}
1479
1480out:
1481	scf_pg_destroy(pg);
1482	scf_value_destroy(v);
1483	return (ret);
1484}
1485
1486/*
1487 * Returns 0 on success, ECONNABORTED if the repository connection is broken,
1488 * ECANCELED if inst is deleted, EROFS if the backend is readonly, or EPERM if
1489 * permission was denied.
1490 */
1491int
1492libscf_set_enable_ovr(scf_instance_t *inst, int enable)
1493{
1494	return (libscf_inst_set_boolean_prop(inst, SCF_PG_GENERAL_OVR,
1495	    SCF_PG_GENERAL_OVR_TYPE, SCF_PG_GENERAL_OVR_FLAGS,
1496	    SCF_PROPERTY_ENABLED, enable));
1497}
1498
1499/*
1500 * Returns
1501 *   0 - success
1502 *   ECONNABORTED - repository connection broken
1503 *   ECANCELED - inst was deleted
1504 *   EPERM
1505 *   EACCES
1506 *   EROFS
1507 */
1508int
1509libscf_inst_delete_prop(scf_instance_t *inst, const char *pgname,
1510    const char *pname)
1511{
1512	scf_handle_t *h;
1513	scf_propertygroup_t *pg;
1514	scf_transaction_t *tx;
1515	scf_transaction_entry_t *e;
1516	scf_error_t serr;
1517	int ret = 0, r;
1518
1519	h = scf_instance_handle(inst);
1520	pg = safe_scf_pg_create(h);
1521
1522	if (scf_instance_get_pg(inst, pgname, pg) != 0) {
1523		scf_pg_destroy(pg);
1524		switch (scf_error()) {
1525		case SCF_ERROR_CONNECTION_BROKEN:
1526		default:
1527			return (ECONNABORTED);
1528
1529		case SCF_ERROR_DELETED:
1530			return (ECANCELED);
1531
1532		case SCF_ERROR_NOT_FOUND:
1533			return (0);
1534
1535		case SCF_ERROR_NOT_SET:
1536			bad_error("scf_instance_get_pg", scf_error());
1537		}
1538	}
1539
1540	tx = safe_scf_transaction_create(h);
1541	e = safe_scf_entry_create(h);
1542
1543	for (;;) {
1544		if (scf_transaction_start(tx, pg) != 0) {
1545			switch (scf_error()) {
1546			case SCF_ERROR_CONNECTION_BROKEN:
1547			default:
1548				ret = ECONNABORTED;
1549				goto out;
1550
1551			case SCF_ERROR_DELETED:
1552				ret = 0;
1553				goto out;
1554
1555			case SCF_ERROR_PERMISSION_DENIED:
1556				ret = EPERM;
1557				goto out;
1558
1559			case SCF_ERROR_BACKEND_ACCESS:
1560				ret = EACCES;
1561				goto out;
1562
1563			case SCF_ERROR_BACKEND_READONLY:
1564				ret = EROFS;
1565				goto out;
1566
1567			case SCF_ERROR_HANDLE_MISMATCH:
1568			case SCF_ERROR_INVALID_ARGUMENT:
1569			case SCF_ERROR_NOT_SET:
1570				bad_error("scf_transaction_start", scf_error());
1571			}
1572		}
1573
1574		if (scf_transaction_property_delete(tx, e, pname) != 0) {
1575			switch (scf_error()) {
1576			case SCF_ERROR_CONNECTION_BROKEN:
1577			default:
1578				ret = ECONNABORTED;
1579				goto out;
1580
1581			case SCF_ERROR_DELETED:
1582			case SCF_ERROR_NOT_FOUND:
1583				ret = 0;
1584				goto out;
1585
1586			case SCF_ERROR_NOT_SET:
1587			case SCF_ERROR_HANDLE_MISMATCH:
1588			case SCF_ERROR_NOT_BOUND:
1589			case SCF_ERROR_INVALID_ARGUMENT:
1590				bad_error("scf_transaction_property_delete",
1591				    scf_error());
1592			}
1593		}
1594
1595		r = scf_transaction_commit(tx);
1596		if (r == 1)
1597			break;
1598		if (r != 0) {
1599			serr = scf_error();
1600			scf_transaction_reset(tx);
1601			switch (serr) {
1602			case SCF_ERROR_CONNECTION_BROKEN:
1603			default:
1604				ret = ECONNABORTED;
1605				goto out;
1606
1607			case SCF_ERROR_DELETED:
1608				ret = 0;
1609				goto out;
1610
1611			case SCF_ERROR_PERMISSION_DENIED:
1612				ret = EPERM;
1613				goto out;
1614
1615			case SCF_ERROR_BACKEND_ACCESS:
1616				ret = EACCES;
1617				goto out;
1618
1619			case SCF_ERROR_BACKEND_READONLY:
1620				ret = EROFS;
1621				goto out;
1622
1623			case SCF_ERROR_NOT_SET:
1624			case SCF_ERROR_INVALID_ARGUMENT:
1625			case SCF_ERROR_NOT_BOUND:
1626				bad_error("scf_transaction_commit", serr);
1627			}
1628		}
1629
1630		scf_transaction_reset(tx);
1631
1632		if (scf_pg_update(pg) == -1) {
1633			switch (scf_error()) {
1634			case SCF_ERROR_CONNECTION_BROKEN:
1635			default:
1636				ret = ECONNABORTED;
1637				goto out;
1638
1639			case SCF_ERROR_DELETED:
1640				ret = 0;
1641				goto out;
1642
1643			case SCF_ERROR_NOT_SET:
1644			case SCF_ERROR_NOT_BOUND:
1645				bad_error("scf_pg_update", scf_error());
1646			}
1647		}
1648	}
1649
1650out:
1651	scf_transaction_destroy(tx);
1652	(void) scf_entry_destroy(e);
1653	scf_pg_destroy(pg);
1654	return (ret);
1655}
1656
1657/*
1658 * Returns 0, ECONNABORTED, ECANCELED, or EPERM.
1659 */
1660int
1661libscf_delete_enable_ovr(scf_instance_t *inst)
1662{
1663	return (libscf_inst_delete_prop(inst, SCF_PG_GENERAL_OVR,
1664	    SCF_PROPERTY_ENABLED));
1665}
1666
1667/*
1668 * Fails with
1669 *   ECONNABORTED - repository connection was broken
1670 *   ECANCELED - pg was deleted
1671 *   ENOENT - pg has no milestone property
1672 *   EINVAL - the milestone property is misconfigured
1673 */
1674static int
1675pg_get_milestone(scf_propertygroup_t *pg, scf_property_t *prop,
1676    scf_value_t *val, char *buf, size_t buf_sz)
1677{
1678	if (scf_pg_get_property(pg, SCF_PROPERTY_MILESTONE, prop) != 0) {
1679		switch (scf_error()) {
1680		case SCF_ERROR_CONNECTION_BROKEN:
1681		default:
1682			return (ECONNABORTED);
1683
1684		case SCF_ERROR_DELETED:
1685			return (ECANCELED);
1686
1687		case SCF_ERROR_NOT_FOUND:
1688			return (ENOENT);
1689
1690		case SCF_ERROR_HANDLE_MISMATCH:
1691		case SCF_ERROR_INVALID_ARGUMENT:
1692		case SCF_ERROR_NOT_SET:
1693			bad_error("scf_pg_get_property", scf_error());
1694		}
1695	}
1696
1697	if (scf_property_get_value(prop, val) != 0) {
1698		switch (scf_error()) {
1699		case SCF_ERROR_CONNECTION_BROKEN:
1700		default:
1701			return (ECONNABORTED);
1702
1703		case SCF_ERROR_DELETED:
1704		case SCF_ERROR_CONSTRAINT_VIOLATED:
1705		case SCF_ERROR_NOT_FOUND:
1706			return (EINVAL);
1707
1708		case SCF_ERROR_NOT_SET:
1709		case SCF_ERROR_PERMISSION_DENIED:
1710			bad_error("scf_property_get_value", scf_error());
1711		}
1712	}
1713
1714	if (scf_value_get_astring(val, buf, buf_sz) < 0) {
1715		switch (scf_error()) {
1716		case SCF_ERROR_TYPE_MISMATCH:
1717			return (EINVAL);
1718
1719		case SCF_ERROR_NOT_SET:
1720		default:
1721			bad_error("scf_value_get_astring", scf_error());
1722		}
1723	}
1724
1725	return (0);
1726}
1727
1728/*
1729 * Fails with
1730 *   ECONNABORTED - repository connection was broken
1731 *   ECANCELED - inst was deleted
1732 *   ENOENT - inst has no milestone property
1733 *   EINVAL - the milestone property is misconfigured
1734 */
1735int
1736libscf_get_milestone(scf_instance_t *inst, scf_property_t *prop,
1737    scf_value_t *val, char *buf, size_t buf_sz)
1738{
1739	scf_propertygroup_t *pg;
1740	int r;
1741
1742	pg = safe_scf_pg_create(scf_instance_handle(inst));
1743
1744	if (scf_instance_get_pg(inst, SCF_PG_OPTIONS_OVR, pg) == 0) {
1745		switch (r = pg_get_milestone(pg, prop, val, buf, buf_sz)) {
1746		case 0:
1747		case ECONNABORTED:
1748		case EINVAL:
1749			goto out;
1750
1751		case ECANCELED:
1752		case ENOENT:
1753			break;
1754
1755		default:
1756			bad_error("pg_get_milestone", r);
1757		}
1758	} else {
1759		switch (scf_error()) {
1760		case SCF_ERROR_CONNECTION_BROKEN:
1761		default:
1762			r = ECONNABORTED;
1763			goto out;
1764
1765		case SCF_ERROR_DELETED:
1766			r = ECANCELED;
1767			goto out;
1768
1769		case SCF_ERROR_NOT_FOUND:
1770			break;
1771
1772		case SCF_ERROR_HANDLE_MISMATCH:
1773		case SCF_ERROR_INVALID_ARGUMENT:
1774		case SCF_ERROR_NOT_SET:
1775			bad_error("scf_instance_get_pg", scf_error());
1776		}
1777	}
1778
1779	if (scf_instance_get_pg(inst, SCF_PG_OPTIONS, pg) == 0) {
1780		r = pg_get_milestone(pg, prop, val, buf, buf_sz);
1781	} else {
1782		switch (scf_error()) {
1783		case SCF_ERROR_CONNECTION_BROKEN:
1784		default:
1785			r = ECONNABORTED;
1786			goto out;
1787
1788		case SCF_ERROR_DELETED:
1789			r = ECANCELED;
1790			goto out;
1791
1792		case SCF_ERROR_NOT_FOUND:
1793			r = ENOENT;
1794			break;
1795
1796		case SCF_ERROR_HANDLE_MISMATCH:
1797		case SCF_ERROR_INVALID_ARGUMENT:
1798		case SCF_ERROR_NOT_SET:
1799			bad_error("scf_instance_get_pg", scf_error());
1800		}
1801	}
1802
1803out:
1804	scf_pg_destroy(pg);
1805
1806	return (r);
1807}
1808
1809/*
1810 * Get the runlevel character from the runlevel property of the given property
1811 * group.  Fails with
1812 *   ECONNABORTED - repository connection was broken
1813 *   ECANCELED - prop's property group was deleted
1814 *   ENOENT - the property has no values
1815 *   EINVAL - the property has more than one value
1816 *	      the property is of the wrong type
1817 *	      the property value is malformed
1818 */
1819int
1820libscf_extract_runlevel(scf_property_t *prop, char *rlp)
1821{
1822	scf_value_t *val;
1823	char buf[2];
1824
1825	val = safe_scf_value_create(scf_property_handle(prop));
1826
1827	if (scf_property_get_value(prop, val) != 0) {
1828		switch (scf_error()) {
1829		case SCF_ERROR_CONNECTION_BROKEN:
1830			return (ECONNABORTED);
1831
1832		case SCF_ERROR_NOT_SET:
1833			return (ENOENT);
1834
1835		case SCF_ERROR_DELETED:
1836			return (ECANCELED);
1837
1838		case SCF_ERROR_CONSTRAINT_VIOLATED:
1839			return (EINVAL);
1840
1841		case SCF_ERROR_NOT_FOUND:
1842			return (ENOENT);
1843
1844		case SCF_ERROR_HANDLE_MISMATCH:
1845		case SCF_ERROR_NOT_BOUND:
1846		case SCF_ERROR_PERMISSION_DENIED:
1847		default:
1848			bad_error("scf_property_get_value", scf_error());
1849		}
1850	}
1851
1852	if (scf_value_get_astring(val, buf, sizeof (buf)) < 0) {
1853		if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
1854			bad_error("scf_value_get_astring", scf_error());
1855
1856		return (EINVAL);
1857	}
1858
1859	if (buf[0] == '\0' || buf[1] != '\0')
1860		return (EINVAL);
1861
1862	*rlp = buf[0];
1863
1864	return (0);
1865}
1866
1867/*
1868 * Delete the "runlevel" property from the given property group.  Also set the
1869 * "milestone" property to the given string.  Fails with ECONNABORTED,
1870 * ECANCELED, EPERM, EACCES, or EROFS.
1871 */
1872int
1873libscf_clear_runlevel(scf_propertygroup_t *pg, const char *milestone)
1874{
1875	scf_handle_t *h;
1876	scf_transaction_t *tx;
1877	scf_transaction_entry_t *e_rl, *e_ms;
1878	scf_value_t *val;
1879	scf_error_t serr;
1880	boolean_t isempty = B_TRUE;
1881	int ret = 0, r;
1882
1883	h = scf_pg_handle(pg);
1884	tx = safe_scf_transaction_create(h);
1885	e_rl = safe_scf_entry_create(h);
1886	e_ms = safe_scf_entry_create(h);
1887	val = safe_scf_value_create(h);
1888
1889	if (milestone) {
1890		r = scf_value_set_astring(val, milestone);
1891		assert(r == 0);
1892	}
1893
1894	for (;;) {
1895		if (scf_transaction_start(tx, pg) != 0) {
1896			switch (scf_error()) {
1897			case SCF_ERROR_CONNECTION_BROKEN:
1898			default:
1899				ret = ECONNABORTED;
1900				goto out;
1901
1902			case SCF_ERROR_DELETED:
1903				ret = ECANCELED;
1904				goto out;
1905
1906			case SCF_ERROR_PERMISSION_DENIED:
1907				ret = EPERM;
1908				goto out;
1909
1910			case SCF_ERROR_BACKEND_ACCESS:
1911				ret = EACCES;
1912				goto out;
1913
1914			case SCF_ERROR_BACKEND_READONLY:
1915				ret = EROFS;
1916				goto out;
1917
1918			case SCF_ERROR_NOT_SET:
1919				bad_error("scf_transaction_start", scf_error());
1920			}
1921		}
1922
1923		if (scf_transaction_property_delete(tx, e_rl,
1924		    "runlevel") == 0) {
1925			isempty = B_FALSE;
1926		} else {
1927			switch (scf_error()) {
1928			case SCF_ERROR_CONNECTION_BROKEN:
1929			default:
1930				ret = ECONNABORTED;
1931				goto out;
1932
1933			case SCF_ERROR_DELETED:
1934				ret = ECANCELED;
1935				goto out;
1936
1937			case SCF_ERROR_NOT_FOUND:
1938				break;
1939
1940			case SCF_ERROR_HANDLE_MISMATCH:
1941			case SCF_ERROR_NOT_BOUND:
1942			case SCF_ERROR_INVALID_ARGUMENT:
1943				bad_error("scf_transaction_property_delete",
1944				    scf_error());
1945			}
1946		}
1947
1948		if (milestone) {
1949			ret = transaction_add_set(tx, e_ms,
1950			    SCF_PROPERTY_MILESTONE, SCF_TYPE_ASTRING);
1951			switch (ret) {
1952			case 0:
1953				break;
1954
1955			case ECONNABORTED:
1956			case ECANCELED:
1957				goto out;
1958
1959			default:
1960				bad_error("transaction_add_set", ret);
1961			}
1962
1963			isempty = B_FALSE;
1964
1965			r = scf_entry_add_value(e_ms, val);
1966			assert(r == 0);
1967		}
1968
1969		if (isempty)
1970			goto out;
1971
1972		r = scf_transaction_commit(tx);
1973		if (r == 1)
1974			break;
1975		if (r != 0) {
1976			serr = scf_error();
1977			scf_transaction_reset(tx);
1978			switch (serr) {
1979			case SCF_ERROR_CONNECTION_BROKEN:
1980				ret = ECONNABORTED;
1981				goto out;
1982
1983			case SCF_ERROR_PERMISSION_DENIED:
1984				ret = EPERM;
1985				goto out;
1986
1987			case SCF_ERROR_BACKEND_ACCESS:
1988				ret = EACCES;
1989				goto out;
1990
1991			case SCF_ERROR_BACKEND_READONLY:
1992				ret = EROFS;
1993				goto out;
1994
1995			default:
1996				bad_error("scf_transaction_commit", serr);
1997			}
1998		}
1999
2000		scf_transaction_reset(tx);
2001
2002		if (scf_pg_update(pg) == -1) {
2003			switch (scf_error()) {
2004			case SCF_ERROR_CONNECTION_BROKEN:
2005				ret = ECONNABORTED;
2006				goto out;
2007
2008			case SCF_ERROR_NOT_SET:
2009				ret = ECANCELED;
2010				goto out;
2011
2012			default:
2013				assert(0);
2014				abort();
2015			}
2016		}
2017	}
2018
2019out:
2020	scf_transaction_destroy(tx);
2021	scf_entry_destroy(e_rl);
2022	scf_entry_destroy(e_ms);
2023	scf_value_destroy(val);
2024	return (ret);
2025}
2026
2027/*
2028 * int libscf_get_template_values(scf_instance_t *, scf_snapshot_t *,
2029 *	char **)
2030 *
2031 *   Return template values for inst in *common_name suitable for use in
2032 *   restarter_inst_t->ri_common_name.  Called by restarter_insert_inst().
2033 *
2034 *   Returns 0 on success, ECANCELED if the instance is deleted, ECHILD if
2035 *   a value fetch failed for a property, ENOENT if the instance has no
2036 *   tm_common_name property group or the property group is deleted, and
2037 *   ECONNABORTED if the repository connection is broken.
2038 */
2039int
2040libscf_get_template_values(scf_instance_t *inst, scf_snapshot_t *snap,
2041    char **common_name, char **c_common_name)
2042{
2043	scf_handle_t *h;
2044	scf_propertygroup_t *pg = NULL;
2045	scf_property_t *prop = NULL;
2046	int ret = 0, r;
2047	char *cname = startd_alloc(max_scf_value_size);
2048	char *c_cname = startd_alloc(max_scf_value_size);
2049	int common_name_initialized = B_FALSE;
2050	int c_common_name_initialized = B_FALSE;
2051
2052	h = scf_instance_handle(inst);
2053	pg = safe_scf_pg_create(h);
2054	prop = safe_scf_property_create(h);
2055
2056	/*
2057	 * The tm_common_name property group, as with all template property
2058	 * groups, is optional.
2059	 */
2060	if (scf_instance_get_pg_composed(inst, snap, SCF_PG_TM_COMMON_NAME, pg)
2061	    == -1) {
2062		switch (scf_error()) {
2063		case SCF_ERROR_DELETED:
2064			ret = ECANCELED;
2065			goto template_values_out;
2066
2067		case SCF_ERROR_NOT_FOUND:
2068			goto template_values_out;
2069
2070		case SCF_ERROR_CONNECTION_BROKEN:
2071		default:
2072			ret = ECONNABORTED;
2073			goto template_values_out;
2074
2075		case SCF_ERROR_INVALID_ARGUMENT:
2076		case SCF_ERROR_HANDLE_MISMATCH:
2077		case SCF_ERROR_NOT_SET:
2078			bad_error("scf_instance_get_pg_composed", scf_error());
2079		}
2080	}
2081
2082	/*
2083	 * The name we wish uses the current locale name as the property name.
2084	 */
2085	if (st->st_locale != NULL) {
2086		if (scf_pg_get_property(pg, st->st_locale, prop) == -1) {
2087			switch (scf_error()) {
2088			case SCF_ERROR_DELETED:
2089			case SCF_ERROR_NOT_FOUND:
2090				break;
2091
2092			case SCF_ERROR_CONNECTION_BROKEN:
2093			default:
2094				ret = ECONNABORTED;
2095				goto template_values_out;
2096
2097			case SCF_ERROR_INVALID_ARGUMENT:
2098			case SCF_ERROR_HANDLE_MISMATCH:
2099			case SCF_ERROR_NOT_SET:
2100				bad_error("scf_pg_get_property", scf_error());
2101			}
2102		} else {
2103			if ((r = libscf_read_single_astring(h, prop, &cname)) !=
2104			    0) {
2105				if (r != LIBSCF_PROPERTY_ABSENT)
2106					ret = ECHILD;
2107				goto template_values_out;
2108			}
2109
2110			*common_name = cname;
2111			common_name_initialized = B_TRUE;
2112		}
2113	}
2114
2115	/*
2116	 * Also pull out the C locale name, as a fallback for the case where
2117	 * service offers no localized name.
2118	 */
2119	if (scf_pg_get_property(pg, "C", prop) == -1) {
2120		switch (scf_error()) {
2121		case SCF_ERROR_DELETED:
2122			ret = ENOENT;
2123			goto template_values_out;
2124
2125		case SCF_ERROR_NOT_FOUND:
2126			break;
2127
2128		case SCF_ERROR_CONNECTION_BROKEN:
2129		default:
2130			ret = ECONNABORTED;
2131			goto template_values_out;
2132
2133		case SCF_ERROR_INVALID_ARGUMENT:
2134		case SCF_ERROR_HANDLE_MISMATCH:
2135		case SCF_ERROR_NOT_SET:
2136			bad_error("scf_pg_get_property", scf_error());
2137		}
2138	} else {
2139		if ((r = libscf_read_single_astring(h, prop, &c_cname)) != 0) {
2140			if (r != LIBSCF_PROPERTY_ABSENT)
2141				ret = ECHILD;
2142			goto template_values_out;
2143		}
2144
2145		*c_common_name = c_cname;
2146		c_common_name_initialized = B_TRUE;
2147	}
2148
2149
2150template_values_out:
2151	if (common_name_initialized == B_FALSE)
2152		startd_free(cname, max_scf_value_size);
2153	if (c_common_name_initialized == B_FALSE)
2154		startd_free(c_cname, max_scf_value_size);
2155	scf_property_destroy(prop);
2156	scf_pg_destroy(pg);
2157
2158	return (ret);
2159}
2160
2161/*
2162 * int libscf_get_startd_properties(scf_handle_t *, scf_instance_t *,
2163 *	scf_snapshot_t *, uint_t *, char **)
2164 *
2165 *   Return startd settings for inst in *flags suitable for use in
2166 *   restarter_inst_t->ri_flags.  Called by restarter_insert_inst().
2167 *
2168 *   Returns 0 on success, ECANCELED if the instance is deleted, ECHILD if
2169 *   a value fetch failed for a property, ENOENT if the instance has no
2170 *   general property group or the property group is deleted, and
2171 *   ECONNABORTED if the repository connection is broken.
2172 */
2173int
2174libscf_get_startd_properties(scf_instance_t *inst,
2175    scf_snapshot_t *snap, uint_t *flags, char **prefixp)
2176{
2177	scf_handle_t *h;
2178	scf_propertygroup_t *pg = NULL;
2179	scf_property_t *prop = NULL;
2180	int style = RINST_CONTRACT;
2181	char *style_str = startd_alloc(max_scf_value_size);
2182	int ret = 0, r;
2183
2184	h = scf_instance_handle(inst);
2185	pg = safe_scf_pg_create(h);
2186	prop = safe_scf_property_create(h);
2187
2188	/*
2189	 * The startd property group is optional.
2190	 */
2191	if (scf_instance_get_pg_composed(inst, snap, SCF_PG_STARTD, pg) == -1) {
2192		switch (scf_error()) {
2193		case SCF_ERROR_DELETED:
2194			ret = ECANCELED;
2195			goto instance_flags_out;
2196
2197		case SCF_ERROR_NOT_FOUND:
2198			ret = ENOENT;
2199			goto instance_flags_out;
2200
2201		case SCF_ERROR_CONNECTION_BROKEN:
2202		default:
2203			ret = ECONNABORTED;
2204			goto instance_flags_out;
2205
2206		case SCF_ERROR_INVALID_ARGUMENT:
2207		case SCF_ERROR_HANDLE_MISMATCH:
2208		case SCF_ERROR_NOT_SET:
2209			bad_error("scf_instance_get_pg_composed", scf_error());
2210		}
2211	}
2212
2213	/*
2214	 * 1.  Duration property.
2215	 */
2216	if (scf_pg_get_property(pg, SCF_PROPERTY_DURATION, prop) == -1) {
2217		switch (scf_error()) {
2218		case SCF_ERROR_DELETED:
2219			ret = ENOENT;
2220			goto instance_flags_out;
2221
2222		case SCF_ERROR_NOT_FOUND:
2223			break;
2224
2225		case SCF_ERROR_CONNECTION_BROKEN:
2226		default:
2227			ret = ECONNABORTED;
2228			goto instance_flags_out;
2229
2230		case SCF_ERROR_INVALID_ARGUMENT:
2231		case SCF_ERROR_HANDLE_MISMATCH:
2232		case SCF_ERROR_NOT_SET:
2233			bad_error("scf_pg_get_property", scf_error());
2234		}
2235	} else {
2236		errno = 0;
2237		if ((r = libscf_read_single_astring(h, prop, &style_str))
2238		    != 0) {
2239			if (r != LIBSCF_PROPERTY_ABSENT)
2240				ret = ECHILD;
2241			goto instance_flags_out;
2242		}
2243
2244		if (strcmp(style_str, "child") == 0)
2245			style = RINST_WAIT;
2246		else if (strcmp(style_str, "transient") == 0)
2247			style = RINST_TRANSIENT;
2248	}
2249
2250	/*
2251	 * 2.  utmpx prefix property.
2252	 */
2253	if (scf_pg_get_property(pg, SCF_PROPERTY_UTMPX_PREFIX, prop) == 0) {
2254		errno = 0;
2255		if ((r = libscf_read_single_astring(h, prop, prefixp)) != 0) {
2256			if (r != LIBSCF_PROPERTY_ABSENT)
2257				ret = ECHILD;
2258			goto instance_flags_out;
2259		}
2260	} else {
2261		switch (scf_error()) {
2262		case SCF_ERROR_DELETED:
2263			ret = ENOENT;
2264			goto instance_flags_out;
2265
2266		case SCF_ERROR_NOT_FOUND:
2267			goto instance_flags_out;
2268
2269		case SCF_ERROR_CONNECTION_BROKEN:
2270		default:
2271			ret = ECONNABORTED;
2272			goto instance_flags_out;
2273
2274		case SCF_ERROR_INVALID_ARGUMENT:
2275		case SCF_ERROR_HANDLE_MISMATCH:
2276		case SCF_ERROR_NOT_SET:
2277			bad_error("scf_pg_get_property", scf_error());
2278		}
2279	}
2280
2281instance_flags_out:
2282	startd_free(style_str, max_scf_value_size);
2283	*flags = (*flags & ~RINST_STYLE_MASK) | style;
2284
2285	scf_property_destroy(prop);
2286	scf_pg_destroy(pg);
2287
2288	return (ret);
2289}
2290
2291/*
2292 * int libscf_read_method_ids(scf_handle_t *, scf_instance_t *, ctid_t *,
2293 *   ctid_t *, pid_t *)
2294 *
2295 *  Sets given id_t variables to primary and transient contract IDs and start
2296 *  PID.  Returns 0, ECONNABORTED, and ECANCELED.
2297 */
2298int
2299libscf_read_method_ids(scf_handle_t *h, scf_instance_t *inst, const char *fmri,
2300    ctid_t *primary, ctid_t *transient, pid_t *start_pid)
2301{
2302	scf_propertygroup_t *pg = NULL;
2303	scf_property_t *prop = NULL;
2304	scf_value_t *val = NULL;
2305	uint64_t p, t;
2306	int ret = 0;
2307
2308	*primary = 0;
2309	*transient = 0;
2310	*start_pid = -1;
2311
2312	pg = safe_scf_pg_create(h);
2313	prop = safe_scf_property_create(h);
2314	val = safe_scf_value_create(h);
2315
2316	if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) == -1) {
2317		switch (scf_error()) {
2318		case SCF_ERROR_CONNECTION_BROKEN:
2319		default:
2320			ret = ECONNABORTED;
2321			goto read_id_err;
2322
2323		case SCF_ERROR_DELETED:
2324			ret = ECANCELED;
2325			goto read_id_err;
2326
2327		case SCF_ERROR_NOT_FOUND:
2328			goto read_id_err;
2329
2330		case SCF_ERROR_NOT_SET:
2331			bad_error("scf_instance_get_pg", scf_error());
2332		}
2333	}
2334
2335	ret = get_count(pg, SCF_PROPERTY_CONTRACT, &p);
2336	switch (ret) {
2337	case 0:
2338		break;
2339
2340	case EINVAL:
2341		log_error(LOG_NOTICE,
2342		    "%s: Ignoring %s/%s: multivalued or not of type count\n",
2343		    fmri, SCF_PG_RESTARTER, SCF_PROPERTY_CONTRACT);
2344		/* FALLTHROUGH */
2345	case ENOENT:
2346		ret = 0;
2347		goto read_trans;
2348
2349	case ECONNABORTED:
2350	case ECANCELED:
2351		goto read_id_err;
2352
2353	case EACCES:
2354	default:
2355		bad_error("get_count", ret);
2356	}
2357
2358	*primary = p;
2359
2360read_trans:
2361	ret = get_count(pg, SCF_PROPERTY_TRANSIENT_CONTRACT, &t);
2362	switch (ret) {
2363	case 0:
2364		break;
2365
2366	case EINVAL:
2367		log_error(LOG_NOTICE,
2368		    "%s: Ignoring %s/%s: multivalued or not of type count\n",
2369		    fmri, SCF_PG_RESTARTER, SCF_PROPERTY_TRANSIENT_CONTRACT);
2370		/* FALLTHROUGH */
2371
2372	case ENOENT:
2373		ret = 0;
2374		goto read_pid_only;
2375
2376	case ECONNABORTED:
2377	case ECANCELED:
2378		goto read_id_err;
2379
2380	case EACCES:
2381	default:
2382		bad_error("get_count", ret);
2383	}
2384
2385	*transient = t;
2386
2387read_pid_only:
2388	ret = get_count(pg, SCF_PROPERTY_START_PID, &p);
2389	switch (ret) {
2390	case 0:
2391		break;
2392
2393	case EINVAL:
2394		log_error(LOG_NOTICE,
2395		    "%s: Ignoring %s/%s: multivalued or not of type count\n",
2396		    fmri, SCF_PG_RESTARTER, SCF_PROPERTY_START_PID);
2397		/* FALLTHROUGH */
2398	case ENOENT:
2399		ret = 0;
2400		goto read_id_err;
2401
2402	case ECONNABORTED:
2403	case ECANCELED:
2404		goto read_id_err;
2405
2406	case EACCES:
2407	default:
2408		bad_error("get_count", ret);
2409	}
2410
2411	*start_pid = p;
2412
2413read_id_err:
2414	scf_value_destroy(val);
2415	scf_property_destroy(prop);
2416	scf_pg_destroy(pg);
2417	return (ret);
2418}
2419
2420/*
2421 * Returns with
2422 *   0 - success
2423 *   ECONNABORTED - repository connection broken
2424 *		  - unknown libscf error
2425 *   ECANCELED - s_inst was deleted
2426 *   EPERM
2427 *   EACCES
2428 *   EROFS
2429 */
2430int
2431libscf_write_start_pid(scf_instance_t *s_inst, pid_t pid)
2432{
2433	scf_handle_t *h;
2434	scf_transaction_entry_t *t_pid;
2435	scf_value_t *v_pid;
2436	scf_propertygroup_t *pg;
2437	int ret = 0;
2438
2439	h = scf_instance_handle(s_inst);
2440
2441	pg = safe_scf_pg_create(h);
2442	t_pid = safe_scf_entry_create(h);
2443	v_pid = safe_scf_value_create(h);
2444
2445get_pg:
2446	ret = libscf_inst_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
2447	    SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
2448	switch (ret) {
2449	case 0:
2450		break;
2451
2452	case ECONNABORTED:
2453	case ECANCELED:
2454	case EPERM:
2455	case EACCES:
2456	case EROFS:
2457		goto write_start_err;
2458
2459	default:
2460		bad_error("libscf_inst_get_or_add_pg", ret);
2461	}
2462
2463	scf_value_set_count(v_pid, pid);
2464
2465	ret = pg_set_prop_value(pg, SCF_PROPERTY_START_PID, v_pid);
2466	switch (ret) {
2467	case 0:
2468	case ECONNABORTED:
2469	case EPERM:
2470	case EACCES:
2471	case EROFS:
2472		break;
2473
2474	case ECANCELED:
2475		goto get_pg;
2476
2477	default:
2478		bad_error("pg_set_prop_value", ret);
2479	}
2480
2481write_start_err:
2482	scf_entry_destroy(t_pid);
2483	scf_value_destroy(v_pid);
2484	scf_pg_destroy(pg);
2485
2486	return (ret);
2487}
2488
2489/*
2490 * Add a property indicating the instance log file.  If the dir is
2491 * equal to LOG_PREFIX_EARLY, then the property restarter/alt_logfile
2492 * of the instance is used; otherwise, restarter/logfile is used.
2493 *
2494 * Returns
2495 *   0 - success
2496 *   ECONNABORTED
2497 *   ECANCELED
2498 *   EPERM
2499 *   EACCES
2500 *   EROFS
2501 *   EAGAIN
2502 */
2503int
2504libscf_note_method_log(scf_instance_t *inst, const char *dir, const char *file)
2505{
2506	scf_handle_t *h;
2507	scf_value_t *v;
2508	scf_propertygroup_t *pg;
2509	int ret = 0;
2510	char *logname;
2511	const char *propname;
2512
2513	h = scf_instance_handle(inst);
2514	pg = safe_scf_pg_create(h);
2515	v = safe_scf_value_create(h);
2516
2517	logname = uu_msprintf("%s%s", dir, file);
2518
2519	if (logname == NULL) {
2520		ret = errno;
2521		goto out;
2522	}
2523
2524	ret = libscf_inst_get_or_add_pg(inst, SCF_PG_RESTARTER,
2525	    SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
2526	switch (ret) {
2527	case 0:
2528		break;
2529
2530	case ECONNABORTED:
2531	case ECANCELED:
2532	case EPERM:
2533	case EACCES:
2534	case EROFS:
2535		goto out;
2536
2537	default:
2538		bad_error("libscf_inst_get_or_add_pg", ret);
2539	}
2540
2541	(void) scf_value_set_astring(v, logname);
2542
2543	if (strcmp(LOG_PREFIX_EARLY, dir) == 0)
2544		propname = SCF_PROPERTY_ALT_LOGFILE;
2545	else
2546		propname = SCF_PROPERTY_LOGFILE;
2547
2548	ret = pg_set_prop_value(pg, propname, v);
2549	switch (ret) {
2550	case 0:
2551	case ECONNABORTED:
2552	case ECANCELED:
2553	case EPERM:
2554	case EACCES:
2555	case EROFS:
2556		break;
2557
2558	default:
2559		bad_error("pg_set_prop_value", ret);
2560	}
2561
2562out:
2563	scf_pg_destroy(pg);
2564	scf_value_destroy(v);
2565	uu_free(logname);
2566	return (ret);
2567}
2568
2569/*
2570 * Returns
2571 *   0 - success
2572 *   ENAMETOOLONG - name is too long
2573 *   ECONNABORTED
2574 *   ECANCELED
2575 *   EPERM
2576 *   EACCES
2577 *   EROFS
2578 */
2579int
2580libscf_write_method_status(scf_instance_t *s_inst, const char *name,
2581    int status)
2582{
2583	scf_handle_t *h;
2584	scf_transaction_t *tx;
2585	scf_transaction_entry_t *e_time, *e_stat;
2586	scf_value_t *v_time, *v_stat;
2587	scf_propertygroup_t *pg;
2588	int ret = 0, r;
2589	char pname[30];
2590	struct timeval tv;
2591	scf_error_t scfe;
2592
2593	if (strlen(name) + sizeof ("_method_waitstatus") > sizeof (pname))
2594		return (ENAMETOOLONG);
2595
2596	h = scf_instance_handle(s_inst);
2597
2598	pg = safe_scf_pg_create(h);
2599	tx = safe_scf_transaction_create(h);
2600	e_time = safe_scf_entry_create(h);
2601	v_time = safe_scf_value_create(h);
2602	e_stat = safe_scf_entry_create(h);
2603	v_stat = safe_scf_value_create(h);
2604
2605get_pg:
2606	ret = libscf_inst_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
2607	    SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
2608	switch (ret) {
2609	case 0:
2610		break;
2611
2612	case ECONNABORTED:
2613	case ECANCELED:
2614	case EPERM:
2615	case EACCES:
2616	case EROFS:
2617		goto out;
2618
2619	default:
2620		bad_error("libscf_inst_get_or_add_pg", ret);
2621	}
2622
2623	(void) gettimeofday(&tv, NULL);
2624
2625	r = scf_value_set_time(v_time, tv.tv_sec, tv.tv_usec * 1000);
2626	assert(r == 0);
2627
2628	scf_value_set_integer(v_stat, status);
2629
2630	for (;;) {
2631		if (scf_transaction_start(tx, pg) != 0) {
2632			switch (scf_error()) {
2633			case SCF_ERROR_CONNECTION_BROKEN:
2634			default:
2635				ret = ECONNABORTED;
2636				goto out;
2637
2638			case SCF_ERROR_DELETED:
2639				ret = ECANCELED;
2640				goto out;
2641
2642			case SCF_ERROR_PERMISSION_DENIED:
2643				ret = EPERM;
2644				goto out;
2645
2646			case SCF_ERROR_BACKEND_ACCESS:
2647				ret = EACCES;
2648				goto out;
2649
2650			case SCF_ERROR_BACKEND_READONLY:
2651				ret = EROFS;
2652				goto out;
2653
2654			case SCF_ERROR_NOT_SET:
2655				bad_error("scf_transaction_start", ret);
2656			}
2657		}
2658
2659		(void) snprintf(pname, sizeof (pname), "%s_method_timestamp",
2660		    name);
2661		ret = transaction_add_set(tx, e_time, pname, SCF_TYPE_TIME);
2662		switch (ret) {
2663		case 0:
2664			break;
2665
2666		case ECONNABORTED:
2667		case ECANCELED:
2668			goto out;
2669
2670		default:
2671			bad_error("transaction_add_set", ret);
2672		}
2673
2674		r = scf_entry_add_value(e_time, v_time);
2675		assert(r == 0);
2676
2677		(void) snprintf(pname, sizeof (pname), "%s_method_waitstatus",
2678		    name);
2679		ret = transaction_add_set(tx, e_stat, pname, SCF_TYPE_INTEGER);
2680		switch (ret) {
2681		case 0:
2682			break;
2683
2684		case ECONNABORTED:
2685		case ECANCELED:
2686			goto out;
2687
2688		default:
2689			bad_error("transaction_add_set", ret);
2690		}
2691
2692		r = scf_entry_add_value(e_stat, v_stat);
2693		if (r != 0)
2694			bad_error("scf_entry_add_value", scf_error());
2695
2696		r = scf_transaction_commit(tx);
2697		if (r == 1)
2698			break;
2699		if (r != 0) {
2700			scfe = scf_error();
2701			scf_transaction_reset_all(tx);
2702			switch (scfe) {
2703			case SCF_ERROR_CONNECTION_BROKEN:
2704			default:
2705				ret = ECONNABORTED;
2706				goto out;
2707
2708			case SCF_ERROR_DELETED:
2709				ret = ECANCELED;
2710				goto out;
2711
2712			case SCF_ERROR_PERMISSION_DENIED:
2713				ret = EPERM;
2714				goto out;
2715
2716			case SCF_ERROR_BACKEND_ACCESS:
2717				ret = EACCES;
2718				goto out;
2719
2720			case SCF_ERROR_BACKEND_READONLY:
2721				ret = EROFS;
2722				goto out;
2723
2724			case SCF_ERROR_NOT_SET:
2725				bad_error("scf_transaction_commit", scfe);
2726			}
2727		}
2728
2729		scf_transaction_reset_all(tx);
2730
2731		if (scf_pg_update(pg) == -1) {
2732			switch (scf_error()) {
2733			case SCF_ERROR_CONNECTION_BROKEN:
2734			default:
2735				ret = ECONNABORTED;
2736				goto out;
2737
2738			case SCF_ERROR_DELETED:
2739				ret = ECANCELED;
2740				goto out;
2741
2742			case SCF_ERROR_NOT_SET:
2743				bad_error("scf_pg_update", scf_error());
2744			}
2745		}
2746	}
2747
2748out:
2749	scf_transaction_destroy(tx);
2750	scf_entry_destroy(e_time);
2751	scf_value_destroy(v_time);
2752	scf_entry_destroy(e_stat);
2753	scf_value_destroy(v_stat);
2754	scf_pg_destroy(pg);
2755
2756	return (ret);
2757}
2758
2759/*
2760 * Call dgraph_add_instance() for each instance in the repository.
2761 */
2762void
2763libscf_populate_graph(scf_handle_t *h)
2764{
2765	scf_scope_t *scope;
2766	scf_service_t *svc;
2767	scf_instance_t *inst;
2768	scf_iter_t *svc_iter;
2769	scf_iter_t *inst_iter;
2770	int ret;
2771
2772	scope = safe_scf_scope_create(h);
2773	svc = safe_scf_service_create(h);
2774	inst = safe_scf_instance_create(h);
2775	svc_iter = safe_scf_iter_create(h);
2776	inst_iter = safe_scf_iter_create(h);
2777
2778	if ((ret = scf_handle_get_local_scope(h, scope)) !=
2779	    SCF_SUCCESS)
2780		uu_die("retrieving local scope failed: %d\n", ret);
2781
2782	if (scf_iter_scope_services(svc_iter, scope) == -1)
2783		uu_die("walking local scope's services failed\n");
2784
2785	while (scf_iter_next_service(svc_iter, svc) > 0) {
2786		if (scf_iter_service_instances(inst_iter, svc) == -1)
2787			uu_die("unable to walk service's instances");
2788
2789		while (scf_iter_next_instance(inst_iter, inst) > 0) {
2790			char *fmri;
2791
2792			if (libscf_instance_get_fmri(inst, &fmri) == 0) {
2793				int err;
2794
2795				err = dgraph_add_instance(fmri, inst, B_TRUE);
2796				if (err != 0 && err != EEXIST)
2797					log_error(LOG_WARNING,
2798					    "Failed to add %s (%s).\n", fmri,
2799					    strerror(err));
2800				startd_free(fmri, max_scf_fmri_size);
2801			}
2802		}
2803	}
2804
2805	scf_iter_destroy(inst_iter);
2806	scf_iter_destroy(svc_iter);
2807	scf_instance_destroy(inst);
2808	scf_service_destroy(svc);
2809	scf_scope_destroy(scope);
2810}
2811
2812/*
2813 * Monitors get handled differently since there can be multiple of them.
2814 *
2815 * Returns exec string on success.  If method not defined, returns
2816 * LIBSCF_PGROUP_ABSENT; if exec property missing, returns
2817 * LIBSCF_PROPERTY_ABSENT.  Returns LIBSCF_PROPERTY_ERROR on other failures.
2818 */
2819char *
2820libscf_get_method(scf_handle_t *h, int type, restarter_inst_t *inst,
2821    scf_snapshot_t *snap, method_restart_t *restart_on, uint_t *cte_mask,
2822    uint8_t *need_sessionp, uint64_t *timeout, uint8_t *timeout_retry)
2823{
2824	scf_instance_t *scf_inst = NULL;
2825	scf_propertygroup_t *pg = NULL, *pg_startd = NULL;
2826	scf_property_t *prop = NULL;
2827	const char *name;
2828	char *method = startd_alloc(max_scf_value_size);
2829	char *ig = startd_alloc(max_scf_value_size);
2830	char *restart = startd_alloc(max_scf_value_size);
2831	char *ret;
2832	int error = 0, r;
2833
2834	scf_inst = safe_scf_instance_create(h);
2835	pg = safe_scf_pg_create(h);
2836	pg_startd = safe_scf_pg_create(h);
2837	prop = safe_scf_property_create(h);
2838
2839	ret = NULL;
2840
2841	*restart_on = METHOD_RESTART_UNKNOWN;
2842
2843	switch (type) {
2844	case METHOD_START:
2845		name = "start";
2846		break;
2847	case METHOD_STOP:
2848		name = "stop";
2849		break;
2850	case METHOD_REFRESH:
2851		name = "refresh";
2852		break;
2853	default:
2854		error = LIBSCF_PROPERTY_ERROR;
2855		goto get_method_cleanup;
2856	}
2857
2858	if (scf_handle_decode_fmri(h, inst->ri_i.i_fmri, NULL, NULL, scf_inst,
2859	    NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
2860		log_error(LOG_WARNING,
2861		    "%s: get_method decode instance FMRI failed: %s\n",
2862		    inst->ri_i.i_fmri, scf_strerror(scf_error()));
2863		error = LIBSCF_PROPERTY_ERROR;
2864		goto get_method_cleanup;
2865	}
2866
2867	if (scf_instance_get_pg_composed(scf_inst, snap, name, pg) == -1) {
2868		if (scf_error() == SCF_ERROR_NOT_FOUND)
2869			error = LIBSCF_PGROUP_ABSENT;
2870		else
2871			error = LIBSCF_PROPERTY_ERROR;
2872		goto get_method_cleanup;
2873	}
2874
2875	if (scf_pg_get_property(pg, SCF_PROPERTY_EXEC, prop) == -1) {
2876		if (scf_error() == SCF_ERROR_NOT_FOUND)
2877			error = LIBSCF_PROPERTY_ABSENT;
2878		else
2879			error = LIBSCF_PROPERTY_ERROR;
2880		goto get_method_cleanup;
2881	}
2882
2883	error = libscf_read_single_astring(h, prop, &method);
2884	if (error != 0) {
2885		log_error(LOG_WARNING,
2886		    "%s: get_method failed: can't get a single astring "
2887		    "from %s/%s\n", inst->ri_i.i_fmri, name, SCF_PROPERTY_EXEC);
2888		goto get_method_cleanup;
2889	}
2890
2891	error = expand_method_tokens(method, scf_inst, snap, type, &ret);
2892	if (error != 0) {
2893		log_instance(inst, B_TRUE, "Could not expand method tokens "
2894		    "in \"%s\": %s", method, ret);
2895		error = LIBSCF_PROPERTY_ERROR;
2896		goto get_method_cleanup;
2897	}
2898
2899	r = get_count(pg, SCF_PROPERTY_TIMEOUT, timeout);
2900	switch (r) {
2901	case 0:
2902		break;
2903
2904	case ECONNABORTED:
2905		error = LIBSCF_PROPERTY_ERROR;
2906		goto get_method_cleanup;
2907
2908	case EINVAL:
2909		log_instance(inst, B_TRUE, "%s/%s is multi-valued or not of "
2910		    "type count.  Using infinite timeout.", name,
2911		    SCF_PROPERTY_TIMEOUT);
2912		/* FALLTHROUGH */
2913	case ECANCELED:
2914	case ENOENT:
2915		*timeout = METHOD_TIMEOUT_INFINITE;
2916		break;
2917
2918	case EACCES:
2919	default:
2920		bad_error("get_count", r);
2921	}
2922
2923	/* Both 0 and -1 (ugh) are considered infinite timeouts. */
2924	if (*timeout == -1 || *timeout == 0)
2925		*timeout = METHOD_TIMEOUT_INFINITE;
2926
2927	if (scf_instance_get_pg_composed(scf_inst, snap, SCF_PG_STARTD,
2928	    pg_startd) == -1) {
2929		switch (scf_error()) {
2930		case SCF_ERROR_CONNECTION_BROKEN:
2931		case SCF_ERROR_DELETED:
2932			error = LIBSCF_PROPERTY_ERROR;
2933			goto get_method_cleanup;
2934
2935		case SCF_ERROR_NOT_FOUND:
2936			*cte_mask = 0;
2937			break;
2938
2939		case SCF_ERROR_INVALID_ARGUMENT:
2940		case SCF_ERROR_HANDLE_MISMATCH:
2941		case SCF_ERROR_NOT_BOUND:
2942		case SCF_ERROR_NOT_SET:
2943			bad_error("scf_instance_get_pg_composed", scf_error());
2944		}
2945	} else {
2946		if (scf_pg_get_property(pg_startd, SCF_PROPERTY_IGNORE,
2947		    prop) == -1) {
2948			if (scf_error() == SCF_ERROR_NOT_FOUND)
2949				*cte_mask = 0;
2950			else {
2951				error = LIBSCF_PROPERTY_ERROR;
2952				goto get_method_cleanup;
2953			}
2954		} else {
2955			error = libscf_read_single_astring(h, prop, &ig);
2956			if (error != 0) {
2957				log_error(LOG_WARNING,
2958				    "%s: get_method failed: can't get a single "
2959				    "astring from %s/%s\n", inst->ri_i.i_fmri,
2960				    name, SCF_PROPERTY_IGNORE);
2961				goto get_method_cleanup;
2962			}
2963
2964			if (strcmp(ig, "core") == 0)
2965				*cte_mask = CT_PR_EV_CORE;
2966			else if (strcmp(ig, "signal") == 0)
2967				*cte_mask = CT_PR_EV_SIGNAL;
2968			else if (strcmp(ig, "core,signal") == 0 ||
2969			    strcmp(ig, "signal,core") == 0)
2970				*cte_mask = CT_PR_EV_CORE | CT_PR_EV_SIGNAL;
2971			else
2972				*cte_mask = 0;
2973		}
2974
2975		r = get_boolean(pg_startd, SCF_PROPERTY_NEED_SESSION,
2976		    need_sessionp);
2977		switch (r) {
2978		case 0:
2979			break;
2980
2981		case ECONNABORTED:
2982			error = LIBSCF_PROPERTY_ERROR;
2983			goto get_method_cleanup;
2984
2985		case ECANCELED:
2986		case ENOENT:
2987		case EINVAL:
2988			*need_sessionp = 0;
2989			break;
2990
2991		case EACCES:
2992		default:
2993			bad_error("get_boolean", r);
2994		}
2995
2996		/*
2997		 * Determine whether service has overriden retry after
2998		 * method timeout.  Default to retry if no value is
2999		 * specified.
3000		 */
3001		r = get_boolean(pg_startd, SCF_PROPERTY_TIMEOUT_RETRY,
3002		    timeout_retry);
3003		switch (r) {
3004		case 0:
3005			break;
3006
3007		case ECONNABORTED:
3008			error = LIBSCF_PROPERTY_ERROR;
3009			goto get_method_cleanup;
3010
3011		case ECANCELED:
3012		case ENOENT:
3013		case EINVAL:
3014			*timeout_retry = 1;
3015			break;
3016
3017		case EACCES:
3018		default:
3019			bad_error("get_boolean", r);
3020		}
3021	}
3022
3023	if (type != METHOD_START)
3024		goto get_method_cleanup;
3025
3026	/* Only start methods need to honor the restart_on property. */
3027
3028	if (scf_pg_get_property(pg, SCF_PROPERTY_RESTART_ON, prop) == -1) {
3029		if (scf_error() == SCF_ERROR_NOT_FOUND)
3030			*restart_on = METHOD_RESTART_ALL;
3031		else
3032			error = LIBSCF_PROPERTY_ERROR;
3033		goto get_method_cleanup;
3034	}
3035
3036	error = libscf_read_single_astring(h, prop, &restart);
3037	if (error != 0) {
3038		log_error(LOG_WARNING,
3039		    "%s: get_method failed: can't get a single astring "
3040		    "from %s/%s\n", inst->ri_i.i_fmri, name,
3041		    SCF_PROPERTY_RESTART_ON);
3042		goto get_method_cleanup;
3043	}
3044
3045	if (strcmp(restart, "all") == 0)
3046		*restart_on = METHOD_RESTART_ALL;
3047	else if (strcmp(restart, "external_fault") == 0)
3048		*restart_on = METHOD_RESTART_EXTERNAL_FAULT;
3049	else if (strcmp(restart, "any_fault") == 0)
3050		*restart_on = METHOD_RESTART_ANY_FAULT;
3051
3052get_method_cleanup:
3053	startd_free(ig, max_scf_value_size);
3054	startd_free(method, max_scf_value_size);
3055	startd_free(restart, max_scf_value_size);
3056
3057	scf_instance_destroy(scf_inst);
3058	scf_pg_destroy(pg);
3059	scf_pg_destroy(pg_startd);
3060	scf_property_destroy(prop);
3061
3062	if (error != 0 && ret != NULL) {
3063		free(ret);
3064		ret = NULL;
3065	}
3066
3067	errno = error;
3068	return (ret);
3069}
3070
3071/*
3072 * Returns 1 if we've reached the fault threshold
3073 */
3074int
3075update_fault_count(restarter_inst_t *inst, int type)
3076{
3077	assert(type == FAULT_COUNT_INCR || type == FAULT_COUNT_RESET);
3078
3079	if (type == FAULT_COUNT_INCR) {
3080		inst->ri_i.i_fault_count++;
3081		log_framework(LOG_INFO, "%s: Increasing fault count to %d\n",
3082		    inst->ri_i.i_fmri, inst->ri_i.i_fault_count);
3083	}
3084	if (type == FAULT_COUNT_RESET)
3085		inst->ri_i.i_fault_count = 0;
3086
3087	if (inst->ri_i.i_fault_count >= FAULT_THRESHOLD)
3088		return (1);
3089
3090	return (0);
3091}
3092
3093/*
3094 * int libscf_unset_action()
3095 *   Delete any pending timestamps for the specified action which is
3096 *   older than the supplied ts.
3097 *
3098 *   Returns 0 on success, ECONNABORTED, EACCES, or EPERM on failure.
3099 */
3100int
3101libscf_unset_action(scf_handle_t *h, scf_propertygroup_t *pg,
3102    admin_action_t a, hrtime_t ts)
3103{
3104	scf_transaction_t *t;
3105	scf_transaction_entry_t *e;
3106	scf_property_t *prop;
3107	scf_value_t *val;
3108	hrtime_t rep_ts;
3109	int ret = 0, r;
3110
3111	t = safe_scf_transaction_create(h);
3112	e = safe_scf_entry_create(h);
3113	prop = safe_scf_property_create(h);
3114	val = safe_scf_value_create(h);
3115
3116	for (;;) {
3117		if (scf_pg_update(pg) == -1) {
3118			switch (scf_error()) {
3119			case SCF_ERROR_CONNECTION_BROKEN:
3120			default:
3121				ret = ECONNABORTED;
3122				goto unset_action_cleanup;
3123
3124			case SCF_ERROR_DELETED:
3125				goto unset_action_cleanup;
3126
3127			case SCF_ERROR_NOT_SET:
3128				assert(0);
3129				abort();
3130			}
3131		}
3132
3133		if (scf_transaction_start(t, pg) == -1) {
3134			switch (scf_error()) {
3135			case SCF_ERROR_CONNECTION_BROKEN:
3136			default:
3137				ret = ECONNABORTED;
3138				goto unset_action_cleanup;
3139
3140			case SCF_ERROR_DELETED:
3141				goto unset_action_cleanup;
3142
3143			case SCF_ERROR_PERMISSION_DENIED:
3144				ret = EPERM;
3145				goto unset_action_cleanup;
3146
3147			case SCF_ERROR_BACKEND_ACCESS:
3148			case SCF_ERROR_BACKEND_READONLY:
3149				ret = EACCES;
3150				goto unset_action_cleanup;
3151
3152			case SCF_ERROR_IN_USE:
3153			case SCF_ERROR_HANDLE_MISMATCH:
3154			case SCF_ERROR_NOT_SET:
3155				assert(0);
3156				abort();
3157			}
3158		}
3159
3160		/* Return failure only if the property hasn't been deleted. */
3161		if (scf_pg_get_property(pg, admin_actions[a], prop) == -1) {
3162			switch (scf_error()) {
3163			case SCF_ERROR_CONNECTION_BROKEN:
3164			default:
3165				ret = ECONNABORTED;
3166				goto unset_action_cleanup;
3167
3168			case SCF_ERROR_DELETED:
3169			case SCF_ERROR_NOT_FOUND:
3170				goto unset_action_cleanup;
3171
3172			case SCF_ERROR_HANDLE_MISMATCH:
3173			case SCF_ERROR_INVALID_ARGUMENT:
3174			case SCF_ERROR_NOT_SET:
3175				assert(0);
3176				abort();
3177			}
3178		}
3179
3180		if (scf_property_get_value(prop, val) == -1) {
3181			switch (scf_error()) {
3182			case SCF_ERROR_CONNECTION_BROKEN:
3183			default:
3184				ret = ECONNABORTED;
3185				goto unset_action_cleanup;
3186
3187			case SCF_ERROR_DELETED:
3188			case SCF_ERROR_NOT_FOUND:
3189				goto unset_action_cleanup;
3190
3191			case SCF_ERROR_CONSTRAINT_VIOLATED:
3192				/*
3193				 * More than one value was associated with
3194				 * this property -- this is incorrect. Take
3195				 * the opportunity to clean up and clear the
3196				 * entire property.
3197				 */
3198				rep_ts = ts;
3199				break;
3200
3201			case SCF_ERROR_PERMISSION_DENIED:
3202			case SCF_ERROR_NOT_SET:
3203				assert(0);
3204				abort();
3205			}
3206		} else if (scf_value_get_integer(val, &rep_ts) == -1) {
3207			assert(scf_error() == SCF_ERROR_TYPE_MISMATCH);
3208			rep_ts = 0;
3209		}
3210
3211		/* Repository ts is more current. Don't clear the action. */
3212		if (rep_ts > ts)
3213			goto unset_action_cleanup;
3214
3215		r = scf_transaction_property_change_type(t, e,
3216		    admin_actions[a], SCF_TYPE_INTEGER);
3217		assert(r == 0);
3218
3219		r = scf_transaction_commit(t);
3220		if (r == 1)
3221			break;
3222
3223		if (r != 0) {
3224			switch (scf_error()) {
3225			case SCF_ERROR_CONNECTION_BROKEN:
3226			default:
3227				ret = ECONNABORTED;
3228				goto unset_action_cleanup;
3229
3230			case SCF_ERROR_DELETED:
3231				break;
3232
3233			case SCF_ERROR_PERMISSION_DENIED:
3234				ret = EPERM;
3235				goto unset_action_cleanup;
3236
3237			case SCF_ERROR_BACKEND_ACCESS:
3238			case SCF_ERROR_BACKEND_READONLY:
3239				ret = EACCES;
3240				goto unset_action_cleanup;
3241
3242			case SCF_ERROR_INVALID_ARGUMENT:
3243			case SCF_ERROR_NOT_SET:
3244				assert(0);
3245				abort();
3246			}
3247		}
3248
3249		scf_transaction_reset(t);
3250	}
3251
3252unset_action_cleanup:
3253	scf_transaction_destroy(t);
3254	scf_entry_destroy(e);
3255	scf_property_destroy(prop);
3256	scf_value_destroy(val);
3257
3258	return (ret);
3259}
3260
3261/*
3262 * Decorates & binds hndl.  hndl must be unbound.  Returns
3263 *   0 - success
3264 *   -1 - repository server is not running
3265 *   -1 - repository server is out of resources
3266 */
3267static int
3268handle_decorate_and_bind(scf_handle_t *hndl)
3269{
3270	scf_value_t *door_dec_value;
3271
3272	door_dec_value = safe_scf_value_create(hndl);
3273
3274	/*
3275	 * Decorate if alternate door path set.
3276	 */
3277	if (st->st_door_path) {
3278		if (scf_value_set_astring(door_dec_value, st->st_door_path) !=
3279		    0)
3280			uu_die("$STARTD_ALT_DOOR is too long.\n");
3281
3282		if (scf_handle_decorate(hndl, "door_path", door_dec_value) != 0)
3283			bad_error("scf_handle_decorate", scf_error());
3284	}
3285
3286	scf_value_destroy(door_dec_value);
3287
3288	if (scf_handle_bind(hndl) == 0)
3289		return (0);
3290
3291	switch (scf_error()) {
3292	case SCF_ERROR_NO_SERVER:
3293	case SCF_ERROR_NO_RESOURCES:
3294		return (-1);
3295
3296	case SCF_ERROR_INVALID_ARGUMENT:
3297	case SCF_ERROR_IN_USE:
3298	default:
3299		bad_error("scf_handle_bind", scf_error());
3300		/* NOTREACHED */
3301	}
3302}
3303
3304scf_handle_t *
3305libscf_handle_create_bound(scf_version_t v)
3306{
3307	scf_handle_t *hndl = scf_handle_create(v);
3308
3309	if (hndl == NULL)
3310		return (hndl);
3311
3312	if (handle_decorate_and_bind(hndl) == 0)
3313		return (hndl);
3314
3315	scf_handle_destroy(hndl);
3316	return (NULL);
3317}
3318
3319void
3320libscf_handle_rebind(scf_handle_t *h)
3321{
3322	(void) scf_handle_unbind(h);
3323
3324	MUTEX_LOCK(&st->st_configd_live_lock);
3325
3326	/*
3327	 * Try to rebind the handle before sleeping in case the server isn't
3328	 * really dead.
3329	 */
3330	while (handle_decorate_and_bind(h) != 0)
3331		(void) pthread_cond_wait(&st->st_configd_live_cv,
3332		    &st->st_configd_live_lock);
3333
3334	MUTEX_UNLOCK(&st->st_configd_live_lock);
3335}
3336
3337/*
3338 * Create a handle and try to bind it until it succeeds.  Always returns
3339 * a bound handle.
3340 */
3341scf_handle_t *
3342libscf_handle_create_bound_loop()
3343{
3344	scf_handle_t *h;
3345
3346	while ((h = scf_handle_create(SCF_VERSION)) == NULL) {
3347		/* This should have been caught earlier. */
3348		assert(scf_error() != SCF_ERROR_VERSION_MISMATCH);
3349		(void) sleep(2);
3350	}
3351
3352	if (handle_decorate_and_bind(h) != 0)
3353		libscf_handle_rebind(h);
3354
3355	return (h);
3356}
3357
3358/*
3359 * Call cb for each dependency property group of inst.  cb is invoked with
3360 * a pointer to the scf_propertygroup_t and arg.  If the repository connection
3361 * is broken, returns ECONNABORTED.  If inst is deleted, returns ECANCELED.
3362 * If cb returns non-zero, the walk is stopped and EINTR is returned.
3363 * Otherwise returns 0.
3364 */
3365int
3366walk_dependency_pgs(scf_instance_t *inst, callback_t cb, void *arg)
3367{
3368	scf_handle_t *h;
3369	scf_snapshot_t *snap;
3370	scf_iter_t *iter;
3371	scf_propertygroup_t *pg;
3372	int r;
3373
3374	h = scf_instance_handle(inst);
3375
3376	iter = safe_scf_iter_create(h);
3377	pg = safe_scf_pg_create(h);
3378
3379	snap = libscf_get_running_snapshot(inst);
3380
3381	if (scf_iter_instance_pgs_typed_composed(iter, inst, snap,
3382	    SCF_GROUP_DEPENDENCY) != 0) {
3383		scf_snapshot_destroy(snap);
3384		scf_pg_destroy(pg);
3385		scf_iter_destroy(iter);
3386		switch (scf_error()) {
3387		case SCF_ERROR_CONNECTION_BROKEN:
3388		default:
3389			return (ECONNABORTED);
3390
3391		case SCF_ERROR_DELETED:
3392			return (ECANCELED);
3393
3394		case SCF_ERROR_HANDLE_MISMATCH:
3395		case SCF_ERROR_INVALID_ARGUMENT:
3396		case SCF_ERROR_NOT_SET:
3397			assert(0);
3398			abort();
3399		}
3400	}
3401
3402	for (;;) {
3403		r = scf_iter_next_pg(iter, pg);
3404		if (r == 0)
3405			break;
3406		if (r == -1) {
3407			scf_snapshot_destroy(snap);
3408			scf_pg_destroy(pg);
3409			scf_iter_destroy(iter);
3410
3411			switch (scf_error()) {
3412			case SCF_ERROR_CONNECTION_BROKEN:
3413				return (ECONNABORTED);
3414
3415			case SCF_ERROR_DELETED:
3416				return (ECANCELED);
3417
3418			case SCF_ERROR_NOT_SET:
3419			case SCF_ERROR_INVALID_ARGUMENT:
3420			case SCF_ERROR_NOT_BOUND:
3421			case SCF_ERROR_HANDLE_MISMATCH:
3422			default:
3423				bad_error("scf_iter_next_pg", scf_error());
3424			}
3425		}
3426
3427		r = cb(pg, arg);
3428
3429		if (r != 0)
3430			break;
3431	}
3432
3433	scf_snapshot_destroy(snap);
3434	scf_pg_destroy(pg);
3435	scf_iter_destroy(iter);
3436
3437	return (r == 0 ? 0 : EINTR);
3438}
3439
3440/*
3441 * Call cb for each of the string values of prop.  cb is invoked with
3442 * a pointer to the string and arg.  If the connection to the repository is
3443 * broken, ECONNABORTED is returned.  If the property is deleted, ECANCELED is
3444 * returned.  If the property does not have astring type, EINVAL is returned.
3445 * If cb returns non-zero, the walk is stopped and EINTR is returned.
3446 * Otherwise 0 is returned.
3447 */
3448int
3449walk_property_astrings(scf_property_t *prop, callback_t cb, void *arg)
3450{
3451	scf_handle_t *h;
3452	scf_value_t *val;
3453	scf_iter_t *iter;
3454	char *buf;
3455	int r;
3456	ssize_t sz;
3457
3458	if (scf_property_is_type(prop, SCF_TYPE_ASTRING) != 0) {
3459		switch (scf_error()) {
3460		case SCF_ERROR_CONNECTION_BROKEN:
3461		default:
3462			return (ECONNABORTED);
3463
3464		case SCF_ERROR_DELETED:
3465			return (ECANCELED);
3466
3467		case SCF_ERROR_TYPE_MISMATCH:
3468			return (EINVAL);
3469
3470		case SCF_ERROR_NOT_SET:
3471			assert(0);
3472			abort();
3473		}
3474	}
3475
3476	h = scf_property_handle(prop);
3477
3478	val = safe_scf_value_create(h);
3479	iter = safe_scf_iter_create(h);
3480
3481	if (scf_iter_property_values(iter, prop) != 0) {
3482		scf_iter_destroy(iter);
3483		scf_value_destroy(val);
3484		switch (scf_error()) {
3485		case SCF_ERROR_CONNECTION_BROKEN:
3486		default:
3487			return (ECONNABORTED);
3488
3489		case SCF_ERROR_DELETED:
3490			return (ECANCELED);
3491
3492		case SCF_ERROR_HANDLE_MISMATCH:
3493		case SCF_ERROR_NOT_SET:
3494			assert(0);
3495			abort();
3496		}
3497	}
3498
3499	buf = startd_alloc(max_scf_value_size);
3500
3501	for (;;) {
3502		r = scf_iter_next_value(iter, val);
3503		if (r < 0) {
3504			startd_free(buf, max_scf_value_size);
3505			scf_iter_destroy(iter);
3506			scf_value_destroy(val);
3507
3508			switch (scf_error()) {
3509			case SCF_ERROR_CONNECTION_BROKEN:
3510				return (ECONNABORTED);
3511
3512			case SCF_ERROR_DELETED:
3513				return (ECANCELED);
3514
3515			case SCF_ERROR_NOT_SET:
3516			case SCF_ERROR_INVALID_ARGUMENT:
3517			case SCF_ERROR_NOT_BOUND:
3518			case SCF_ERROR_HANDLE_MISMATCH:
3519			case SCF_ERROR_PERMISSION_DENIED:
3520			default:
3521				bad_error("scf_iter_next_value", scf_error());
3522			}
3523		}
3524		if (r == 0)
3525			break;
3526
3527		sz = scf_value_get_astring(val, buf, max_scf_value_size);
3528		assert(sz >= 0);
3529
3530		r = cb(buf, arg);
3531
3532		if (r != 0)
3533			break;
3534	}
3535
3536	startd_free(buf, max_scf_value_size);
3537	scf_value_destroy(val);
3538	scf_iter_destroy(iter);
3539
3540	return (r == 0 ? 0 : EINTR);
3541}
3542
3543/*
3544 * Returns 0 or ECONNABORTED.
3545 */
3546int
3547libscf_create_self(scf_handle_t *h)
3548{
3549	scf_scope_t *scope;
3550	scf_service_t *svc;
3551	scf_instance_t *inst;
3552	instance_data_t idata;
3553	int ret = 0, r;
3554	ctid_t ctid;
3555	uint64_t uint64;
3556	uint_t count = 0, msecs = ALLOC_DELAY;
3557
3558	const char * const startd_svc = "system/svc/restarter";
3559	const char * const startd_inst = "default";
3560
3561	/* If SCF_SERVICE_STARTD changes, our strings must change, too. */
3562	assert(strcmp(SCF_SERVICE_STARTD,
3563	    "svc:/system/svc/restarter:default") == 0);
3564
3565	scope = safe_scf_scope_create(h);
3566	svc = safe_scf_service_create(h);
3567	inst = safe_scf_instance_create(h);
3568
3569	if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, scope) != 0) {
3570		assert(scf_error() == SCF_ERROR_CONNECTION_BROKEN);
3571		ret = ECONNABORTED;
3572		goto out;
3573	}
3574
3575get_svc:
3576	if (scf_scope_get_service(scope, startd_svc, svc) != 0) {
3577		switch (scf_error()) {
3578		case SCF_ERROR_CONNECTION_BROKEN:
3579		case SCF_ERROR_DELETED:
3580		default:
3581			ret = ECONNABORTED;
3582			goto out;
3583
3584		case SCF_ERROR_NOT_FOUND:
3585			break;
3586
3587		case SCF_ERROR_HANDLE_MISMATCH:
3588		case SCF_ERROR_INVALID_ARGUMENT:
3589		case SCF_ERROR_NOT_SET:
3590			bad_error("scf_scope_get_service", scf_error());
3591		}
3592
3593add_svc:
3594		if (scf_scope_add_service(scope, startd_svc, svc) != 0) {
3595			switch (scf_error()) {
3596			case SCF_ERROR_CONNECTION_BROKEN:
3597			case SCF_ERROR_DELETED:
3598			default:
3599				ret = ECONNABORTED;
3600				goto out;
3601
3602			case SCF_ERROR_EXISTS:
3603				goto get_svc;
3604
3605			case SCF_ERROR_PERMISSION_DENIED:
3606			case SCF_ERROR_BACKEND_ACCESS:
3607			case SCF_ERROR_BACKEND_READONLY:
3608				uu_warn("Could not create %s: %s\n",
3609				    SCF_SERVICE_STARTD,
3610				    scf_strerror(scf_error()));
3611				goto out;
3612
3613			case SCF_ERROR_HANDLE_MISMATCH:
3614			case SCF_ERROR_INVALID_ARGUMENT:
3615			case SCF_ERROR_NOT_SET:
3616				bad_error("scf_scope_add_service", scf_error());
3617			}
3618		}
3619	}
3620
3621	if (scf_service_get_instance(svc, startd_inst, NULL) == 0)
3622		goto out;
3623
3624	switch (scf_error()) {
3625	case SCF_ERROR_CONNECTION_BROKEN:
3626	default:
3627		ret = ECONNABORTED;
3628		goto out;
3629
3630	case SCF_ERROR_NOT_FOUND:
3631		break;
3632
3633	case SCF_ERROR_DELETED:
3634		goto add_svc;
3635
3636	case SCF_ERROR_HANDLE_MISMATCH:
3637	case SCF_ERROR_INVALID_ARGUMENT:
3638	case SCF_ERROR_NOT_SET:
3639		bad_error("scf_service_get_instance", scf_error());
3640	}
3641
3642add_inst:
3643	if (scf_service_add_instance(svc, startd_inst, inst) != 0) {
3644		switch (scf_error()) {
3645		case SCF_ERROR_CONNECTION_BROKEN:
3646		default:
3647			ret = ECONNABORTED;
3648			goto out;
3649
3650		case SCF_ERROR_EXISTS:
3651			break;
3652
3653		case SCF_ERROR_PERMISSION_DENIED:
3654		case SCF_ERROR_BACKEND_ACCESS:
3655			uu_die("Could not create %s: %s\n", SCF_SERVICE_STARTD,
3656			    scf_strerror(scf_error()));
3657			/* NOTREACHED */
3658
3659		case SCF_ERROR_BACKEND_READONLY:
3660			log_error(LOG_NOTICE,
3661			    "Could not create %s: backend readonly.\n",
3662			    SCF_SERVICE_STARTD);
3663			goto out;
3664
3665		case SCF_ERROR_DELETED:
3666			goto add_svc;
3667
3668		case SCF_ERROR_HANDLE_MISMATCH:
3669		case SCF_ERROR_INVALID_ARGUMENT:
3670		case SCF_ERROR_NOT_SET:
3671			bad_error("scf_service_add_instance", scf_error());
3672		}
3673	}
3674
3675	/* Set start time. */
3676	idata.i_fmri = SCF_SERVICE_STARTD;
3677	idata.i_state = RESTARTER_STATE_NONE;
3678	idata.i_next_state = RESTARTER_STATE_NONE;
3679set_state:
3680	switch (r = _restarter_commit_states(h, &idata, RESTARTER_STATE_ONLINE,
3681	    RESTARTER_STATE_NONE, NULL)) {
3682	case 0:
3683		break;
3684
3685	case ENOMEM:
3686		++count;
3687		if (count < ALLOC_RETRY) {
3688			(void) poll(NULL, 0, msecs);
3689			msecs *= ALLOC_DELAY_MULT;
3690			goto set_state;
3691		}
3692
3693		uu_die("Insufficient memory.\n");
3694		/* NOTREACHED */
3695
3696	case ECONNABORTED:
3697		ret = ECONNABORTED;
3698		goto out;
3699
3700	case ENOENT:
3701		goto add_inst;
3702
3703	case EPERM:
3704	case EACCES:
3705	case EROFS:
3706		uu_warn("Could not timestamp %s: %s\n", idata.i_fmri,
3707		    strerror(r));
3708		break;
3709
3710	case EINVAL:
3711	default:
3712		bad_error("_restarter_commit_states", r);
3713	}
3714
3715	/* Set general/enabled. */
3716	ret = libscf_inst_set_boolean_prop(inst, SCF_PG_GENERAL,
3717	    SCF_PG_GENERAL_TYPE, SCF_PG_GENERAL_FLAGS, SCF_PROPERTY_ENABLED, 1);
3718	switch (ret) {
3719	case 0:
3720	case ECONNABORTED:
3721	case EPERM:
3722	case EACCES:
3723	case EROFS:
3724		break;
3725
3726	case ECANCELED:
3727		goto add_inst;
3728
3729	default:
3730		bad_error("libscf_inst_set_boolean_prop", ret);
3731	}
3732
3733	ret = libscf_write_start_pid(inst, getpid());
3734	switch (ret) {
3735	case 0:
3736	case ECONNABORTED:
3737	case EPERM:
3738	case EACCES:
3739	case EROFS:
3740		break;
3741
3742	case ECANCELED:
3743		goto add_inst;
3744
3745	default:
3746		bad_error("libscf_write_start_pid", ret);
3747	}
3748
3749	ctid = proc_get_ctid();
3750	if (ctid > 0) {
3751
3752		uint64 = (uint64_t)ctid;
3753		ret = libscf_inst_set_count_prop(inst,
3754		    SCF_PG_RESTARTER, SCF_PG_RESTARTER_TYPE,
3755		    SCF_PG_RESTARTER_FLAGS, SCF_PROPERTY_CONTRACT, uint64);
3756
3757		switch (ret) {
3758		case 0:
3759		case ECONNABORTED:
3760		case EPERM:
3761		case EACCES:
3762		case EROFS:
3763			break;
3764
3765		case ECANCELED:
3766			goto add_inst;
3767
3768		default:
3769			bad_error("libscf_inst_set_count_prop", ret);
3770		}
3771	}
3772
3773	ret = libscf_note_method_log(inst, LOG_PREFIX_EARLY,
3774	    STARTD_DEFAULT_LOG);
3775	if (ret == 0) {
3776		ret = libscf_note_method_log(inst, LOG_PREFIX_NORMAL,
3777		    STARTD_DEFAULT_LOG);
3778	}
3779
3780	switch (ret) {
3781		case ECONNABORTED:
3782		case EPERM:
3783		case EACCES:
3784		case EROFS:
3785		case EAGAIN:
3786			break;
3787
3788		case ECANCELED:
3789			goto add_inst;
3790
3791		default:
3792			bad_error("libscf_note_method_log", ret);
3793	}
3794
3795out:
3796	scf_instance_destroy(inst);
3797	scf_service_destroy(svc);
3798	scf_scope_destroy(scope);
3799	return (ret);
3800}
3801
3802/*
3803 * Returns
3804 *   0 - success
3805 *   ENOENT - SCF_SERVICE_STARTD does not exist in repository
3806 *   EPERM
3807 *   EACCES
3808 *   EROFS
3809 */
3810int
3811libscf_set_reconfig(int set)
3812{
3813	scf_handle_t *h;
3814	scf_instance_t *inst;
3815	scf_propertygroup_t *pg;
3816	int ret = 0;
3817
3818	h = libscf_handle_create_bound_loop();
3819	inst = safe_scf_instance_create(h);
3820	pg = safe_scf_pg_create(h);
3821
3822again:
3823	if (scf_handle_decode_fmri(h, SCF_SERVICE_STARTD, NULL, NULL,
3824	    inst, NULL, NULL,  SCF_DECODE_FMRI_EXACT) == -1) {
3825		switch (scf_error()) {
3826		case SCF_ERROR_CONNECTION_BROKEN:
3827		default:
3828			libscf_handle_rebind(h);
3829			goto again;
3830
3831		case SCF_ERROR_NOT_FOUND:
3832			ret = ENOENT;
3833			goto reconfig_out;
3834
3835		case SCF_ERROR_HANDLE_MISMATCH:
3836		case SCF_ERROR_INVALID_ARGUMENT:
3837		case SCF_ERROR_CONSTRAINT_VIOLATED:
3838			bad_error("scf_handle_decode_fmri", scf_error());
3839		}
3840	}
3841
3842	ret = libscf_inst_set_boolean_prop(inst, "system", SCF_GROUP_FRAMEWORK,
3843	    SCF_PG_FLAG_NONPERSISTENT, "reconfigure", set);
3844	switch (ret) {
3845	case 0:
3846	case EPERM:
3847	case EACCES:
3848	case EROFS:
3849		break;
3850
3851	case ECONNABORTED:
3852		libscf_handle_rebind(h);
3853		goto again;
3854
3855	case ECANCELED:
3856		ret = ENOENT;
3857		break;
3858
3859	default:
3860		bad_error("libscf_inst_set_boolean_prop", ret);
3861	}
3862
3863reconfig_out:
3864	scf_pg_destroy(pg);
3865	scf_instance_destroy(inst);
3866	scf_handle_destroy(h);
3867	return (ret);
3868}
3869
3870/*
3871 * Set inst->ri_m_inst to the scf instance for inst.  If it has been deleted,
3872 * set inst->ri_mi_deleted to true.  If the repository connection is broken, it
3873 * is rebound with libscf_handle_rebound().
3874 */
3875void
3876libscf_reget_instance(restarter_inst_t *inst)
3877{
3878	scf_handle_t *h;
3879	int r;
3880
3881	h = scf_instance_handle(inst->ri_m_inst);
3882
3883again:
3884	r = libscf_lookup_instance(inst->ri_i.i_fmri, inst->ri_m_inst);
3885	switch (r) {
3886	case 0:
3887	case ENOENT:
3888		inst->ri_mi_deleted = (r == ENOENT);
3889		return;
3890
3891	case ECONNABORTED:
3892		libscf_handle_rebind(h);
3893		goto again;
3894
3895	case EINVAL:
3896	case ENOTSUP:
3897	default:
3898		bad_error("libscf_lookup_instance", r);
3899	}
3900}
3901