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