xref: /onnv-gate/usr/src/cmd/svc/svcadm/synch.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * synchronous svcadm logic
31*0Sstevel@tonic-gate  */
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate #include <locale.h>
34*0Sstevel@tonic-gate #include <libintl.h>
35*0Sstevel@tonic-gate #include <libscf.h>
36*0Sstevel@tonic-gate #include <libscf_priv.h>
37*0Sstevel@tonic-gate #include <libuutil.h>
38*0Sstevel@tonic-gate #include <stddef.h>
39*0Sstevel@tonic-gate #include <stdio.h>
40*0Sstevel@tonic-gate #include <stdlib.h>
41*0Sstevel@tonic-gate #include <string.h>
42*0Sstevel@tonic-gate #include <unistd.h>
43*0Sstevel@tonic-gate #include <assert.h>
44*0Sstevel@tonic-gate #include <errno.h>
45*0Sstevel@tonic-gate #include <sys/stat.h>
46*0Sstevel@tonic-gate 
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate /*
49*0Sstevel@tonic-gate  * Definitions from svcadm.c.
50*0Sstevel@tonic-gate  */
51*0Sstevel@tonic-gate extern scf_handle_t *h;
52*0Sstevel@tonic-gate extern ssize_t max_scf_fmri_sz;
53*0Sstevel@tonic-gate 
54*0Sstevel@tonic-gate extern void do_scfdie(int);
55*0Sstevel@tonic-gate extern int inst_get_state(scf_instance_t *, char *, const char *,
56*0Sstevel@tonic-gate     scf_propertygroup_t **);
57*0Sstevel@tonic-gate extern ssize_t get_astring_prop(const scf_propertygroup_t *, const char *,
58*0Sstevel@tonic-gate     scf_property_t *, scf_value_t *, char *, size_t);
59*0Sstevel@tonic-gate extern int get_bool_prop(scf_propertygroup_t *, const char *, uint8_t *);
60*0Sstevel@tonic-gate 
61*0Sstevel@tonic-gate #define	scfdie()	do_scfdie(__LINE__)
62*0Sstevel@tonic-gate 
63*0Sstevel@tonic-gate int has_potential(scf_instance_t *, int);
64*0Sstevel@tonic-gate 
65*0Sstevel@tonic-gate /*
66*0Sstevel@tonic-gate  * Determines if the specified instance is enabled, composing the
67*0Sstevel@tonic-gate  * general and general_ovr property groups.  For simplicity, we map
68*0Sstevel@tonic-gate  * most errors to "not enabled".
69*0Sstevel@tonic-gate  */
70*0Sstevel@tonic-gate int
71*0Sstevel@tonic-gate is_enabled(scf_instance_t *inst)
72*0Sstevel@tonic-gate {
73*0Sstevel@tonic-gate 	scf_propertygroup_t *pg;
74*0Sstevel@tonic-gate 	uint8_t bp;
75*0Sstevel@tonic-gate 
76*0Sstevel@tonic-gate 	if ((pg = scf_pg_create(h)) == NULL)
77*0Sstevel@tonic-gate 		scfdie();
78*0Sstevel@tonic-gate 
79*0Sstevel@tonic-gate 	if (scf_instance_get_pg(inst, SCF_PG_GENERAL_OVR, pg) == 0 &&
80*0Sstevel@tonic-gate 	    get_bool_prop(pg, SCF_PROPERTY_ENABLED, &bp) == 0) {
81*0Sstevel@tonic-gate 		scf_pg_destroy(pg);
82*0Sstevel@tonic-gate 		return (bp);
83*0Sstevel@tonic-gate 	}
84*0Sstevel@tonic-gate 
85*0Sstevel@tonic-gate 	if (scf_instance_get_pg(inst, SCF_PG_GENERAL, pg) == 0 &&
86*0Sstevel@tonic-gate 	    get_bool_prop(pg, SCF_PROPERTY_ENABLED, &bp) == 0) {
87*0Sstevel@tonic-gate 		scf_pg_destroy(pg);
88*0Sstevel@tonic-gate 		return (bp);
89*0Sstevel@tonic-gate 	}
90*0Sstevel@tonic-gate 
91*0Sstevel@tonic-gate 	scf_pg_destroy(pg);
92*0Sstevel@tonic-gate 	return (B_FALSE);
93*0Sstevel@tonic-gate }
94*0Sstevel@tonic-gate 
95*0Sstevel@tonic-gate /*
96*0Sstevel@tonic-gate  * Reads an astring property from a property group.  If the named
97*0Sstevel@tonic-gate  * property doesn't exist, returns NULL.  The result of a successful
98*0Sstevel@tonic-gate  * call should be freed.
99*0Sstevel@tonic-gate  */
100*0Sstevel@tonic-gate static char *
101*0Sstevel@tonic-gate read_astring_prop(scf_propertygroup_t *pg, scf_value_t *val,
102*0Sstevel@tonic-gate     scf_property_t *prop, const char *name)
103*0Sstevel@tonic-gate {
104*0Sstevel@tonic-gate 	char *value;
105*0Sstevel@tonic-gate 	size_t value_sz;
106*0Sstevel@tonic-gate 
107*0Sstevel@tonic-gate 	if (scf_pg_get_property(pg, name, prop) == -1) {
108*0Sstevel@tonic-gate 		switch (scf_error()) {
109*0Sstevel@tonic-gate 		case SCF_ERROR_NOT_FOUND:
110*0Sstevel@tonic-gate 		case SCF_ERROR_DELETED:
111*0Sstevel@tonic-gate 			return (NULL);
112*0Sstevel@tonic-gate 		default:
113*0Sstevel@tonic-gate 			scfdie();
114*0Sstevel@tonic-gate 		}
115*0Sstevel@tonic-gate 	}
116*0Sstevel@tonic-gate 
117*0Sstevel@tonic-gate 	if (scf_property_get_value(prop, val) != 0) {
118*0Sstevel@tonic-gate 		switch (scf_error()) {
119*0Sstevel@tonic-gate 		case SCF_ERROR_DELETED:
120*0Sstevel@tonic-gate 		case SCF_ERROR_NOT_FOUND:
121*0Sstevel@tonic-gate 		case SCF_ERROR_CONSTRAINT_VIOLATED:
122*0Sstevel@tonic-gate 			return (NULL);
123*0Sstevel@tonic-gate 		default:
124*0Sstevel@tonic-gate 			scfdie();
125*0Sstevel@tonic-gate 		}
126*0Sstevel@tonic-gate 	}
127*0Sstevel@tonic-gate 
128*0Sstevel@tonic-gate 	value_sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
129*0Sstevel@tonic-gate 	if ((value = malloc(value_sz)) == NULL)
130*0Sstevel@tonic-gate 		scfdie();
131*0Sstevel@tonic-gate 
132*0Sstevel@tonic-gate 	if (scf_value_get_astring(val, value, value_sz) <= 0) {
133*0Sstevel@tonic-gate 		free(value);
134*0Sstevel@tonic-gate 		return (NULL);
135*0Sstevel@tonic-gate 	}
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate 	return (value);
138*0Sstevel@tonic-gate }
139*0Sstevel@tonic-gate 
140*0Sstevel@tonic-gate /*
141*0Sstevel@tonic-gate  * Creates and returns an scf_iter for the values of the named
142*0Sstevel@tonic-gate  * multi-value property.  Returns NULL on failure.
143*0Sstevel@tonic-gate  */
144*0Sstevel@tonic-gate static scf_iter_t *
145*0Sstevel@tonic-gate prop_walk_init(scf_propertygroup_t *pg, const char *name)
146*0Sstevel@tonic-gate {
147*0Sstevel@tonic-gate 	scf_iter_t *iter;
148*0Sstevel@tonic-gate 	scf_property_t *prop;
149*0Sstevel@tonic-gate 
150*0Sstevel@tonic-gate 	if ((iter = scf_iter_create(h)) == NULL ||
151*0Sstevel@tonic-gate 	    (prop = scf_property_create(h)) == NULL)
152*0Sstevel@tonic-gate 		scfdie();
153*0Sstevel@tonic-gate 
154*0Sstevel@tonic-gate 	if (scf_pg_get_property(pg, name, prop) != 0) {
155*0Sstevel@tonic-gate 		switch (scf_error()) {
156*0Sstevel@tonic-gate 		case SCF_ERROR_NOT_FOUND:
157*0Sstevel@tonic-gate 		case SCF_ERROR_DELETED:
158*0Sstevel@tonic-gate 			goto error;
159*0Sstevel@tonic-gate 		default:
160*0Sstevel@tonic-gate 			scfdie();
161*0Sstevel@tonic-gate 		}
162*0Sstevel@tonic-gate 	}
163*0Sstevel@tonic-gate 
164*0Sstevel@tonic-gate 	if (scf_iter_property_values(iter, prop) != 0) {
165*0Sstevel@tonic-gate 		if (scf_error() != SCF_ERROR_DELETED)
166*0Sstevel@tonic-gate 			scfdie();
167*0Sstevel@tonic-gate 		goto error;
168*0Sstevel@tonic-gate 	}
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate 	scf_property_destroy(prop);
171*0Sstevel@tonic-gate 	return (iter);
172*0Sstevel@tonic-gate error:
173*0Sstevel@tonic-gate 	scf_property_destroy(prop);
174*0Sstevel@tonic-gate 	scf_iter_destroy(iter);
175*0Sstevel@tonic-gate 	return (NULL);
176*0Sstevel@tonic-gate }
177*0Sstevel@tonic-gate 
178*0Sstevel@tonic-gate /*
179*0Sstevel@tonic-gate  * Reads the next value from the multi-value property using the
180*0Sstevel@tonic-gate  * scf_iter obtained by prop_walk_init, and places it in the buffer
181*0Sstevel@tonic-gate  * pointed to by fmri.  Returns -1 on failure, 0 when done, and non-0
182*0Sstevel@tonic-gate  * when returning a value.
183*0Sstevel@tonic-gate  */
184*0Sstevel@tonic-gate static int
185*0Sstevel@tonic-gate prop_walk_step(scf_iter_t *iter, char *fmri, size_t len)
186*0Sstevel@tonic-gate {
187*0Sstevel@tonic-gate 	int r;
188*0Sstevel@tonic-gate 	scf_value_t *val;
189*0Sstevel@tonic-gate 
190*0Sstevel@tonic-gate 	if ((val = scf_value_create(h)) == NULL)
191*0Sstevel@tonic-gate 		scfdie();
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate 	r = scf_iter_next_value(iter, val);
194*0Sstevel@tonic-gate 	if (r == 0)
195*0Sstevel@tonic-gate 		goto out;
196*0Sstevel@tonic-gate 	if (r == -1) {
197*0Sstevel@tonic-gate 		if (scf_error() != SCF_ERROR_DELETED)
198*0Sstevel@tonic-gate 			scfdie();
199*0Sstevel@tonic-gate 		goto out;
200*0Sstevel@tonic-gate 	}
201*0Sstevel@tonic-gate 	if (scf_value_get_astring(val, fmri, len) <= 0) {
202*0Sstevel@tonic-gate 		r = -1;
203*0Sstevel@tonic-gate 		goto out;
204*0Sstevel@tonic-gate 	}
205*0Sstevel@tonic-gate 
206*0Sstevel@tonic-gate out:
207*0Sstevel@tonic-gate 	scf_value_destroy(val);
208*0Sstevel@tonic-gate 	return (r);
209*0Sstevel@tonic-gate }
210*0Sstevel@tonic-gate 
211*0Sstevel@tonic-gate /*
212*0Sstevel@tonic-gate  * Determines if a file dependency is satisfied, taking into account
213*0Sstevel@tonic-gate  * whether it is an exclusion dependency or not.  If we can't access
214*0Sstevel@tonic-gate  * the file, we err on the side of caution and assume the dependency
215*0Sstevel@tonic-gate  * isn't satisfied.
216*0Sstevel@tonic-gate  */
217*0Sstevel@tonic-gate static int
218*0Sstevel@tonic-gate file_has_potential(char *fmri, int exclude)
219*0Sstevel@tonic-gate {
220*0Sstevel@tonic-gate 	const char *path;
221*0Sstevel@tonic-gate 	struct stat st;
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate 	int good = exclude ? B_FALSE : B_TRUE;
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate 	if (scf_parse_file_fmri(fmri, NULL, &path) != 0)
226*0Sstevel@tonic-gate 		return (good);
227*0Sstevel@tonic-gate 
228*0Sstevel@tonic-gate 	if (stat(path, &st) == 0)
229*0Sstevel@tonic-gate 		return (good);
230*0Sstevel@tonic-gate 
231*0Sstevel@tonic-gate 	if (errno == EACCES) {
232*0Sstevel@tonic-gate 		uu_warn(gettext("Unable to access \"%s\".\n"), path);
233*0Sstevel@tonic-gate 		return (B_FALSE);
234*0Sstevel@tonic-gate 	}
235*0Sstevel@tonic-gate 
236*0Sstevel@tonic-gate 	return (!good);
237*0Sstevel@tonic-gate }
238*0Sstevel@tonic-gate 
239*0Sstevel@tonic-gate /*
240*0Sstevel@tonic-gate  * Determines if a dependency on a service instance is satisfiable.
241*0Sstevel@tonic-gate  * Returns 0 if not, 1 if it is, or 2 if it is an optional or exclude
242*0Sstevel@tonic-gate  * dependency and the service only "weakly" satisfies (i.e. is disabled
243*0Sstevel@tonic-gate  * or is in maintenance state).
244*0Sstevel@tonic-gate  */
245*0Sstevel@tonic-gate static int
246*0Sstevel@tonic-gate inst_has_potential(scf_instance_t *inst, int enabled, int optional, int exclude)
247*0Sstevel@tonic-gate {
248*0Sstevel@tonic-gate 	char state[MAX_SCF_STATE_STRING_SZ];
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate 	if (!enabled)
251*0Sstevel@tonic-gate 		return ((optional || exclude) ? 2 : 0);
252*0Sstevel@tonic-gate 
253*0Sstevel@tonic-gate 	/*
254*0Sstevel@tonic-gate 	 * Normally we would return a positive value on failure;
255*0Sstevel@tonic-gate 	 * relying on startd to place the service in maintenance.  But
256*0Sstevel@tonic-gate 	 * if we can't read a service's state, we have to assume it is
257*0Sstevel@tonic-gate 	 * out to lunch.
258*0Sstevel@tonic-gate 	 */
259*0Sstevel@tonic-gate 	if (inst_get_state(inst, state, NULL, NULL) != 0)
260*0Sstevel@tonic-gate 		return (0);
261*0Sstevel@tonic-gate 
262*0Sstevel@tonic-gate 	/*
263*0Sstevel@tonic-gate 	 * Optional dependencies which are offline always have a possibility of
264*0Sstevel@tonic-gate 	 * coming online.
265*0Sstevel@tonic-gate 	 */
266*0Sstevel@tonic-gate 	if (optional && strcmp(state, SCF_STATE_STRING_OFFLINE) == 0)
267*0Sstevel@tonic-gate 		return (2);
268*0Sstevel@tonic-gate 
269*0Sstevel@tonic-gate 	if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) {
270*0Sstevel@tonic-gate 		/*
271*0Sstevel@tonic-gate 		 * Enabled services in maintenance state satisfy
272*0Sstevel@tonic-gate 		 * optional-all dependencies.
273*0Sstevel@tonic-gate 		 */
274*0Sstevel@tonic-gate 		return ((optional || exclude) ? 2 : 0);
275*0Sstevel@tonic-gate 	}
276*0Sstevel@tonic-gate 
277*0Sstevel@tonic-gate 	/*
278*0Sstevel@tonic-gate 	 * We're enabled and not in maintenance.
279*0Sstevel@tonic-gate 	 */
280*0Sstevel@tonic-gate 	if (exclude)
281*0Sstevel@tonic-gate 		return (0);
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate 	if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0 ||
284*0Sstevel@tonic-gate 	    strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)
285*0Sstevel@tonic-gate 		return (1);
286*0Sstevel@tonic-gate 
287*0Sstevel@tonic-gate 	return (has_potential(inst, B_FALSE));
288*0Sstevel@tonic-gate }
289*0Sstevel@tonic-gate 
290*0Sstevel@tonic-gate /*
291*0Sstevel@tonic-gate  * Determines if a dependency on an fmri is satisfiable, handling the
292*0Sstevel@tonic-gate  * separate cases for file, service, and instance fmris.  Returns false
293*0Sstevel@tonic-gate  * if not, or true if it is.  Takes into account if the dependency is
294*0Sstevel@tonic-gate  * an optional or exclusive one.
295*0Sstevel@tonic-gate  */
296*0Sstevel@tonic-gate static int
297*0Sstevel@tonic-gate fmri_has_potential(char *fmri, int isfile, int optional, int exclude,
298*0Sstevel@tonic-gate     int restarter)
299*0Sstevel@tonic-gate {
300*0Sstevel@tonic-gate 	scf_instance_t *inst;
301*0Sstevel@tonic-gate 	scf_service_t *svc;
302*0Sstevel@tonic-gate 	scf_iter_t *iter;
303*0Sstevel@tonic-gate 	int good = exclude ? B_FALSE : B_TRUE;
304*0Sstevel@tonic-gate 	int enabled;
305*0Sstevel@tonic-gate 	int r, result;
306*0Sstevel@tonic-gate 	int optbad;
307*0Sstevel@tonic-gate 
308*0Sstevel@tonic-gate 	assert(!optional || !exclude);
309*0Sstevel@tonic-gate 
310*0Sstevel@tonic-gate 	if (isfile)
311*0Sstevel@tonic-gate 		return (file_has_potential(fmri, exclude));
312*0Sstevel@tonic-gate 
313*0Sstevel@tonic-gate 	if ((inst = scf_instance_create(h)) == NULL ||
314*0Sstevel@tonic-gate 	    (svc = scf_service_create(h)) == NULL ||
315*0Sstevel@tonic-gate 	    (iter = scf_iter_create(h)) == NULL)
316*0Sstevel@tonic-gate 		scfdie();
317*0Sstevel@tonic-gate 
318*0Sstevel@tonic-gate 	if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL, NULL,
319*0Sstevel@tonic-gate 	    SCF_DECODE_FMRI_EXACT) == 0) {
320*0Sstevel@tonic-gate 		enabled = is_enabled(inst);
321*0Sstevel@tonic-gate 		result =
322*0Sstevel@tonic-gate 		    (inst_has_potential(inst, enabled, optional, exclude) != 0);
323*0Sstevel@tonic-gate 		goto out;
324*0Sstevel@tonic-gate 	}
325*0Sstevel@tonic-gate 
326*0Sstevel@tonic-gate 	if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL,
327*0Sstevel@tonic-gate 	    SCF_DECODE_FMRI_EXACT) != 0) {
328*0Sstevel@tonic-gate 		/*
329*0Sstevel@tonic-gate 		 * If we are checking a restarter dependency, a bad
330*0Sstevel@tonic-gate 		 * or nonexistent service will never be noticed.
331*0Sstevel@tonic-gate 		 */
332*0Sstevel@tonic-gate 		result = restarter ? B_FALSE : good;
333*0Sstevel@tonic-gate 		goto out;
334*0Sstevel@tonic-gate 	}
335*0Sstevel@tonic-gate 
336*0Sstevel@tonic-gate 	if (scf_iter_service_instances(iter, svc) != 0) {
337*0Sstevel@tonic-gate 		if (scf_error() != SCF_ERROR_DELETED)
338*0Sstevel@tonic-gate 			scfdie();
339*0Sstevel@tonic-gate 		result = good;
340*0Sstevel@tonic-gate 		goto out;
341*0Sstevel@tonic-gate 	}
342*0Sstevel@tonic-gate 
343*0Sstevel@tonic-gate 	optbad = 0;
344*0Sstevel@tonic-gate 	for (;;) {
345*0Sstevel@tonic-gate 		r = scf_iter_next_instance(iter, inst);
346*0Sstevel@tonic-gate 		if (r == 0) {
347*0Sstevel@tonic-gate 			result = exclude || (optional && !optbad);
348*0Sstevel@tonic-gate 			goto out;
349*0Sstevel@tonic-gate 		}
350*0Sstevel@tonic-gate 		if (r == -1) {
351*0Sstevel@tonic-gate 			if (scf_error() != SCF_ERROR_DELETED)
352*0Sstevel@tonic-gate 				scfdie();
353*0Sstevel@tonic-gate 			result = good;
354*0Sstevel@tonic-gate 			goto out;
355*0Sstevel@tonic-gate 		}
356*0Sstevel@tonic-gate 
357*0Sstevel@tonic-gate 		enabled = is_enabled(inst);
358*0Sstevel@tonic-gate 		r = inst_has_potential(inst, enabled, optional, exclude);
359*0Sstevel@tonic-gate 
360*0Sstevel@tonic-gate 		/*
361*0Sstevel@tonic-gate 		 * Exclusion dependencies over services map to
362*0Sstevel@tonic-gate 		 * require-none for its instances.
363*0Sstevel@tonic-gate 		 */
364*0Sstevel@tonic-gate 		if (exclude)
365*0Sstevel@tonic-gate 			r = (r == 0);
366*0Sstevel@tonic-gate 
367*0Sstevel@tonic-gate 		if (r == 1) {
368*0Sstevel@tonic-gate 			/*
369*0Sstevel@tonic-gate 			 * Remember, if this is an exclusion dependency
370*0Sstevel@tonic-gate 			 * (which means we are here because there
371*0Sstevel@tonic-gate 			 * exists an instance which wasn't satisfiable
372*0Sstevel@tonic-gate 			 * in that regard), good means bad.
373*0Sstevel@tonic-gate 			 */
374*0Sstevel@tonic-gate 			result = good;
375*0Sstevel@tonic-gate 			goto out;
376*0Sstevel@tonic-gate 		}
377*0Sstevel@tonic-gate 
378*0Sstevel@tonic-gate 		if (optional && r == 0)
379*0Sstevel@tonic-gate 			optbad = 1;
380*0Sstevel@tonic-gate 	}
381*0Sstevel@tonic-gate 
382*0Sstevel@tonic-gate out:
383*0Sstevel@tonic-gate 	scf_instance_destroy(inst);
384*0Sstevel@tonic-gate 	scf_service_destroy(svc);
385*0Sstevel@tonic-gate 	scf_iter_destroy(iter);
386*0Sstevel@tonic-gate 	return (result);
387*0Sstevel@tonic-gate }
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate static int
390*0Sstevel@tonic-gate eval_require_any(scf_iter_t *iter, char *value, size_t value_sz, int isfile)
391*0Sstevel@tonic-gate {
392*0Sstevel@tonic-gate 	int r, empty = B_TRUE;
393*0Sstevel@tonic-gate 
394*0Sstevel@tonic-gate 	for (;;) {
395*0Sstevel@tonic-gate 		/*
396*0Sstevel@tonic-gate 		 * For reasons unknown, an empty require_any dependency
397*0Sstevel@tonic-gate 		 * group is considered by startd to be satisfied.
398*0Sstevel@tonic-gate 		 * This insanity fortunately doesn't extend to
399*0Sstevel@tonic-gate 		 * dependencies on services with no instances.
400*0Sstevel@tonic-gate 		 */
401*0Sstevel@tonic-gate 		if ((r = prop_walk_step(iter, value, value_sz)) <= 0)
402*0Sstevel@tonic-gate 			return ((r == 0 && empty) ? B_TRUE : r);
403*0Sstevel@tonic-gate 		if (fmri_has_potential(value, isfile, B_FALSE, B_FALSE,
404*0Sstevel@tonic-gate 		    B_FALSE))
405*0Sstevel@tonic-gate 			return (1);
406*0Sstevel@tonic-gate 		empty = B_FALSE;
407*0Sstevel@tonic-gate 	}
408*0Sstevel@tonic-gate }
409*0Sstevel@tonic-gate 
410*0Sstevel@tonic-gate static int
411*0Sstevel@tonic-gate eval_all(scf_iter_t *iter, char *value, size_t value_sz,
412*0Sstevel@tonic-gate     int isfile, int optional, int exclude)
413*0Sstevel@tonic-gate {
414*0Sstevel@tonic-gate 	int r;
415*0Sstevel@tonic-gate 
416*0Sstevel@tonic-gate 	for (;;) {
417*0Sstevel@tonic-gate 		if ((r = prop_walk_step(iter, value, value_sz)) <= 0)
418*0Sstevel@tonic-gate 			return ((r == 0) ? 1 : r);
419*0Sstevel@tonic-gate 		if (!fmri_has_potential(value, isfile, optional, exclude,
420*0Sstevel@tonic-gate 		    B_FALSE))
421*0Sstevel@tonic-gate 			return (0);
422*0Sstevel@tonic-gate 	}
423*0Sstevel@tonic-gate }
424*0Sstevel@tonic-gate 
425*0Sstevel@tonic-gate static int
426*0Sstevel@tonic-gate eval_require_all(scf_iter_t *iter, char *value, size_t value_sz, int isfile)
427*0Sstevel@tonic-gate {
428*0Sstevel@tonic-gate 	return (eval_all(iter, value, value_sz, isfile, B_FALSE, B_FALSE));
429*0Sstevel@tonic-gate }
430*0Sstevel@tonic-gate 
431*0Sstevel@tonic-gate static int
432*0Sstevel@tonic-gate eval_optional_all(scf_iter_t *iter, char *value, size_t value_sz, int isfile)
433*0Sstevel@tonic-gate {
434*0Sstevel@tonic-gate 	return (eval_all(iter, value, value_sz, isfile, B_TRUE, B_FALSE));
435*0Sstevel@tonic-gate }
436*0Sstevel@tonic-gate 
437*0Sstevel@tonic-gate static int
438*0Sstevel@tonic-gate eval_exclude_all(scf_iter_t *iter, char *value, size_t value_sz, int isfile)
439*0Sstevel@tonic-gate {
440*0Sstevel@tonic-gate 	return (eval_all(iter, value, value_sz, isfile, B_FALSE, B_TRUE));
441*0Sstevel@tonic-gate }
442*0Sstevel@tonic-gate 
443*0Sstevel@tonic-gate /*
444*0Sstevel@tonic-gate  * Examines the state and health of an instance's restarter and
445*0Sstevel@tonic-gate  * dependencies, and determines the impact of both on the instance's
446*0Sstevel@tonic-gate  * ability to be brought on line.  A true return value indicates that
447*0Sstevel@tonic-gate  * instance appears to be a likely candidate for the online club.
448*0Sstevel@tonic-gate  * False indicates that there is no hope for the instance.
449*0Sstevel@tonic-gate  */
450*0Sstevel@tonic-gate int
451*0Sstevel@tonic-gate has_potential(scf_instance_t *inst, int restarter_only)
452*0Sstevel@tonic-gate {
453*0Sstevel@tonic-gate 	scf_snapshot_t *snap;
454*0Sstevel@tonic-gate 	scf_iter_t *iter, *viter = NULL;
455*0Sstevel@tonic-gate 	scf_propertygroup_t *pg;
456*0Sstevel@tonic-gate 	scf_property_t *prop;
457*0Sstevel@tonic-gate 	scf_value_t *val;
458*0Sstevel@tonic-gate 	char *type = NULL, *grouping = NULL;
459*0Sstevel@tonic-gate 	char *value;
460*0Sstevel@tonic-gate 	size_t value_sz;
461*0Sstevel@tonic-gate 	int result = B_TRUE, r;
462*0Sstevel@tonic-gate 	int isfile;
463*0Sstevel@tonic-gate 
464*0Sstevel@tonic-gate 	value_sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
465*0Sstevel@tonic-gate 	if ((iter = scf_iter_create(h)) == NULL ||
466*0Sstevel@tonic-gate 	    (snap = scf_snapshot_create(h)) == NULL ||
467*0Sstevel@tonic-gate 	    (pg = scf_pg_create(h)) == NULL ||
468*0Sstevel@tonic-gate 	    (val = scf_value_create(h)) == NULL ||
469*0Sstevel@tonic-gate 	    (prop = scf_property_create(h)) == NULL ||
470*0Sstevel@tonic-gate 	    (value = malloc(value_sz)) == NULL)
471*0Sstevel@tonic-gate 		scfdie();
472*0Sstevel@tonic-gate 
473*0Sstevel@tonic-gate 	/*
474*0Sstevel@tonic-gate 	 * First we check our restarter as an implicit dependency.
475*0Sstevel@tonic-gate 	 */
476*0Sstevel@tonic-gate 	if (scf_instance_get_pg_composed(inst, NULL, SCF_PG_GENERAL, pg) != 0)
477*0Sstevel@tonic-gate 		scfdie();
478*0Sstevel@tonic-gate 
479*0Sstevel@tonic-gate 	r = get_astring_prop(pg, SCF_PROPERTY_RESTARTER, prop, val, value,
480*0Sstevel@tonic-gate 	    value_sz);
481*0Sstevel@tonic-gate 	if (r == -ENOENT) {
482*0Sstevel@tonic-gate 		(void) strlcpy(value, SCF_SERVICE_STARTD, value_sz);
483*0Sstevel@tonic-gate 	} else if (r < 0 || r > max_scf_fmri_sz) {
484*0Sstevel@tonic-gate 		/*
485*0Sstevel@tonic-gate 		 * Normally we would return true and let the restarter
486*0Sstevel@tonic-gate 		 * tell our caller there is a problem by changing the
487*0Sstevel@tonic-gate 		 * instance's state, but that's not going to happen if
488*0Sstevel@tonic-gate 		 * the restarter is invalid.
489*0Sstevel@tonic-gate 		 */
490*0Sstevel@tonic-gate 		result = B_FALSE;
491*0Sstevel@tonic-gate 		goto out;
492*0Sstevel@tonic-gate 	}
493*0Sstevel@tonic-gate 
494*0Sstevel@tonic-gate 	if (!fmri_has_potential(value, B_FALSE, B_FALSE, B_FALSE, B_TRUE)) {
495*0Sstevel@tonic-gate 		result = B_FALSE;
496*0Sstevel@tonic-gate 		goto out;
497*0Sstevel@tonic-gate 	}
498*0Sstevel@tonic-gate 
499*0Sstevel@tonic-gate 	if (restarter_only)
500*0Sstevel@tonic-gate 		goto out;
501*0Sstevel@tonic-gate 
502*0Sstevel@tonic-gate 	/*
503*0Sstevel@tonic-gate 	 * Now we check explicit dependencies.
504*0Sstevel@tonic-gate 	 */
505*0Sstevel@tonic-gate 	if (scf_instance_get_snapshot(inst, "running", snap) != 0) {
506*0Sstevel@tonic-gate 		if (scf_error() != SCF_ERROR_NOT_FOUND)
507*0Sstevel@tonic-gate 			scfdie();
508*0Sstevel@tonic-gate 		scf_snapshot_destroy(snap);
509*0Sstevel@tonic-gate 		snap = NULL;
510*0Sstevel@tonic-gate 	}
511*0Sstevel@tonic-gate 
512*0Sstevel@tonic-gate 	if (scf_iter_instance_pgs_typed_composed(iter, inst, snap,
513*0Sstevel@tonic-gate 	    SCF_GROUP_DEPENDENCY) != 0) {
514*0Sstevel@tonic-gate 		if (scf_error() != SCF_ERROR_DELETED)
515*0Sstevel@tonic-gate 			scfdie();
516*0Sstevel@tonic-gate 		goto out;
517*0Sstevel@tonic-gate 	}
518*0Sstevel@tonic-gate 
519*0Sstevel@tonic-gate 	for (;;) {
520*0Sstevel@tonic-gate 		r = scf_iter_next_pg(iter, pg);
521*0Sstevel@tonic-gate 		if (r == 0)
522*0Sstevel@tonic-gate 			break;
523*0Sstevel@tonic-gate 		if (r == -1) {
524*0Sstevel@tonic-gate 			if (scf_error() != SCF_ERROR_DELETED)
525*0Sstevel@tonic-gate 				scfdie();
526*0Sstevel@tonic-gate 			goto out;
527*0Sstevel@tonic-gate 		}
528*0Sstevel@tonic-gate 
529*0Sstevel@tonic-gate 		if ((grouping = read_astring_prop(pg, val, prop,
530*0Sstevel@tonic-gate 		    SCF_PROPERTY_GROUPING)) == NULL)
531*0Sstevel@tonic-gate 			goto out;
532*0Sstevel@tonic-gate 
533*0Sstevel@tonic-gate 		if ((type = read_astring_prop(pg, val, prop,
534*0Sstevel@tonic-gate 		    SCF_PROPERTY_TYPE)) == NULL)
535*0Sstevel@tonic-gate 			goto out;
536*0Sstevel@tonic-gate 
537*0Sstevel@tonic-gate 		if (strcmp(type, "path") == 0) {
538*0Sstevel@tonic-gate 			isfile = B_TRUE;
539*0Sstevel@tonic-gate 		} else if (strcmp(type, "service") == 0) {
540*0Sstevel@tonic-gate 			isfile = B_FALSE;
541*0Sstevel@tonic-gate 		} else {
542*0Sstevel@tonic-gate 			free(type);
543*0Sstevel@tonic-gate 			goto out;
544*0Sstevel@tonic-gate 		}
545*0Sstevel@tonic-gate 		free(type);
546*0Sstevel@tonic-gate 
547*0Sstevel@tonic-gate 		if ((viter = prop_walk_init(pg, SCF_PROPERTY_ENTITIES)) == NULL)
548*0Sstevel@tonic-gate 			goto out;
549*0Sstevel@tonic-gate 
550*0Sstevel@tonic-gate 		if (strcmp(grouping, SCF_DEP_REQUIRE_ALL) == 0) {
551*0Sstevel@tonic-gate 			r = eval_require_all(viter, value, value_sz, isfile);
552*0Sstevel@tonic-gate 		} else if (strcmp(grouping, SCF_DEP_REQUIRE_ANY) == 0) {
553*0Sstevel@tonic-gate 			r = eval_require_any(viter, value, value_sz, isfile);
554*0Sstevel@tonic-gate 		} else if (strcmp(grouping, SCF_DEP_EXCLUDE_ALL) == 0) {
555*0Sstevel@tonic-gate 			r = eval_exclude_all(viter, value, value_sz, isfile);
556*0Sstevel@tonic-gate 		} else if (strcmp(grouping, SCF_DEP_OPTIONAL_ALL) == 0) {
557*0Sstevel@tonic-gate 			r = eval_optional_all(viter, value, value_sz, isfile);
558*0Sstevel@tonic-gate 		} else {
559*0Sstevel@tonic-gate 			scf_iter_destroy(viter);
560*0Sstevel@tonic-gate 			free(grouping);
561*0Sstevel@tonic-gate 			grouping = NULL;
562*0Sstevel@tonic-gate 			goto out;
563*0Sstevel@tonic-gate 		}
564*0Sstevel@tonic-gate 
565*0Sstevel@tonic-gate 		scf_iter_destroy(viter);
566*0Sstevel@tonic-gate 		free(grouping);
567*0Sstevel@tonic-gate 		grouping = NULL;
568*0Sstevel@tonic-gate 
569*0Sstevel@tonic-gate 		if (r == 0) {
570*0Sstevel@tonic-gate 			result = B_FALSE;
571*0Sstevel@tonic-gate 			goto out;
572*0Sstevel@tonic-gate 		} else if (r == -1) {
573*0Sstevel@tonic-gate 			goto out;
574*0Sstevel@tonic-gate 		}
575*0Sstevel@tonic-gate 	}
576*0Sstevel@tonic-gate 
577*0Sstevel@tonic-gate out:
578*0Sstevel@tonic-gate 	free(value);
579*0Sstevel@tonic-gate 	scf_property_destroy(prop);
580*0Sstevel@tonic-gate 	scf_value_destroy(val);
581*0Sstevel@tonic-gate 	scf_pg_destroy(pg);
582*0Sstevel@tonic-gate 	if (snap != NULL)
583*0Sstevel@tonic-gate 		scf_snapshot_destroy(snap);
584*0Sstevel@tonic-gate 	if (grouping != NULL)
585*0Sstevel@tonic-gate 		free(grouping);
586*0Sstevel@tonic-gate 	scf_iter_destroy(iter);
587*0Sstevel@tonic-gate 	return (result);
588*0Sstevel@tonic-gate }
589