4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * synchronous svcadm logic
34 #include <libscf_priv.h>
47 * Definitions from svcadm.c.
49 extern scf_handle_t
*h
;
50 extern ssize_t max_scf_fmri_sz
;
52 extern void do_scfdie(int) __NORETURN
;
53 extern int inst_get_state(scf_instance_t
*, char *, const char *,
54 scf_propertygroup_t
**);
55 extern ssize_t
get_astring_prop(const scf_propertygroup_t
*, const char *,
56 scf_property_t
*, scf_value_t
*, char *, size_t);
57 extern int get_bool_prop(scf_propertygroup_t
*, const char *, uint8_t *);
59 #define scfdie() do_scfdie(__LINE__)
61 int has_potential(scf_instance_t
*, int);
64 * Determines if the specified instance is enabled, composing the
65 * general and general_ovr property groups. For simplicity, we map
66 * most errors to "not enabled".
69 is_enabled(scf_instance_t
*inst
)
71 scf_propertygroup_t
*pg
;
74 if ((pg
= scf_pg_create(h
)) == NULL
)
77 if (scf_instance_get_pg(inst
, SCF_PG_GENERAL_OVR
, pg
) == 0 &&
78 get_bool_prop(pg
, SCF_PROPERTY_ENABLED
, &bp
) == 0) {
83 if (scf_instance_get_pg(inst
, SCF_PG_GENERAL
, pg
) == 0 &&
84 get_bool_prop(pg
, SCF_PROPERTY_ENABLED
, &bp
) == 0) {
94 * Reads an astring property from a property group. If the named
95 * property doesn't exist, returns NULL. The result of a successful
96 * call should be freed.
99 read_astring_prop(scf_propertygroup_t
*pg
, scf_value_t
*val
,
100 scf_property_t
*prop
, const char *name
)
105 if (scf_pg_get_property(pg
, name
, prop
) == -1) {
106 switch (scf_error()) {
107 case SCF_ERROR_NOT_FOUND
:
108 case SCF_ERROR_DELETED
:
115 if (scf_property_get_value(prop
, val
) != 0) {
116 switch (scf_error()) {
117 case SCF_ERROR_DELETED
:
118 case SCF_ERROR_NOT_FOUND
:
119 case SCF_ERROR_CONSTRAINT_VIOLATED
:
126 value_sz
= scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
127 if ((value
= malloc(value_sz
)) == NULL
)
130 if (scf_value_get_astring(val
, value
, value_sz
) <= 0) {
139 * Creates and returns an scf_iter for the values of the named
140 * multi-value property. Returns NULL on failure.
143 prop_walk_init(scf_propertygroup_t
*pg
, const char *name
)
146 scf_property_t
*prop
;
148 if ((iter
= scf_iter_create(h
)) == NULL
||
149 (prop
= scf_property_create(h
)) == NULL
)
152 if (scf_pg_get_property(pg
, name
, prop
) != 0) {
153 switch (scf_error()) {
154 case SCF_ERROR_NOT_FOUND
:
155 case SCF_ERROR_DELETED
:
162 if (scf_iter_property_values(iter
, prop
) != 0) {
163 if (scf_error() != SCF_ERROR_DELETED
)
168 scf_property_destroy(prop
);
171 scf_property_destroy(prop
);
172 scf_iter_destroy(iter
);
177 * Reads the next value from the multi-value property using the
178 * scf_iter obtained by prop_walk_init, and places it in the buffer
179 * pointed to by fmri. Returns -1 on failure, 0 when done, and non-0
180 * when returning a value.
183 prop_walk_step(scf_iter_t
*iter
, char *fmri
, size_t len
)
188 if ((val
= scf_value_create(h
)) == NULL
)
191 r
= scf_iter_next_value(iter
, val
);
195 if (scf_error() != SCF_ERROR_DELETED
)
199 if (scf_value_get_astring(val
, fmri
, len
) <= 0) {
205 scf_value_destroy(val
);
210 * Determines if a file dependency is satisfied, taking into account
211 * whether it is an exclusion dependency or not. If we can't access
212 * the file, we err on the side of caution and assume the dependency
216 file_has_potential(char *fmri
, int exclude
)
221 int good
= exclude
? B_FALSE
: B_TRUE
;
223 if (scf_parse_file_fmri(fmri
, NULL
, &path
) != 0)
226 if (stat(path
, &st
) == 0)
229 if (errno
== EACCES
) {
230 uu_warn(gettext("Unable to access \"%s\".\n"), path
);
238 * Determines if a dependency on a service instance is satisfiable.
239 * Returns 0 if not, 1 if it is, or 2 if it is an optional or exclude
240 * dependency and the service only "weakly" satisfies (i.e. is disabled
241 * or is in maintenance state).
244 inst_has_potential(scf_instance_t
*inst
, int enabled
, int optional
, int exclude
)
246 char state
[MAX_SCF_STATE_STRING_SZ
];
249 return ((optional
|| exclude
) ? 2 : 0);
252 * Normally we would return a positive value on failure;
253 * relying on startd to place the service in maintenance. But
254 * if we can't read a service's state, we have to assume it is
257 if (inst_get_state(inst
, state
, NULL
, NULL
) != 0)
261 * Optional dependencies which are offline always have a possibility of
264 if (optional
&& strcmp(state
, SCF_STATE_STRING_OFFLINE
) == 0)
267 if (strcmp(state
, SCF_STATE_STRING_MAINT
) == 0) {
269 * Enabled services in maintenance state satisfy
270 * optional-all dependencies.
272 return ((optional
|| exclude
) ? 2 : 0);
276 * We're enabled and not in maintenance.
281 if (strcmp(state
, SCF_STATE_STRING_ONLINE
) == 0 ||
282 strcmp(state
, SCF_STATE_STRING_DEGRADED
) == 0)
285 return (has_potential(inst
, B_FALSE
));
289 * Determines if a dependency on an fmri is satisfiable, handling the
290 * separate cases for file, service, and instance fmris. Returns false
291 * if not, or true if it is. Takes into account if the dependency is
292 * an optional or exclusive one.
295 fmri_has_potential(char *fmri
, int isfile
, int optional
, int exclude
,
298 scf_instance_t
*inst
;
301 int good
= exclude
? B_FALSE
: B_TRUE
;
306 assert(!optional
|| !exclude
);
309 return (file_has_potential(fmri
, exclude
));
311 if ((inst
= scf_instance_create(h
)) == NULL
||
312 (svc
= scf_service_create(h
)) == NULL
||
313 (iter
= scf_iter_create(h
)) == NULL
)
316 if (scf_handle_decode_fmri(h
, fmri
, NULL
, NULL
, inst
, NULL
, NULL
,
317 SCF_DECODE_FMRI_EXACT
) == 0) {
318 enabled
= is_enabled(inst
);
320 (inst_has_potential(inst
, enabled
, optional
, exclude
) != 0);
324 if (scf_handle_decode_fmri(h
, fmri
, NULL
, svc
, NULL
, NULL
, NULL
,
325 SCF_DECODE_FMRI_EXACT
) != 0) {
327 * If we are checking a restarter dependency, a bad
328 * or nonexistent service will never be noticed.
330 result
= restarter
? B_FALSE
: good
;
334 if (scf_iter_service_instances(iter
, svc
) != 0) {
335 if (scf_error() != SCF_ERROR_DELETED
)
343 r
= scf_iter_next_instance(iter
, inst
);
345 result
= exclude
|| (optional
&& !optbad
);
349 if (scf_error() != SCF_ERROR_DELETED
)
355 enabled
= is_enabled(inst
);
356 r
= inst_has_potential(inst
, enabled
, optional
, exclude
);
359 * Exclusion dependencies over services map to
360 * require-none for its instances.
367 * Remember, if this is an exclusion dependency
368 * (which means we are here because there
369 * exists an instance which wasn't satisfiable
370 * in that regard), good means bad.
376 if (optional
&& r
== 0)
381 scf_instance_destroy(inst
);
382 scf_service_destroy(svc
);
383 scf_iter_destroy(iter
);
388 eval_require_any(scf_iter_t
*iter
, char *value
, size_t value_sz
, int isfile
)
390 int r
, empty
= B_TRUE
;
394 * For reasons unknown, an empty require_any dependency
395 * group is considered by startd to be satisfied.
396 * This insanity fortunately doesn't extend to
397 * dependencies on services with no instances.
399 if ((r
= prop_walk_step(iter
, value
, value_sz
)) <= 0)
400 return ((r
== 0 && empty
) ? B_TRUE
: r
);
401 if (fmri_has_potential(value
, isfile
, B_FALSE
, B_FALSE
,
409 eval_all(scf_iter_t
*iter
, char *value
, size_t value_sz
,
410 int isfile
, int optional
, int exclude
)
415 if ((r
= prop_walk_step(iter
, value
, value_sz
)) <= 0)
416 return ((r
== 0) ? 1 : r
);
417 if (!fmri_has_potential(value
, isfile
, optional
, exclude
,
424 eval_require_all(scf_iter_t
*iter
, char *value
, size_t value_sz
, int isfile
)
426 return (eval_all(iter
, value
, value_sz
, isfile
, B_FALSE
, B_FALSE
));
430 eval_optional_all(scf_iter_t
*iter
, char *value
, size_t value_sz
, int isfile
)
432 return (eval_all(iter
, value
, value_sz
, isfile
, B_TRUE
, B_FALSE
));
436 eval_exclude_all(scf_iter_t
*iter
, char *value
, size_t value_sz
, int isfile
)
438 return (eval_all(iter
, value
, value_sz
, isfile
, B_FALSE
, B_TRUE
));
442 * Examines the state and health of an instance's restarter and
443 * dependencies, and determines the impact of both on the instance's
444 * ability to be brought on line. A true return value indicates that
445 * instance appears to be a likely candidate for the online club.
446 * False indicates that there is no hope for the instance.
449 has_potential(scf_instance_t
*inst
, int restarter_only
)
451 scf_snapshot_t
*snap
;
452 scf_iter_t
*iter
, *viter
= NULL
;
453 scf_propertygroup_t
*pg
;
454 scf_property_t
*prop
;
456 char *type
= NULL
, *grouping
= NULL
;
459 int result
= B_TRUE
, r
;
462 value_sz
= scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH
);
463 if ((iter
= scf_iter_create(h
)) == NULL
||
464 (snap
= scf_snapshot_create(h
)) == NULL
||
465 (pg
= scf_pg_create(h
)) == NULL
||
466 (val
= scf_value_create(h
)) == NULL
||
467 (prop
= scf_property_create(h
)) == NULL
||
468 (value
= malloc(value_sz
)) == NULL
)
472 * First we check our restarter as an implicit dependency.
474 if (scf_instance_get_pg_composed(inst
, NULL
, SCF_PG_GENERAL
, pg
) != 0)
477 r
= get_astring_prop(pg
, SCF_PROPERTY_RESTARTER
, prop
, val
, value
,
480 (void) strlcpy(value
, SCF_SERVICE_STARTD
, value_sz
);
481 } else if (r
< 0 || r
> max_scf_fmri_sz
) {
483 * Normally we would return true and let the restarter
484 * tell our caller there is a problem by changing the
485 * instance's state, but that's not going to happen if
486 * the restarter is invalid.
492 if (!fmri_has_potential(value
, B_FALSE
, B_FALSE
, B_FALSE
, B_TRUE
)) {
501 * Now we check explicit dependencies.
503 if (scf_instance_get_snapshot(inst
, "running", snap
) != 0) {
504 if (scf_error() != SCF_ERROR_NOT_FOUND
)
506 scf_snapshot_destroy(snap
);
510 if (scf_iter_instance_pgs_typed_composed(iter
, inst
, snap
,
511 SCF_GROUP_DEPENDENCY
) != 0) {
512 if (scf_error() != SCF_ERROR_DELETED
)
518 r
= scf_iter_next_pg(iter
, pg
);
522 if (scf_error() != SCF_ERROR_DELETED
)
527 if ((grouping
= read_astring_prop(pg
, val
, prop
,
528 SCF_PROPERTY_GROUPING
)) == NULL
)
531 if ((type
= read_astring_prop(pg
, val
, prop
,
532 SCF_PROPERTY_TYPE
)) == NULL
)
535 if (strcmp(type
, "path") == 0) {
537 } else if (strcmp(type
, "service") == 0) {
545 if ((viter
= prop_walk_init(pg
, SCF_PROPERTY_ENTITIES
)) == NULL
)
548 if (strcmp(grouping
, SCF_DEP_REQUIRE_ALL
) == 0) {
549 r
= eval_require_all(viter
, value
, value_sz
, isfile
);
550 } else if (strcmp(grouping
, SCF_DEP_REQUIRE_ANY
) == 0) {
551 r
= eval_require_any(viter
, value
, value_sz
, isfile
);
552 } else if (strcmp(grouping
, SCF_DEP_EXCLUDE_ALL
) == 0) {
553 r
= eval_exclude_all(viter
, value
, value_sz
, isfile
);
554 } else if (strcmp(grouping
, SCF_DEP_OPTIONAL_ALL
) == 0) {
555 r
= eval_optional_all(viter
, value
, value_sz
, isfile
);
557 scf_iter_destroy(viter
);
563 scf_iter_destroy(viter
);
570 } else if (r
== -1) {
577 scf_property_destroy(prop
);
578 scf_value_destroy(val
);
581 scf_snapshot_destroy(snap
);
583 scf_iter_destroy(iter
);