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