Home | History | Annotate | Download | only in startd
      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 2004 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <assert.h>
     30 #include <libscf.h>
     31 #include <libscf_priv.h>
     32 #include <libuutil.h>
     33 #include <stdio.h>
     34 #include <stdlib.h>
     35 #include <strings.h>
     36 #include <errno.h>
     37 
     38 #include "startd.h"
     39 
     40 /*
     41  * Return an allocated copy of str, with the Bourne shell's metacharacters
     42  * escaped by '\'.  Returns NULL on (allocation) failure.
     43  */
     44 static char *
     45 quote_for_shell(const char *str)
     46 {
     47 	const char *sp;
     48 	char *dst, *dp;
     49 	size_t dst_len;
     50 
     51 	const char * const metachars = ";&()|^<>\n \t\\\"\'`";
     52 
     53 	dst_len = 0;
     54 	for (sp = str; *sp != '\0'; ++sp) {
     55 		++dst_len;
     56 
     57 		if (strchr(metachars, *sp) != NULL)
     58 			++dst_len;
     59 	}
     60 
     61 	if (sp - str == dst_len)
     62 		return (safe_strdup(str));
     63 
     64 	dst = malloc(dst_len + 1);
     65 	if (dst == NULL)
     66 		return (NULL);
     67 
     68 	for (dp = dst, sp = str; *sp != '\0'; ++dp, ++sp) {
     69 		if (strchr(metachars, *sp) != NULL)
     70 			*dp++ = '\\';
     71 
     72 		*dp = *sp;
     73 	}
     74 	*dp = '\0';
     75 
     76 	return (dst);
     77 }
     78 
     79 /*
     80  * Return an allocated string representation of the value v.
     81  * Return NULL on error.
     82  */
     83 static char *
     84 val_to_str(scf_value_t *v)
     85 {
     86 	char *buf;
     87 	ssize_t buflen, ret;
     88 
     89 	buflen = scf_value_get_as_string(v, NULL, 0);
     90 	assert(buflen >= 0);
     91 
     92 	buf = malloc(buflen + 1);
     93 	if (buf == NULL)
     94 		return (NULL);
     95 
     96 	ret = scf_value_get_as_string(v, buf, buflen + 1);
     97 	assert(ret == buflen);
     98 
     99 	return (buf);
    100 }
    101 
    102 /*
    103  * Look up a property in the given snapshot, or the editing one
    104  * if not found. Returns scf_error() on failure, or 0 otherwise.
    105  */
    106 static int
    107 get_prop(const scf_instance_t *inst, scf_snapshot_t *snap,
    108     const char *pgn, const char *pn, scf_propertygroup_t *pg,
    109     scf_property_t *prop)
    110 {
    111 	int ret;
    112 
    113 	ret = scf_instance_get_pg_composed(inst, snap, pgn, pg);
    114 	if (ret != 0) {
    115 		snap = NULL;
    116 		if (scf_error() == SCF_ERROR_NOT_FOUND)
    117 			ret = scf_instance_get_pg_composed(inst, snap, pgn, pg);
    118 		if (ret != 0)
    119 			return (scf_error());
    120 	}
    121 
    122 	if (scf_pg_get_property(pg, pn, prop) == 0)
    123 		return (0);
    124 
    125 	if (snap == NULL)
    126 		return (scf_error());
    127 
    128 	ret = scf_instance_get_pg_composed(inst, NULL, pgn, pg);
    129 	if (ret != 0)
    130 		return (scf_error());
    131 
    132 	if (scf_pg_get_property(pg, pn, prop) == 0)
    133 		return (0);
    134 
    135 	return (scf_error());
    136 }
    137 
    138 /*
    139  * Get an allocated string representation of the values of the property
    140  * specified by inst & prop_spec and store it in *retstr.  prop_spec may
    141  * be a full property FMRI, or a "property-group/property" pair relative
    142  * to inst, or the name of a property in inst's "application" property
    143  * group.  In the latter two cases, the property is looked up in inst's
    144  * snap snapshot.  In the first case, the target instance's running
    145  * snapshot will be used.  In any case, if the property or its group
    146  * can't be found, the "editing" snapshot will be checked.  Multiple
    147  * values will be separated by sep.
    148  *
    149  * On error, non-zero is returned, and *retstr is set to an error
    150  * string.
    151  *
    152  * *retstr should always be freed by the caller.
    153  */
    154 static int
    155 get_prop_val_str(const scf_instance_t *inst, scf_snapshot_t *snap,
    156     const char *prop_spec, char sep, char **retstr)
    157 {
    158 	scf_handle_t *h = scf_instance_handle(inst);
    159 	scf_scope_t *scope = NULL;
    160 	scf_service_t *svc = NULL;
    161 	scf_instance_t *tmpinst = NULL;
    162 	scf_snapshot_t *tmpsnap = NULL;
    163 	scf_propertygroup_t *pg = NULL;
    164 	scf_iter_t *iter = NULL;
    165 	scf_property_t *prop = NULL;
    166 	scf_value_t *val = NULL;
    167 	char *spec;
    168 	char *str, *qstr;
    169 	size_t strl;
    170 	int ret;
    171 
    172 	spec = safe_strdup(prop_spec);
    173 
    174 	if (strstr(spec, ":properties") != NULL) {
    175 		const char *scn, *sn, *in, *pgn, *pn;
    176 
    177 		if (scf_parse_svc_fmri(spec, &scn, &sn, &in, &pgn,
    178 		    &pn) != 0)
    179 			goto scferr;
    180 
    181 		if (sn == NULL || pgn == NULL || pn == NULL) {
    182 			free(spec);
    183 			*retstr = safe_strdup("parse error");
    184 			return (-1);
    185 		}
    186 
    187 		if ((scope = scf_scope_create(h)) == NULL ||
    188 		    (svc = scf_service_create(h)) == NULL ||
    189 		    (pg = scf_pg_create(h)) == NULL ||
    190 		    (prop = scf_property_create(h)) == NULL)
    191 			goto scferr;
    192 
    193 		if (scf_handle_get_scope(h, scn == NULL ? SCF_SCOPE_LOCAL : scn,
    194 		    scope) != 0)
    195 			goto properr;
    196 
    197 		if (scf_scope_get_service(scope, sn, svc) != 0)
    198 			goto properr;
    199 
    200 		if (in == NULL) {
    201 			if (scf_service_get_pg(svc, pgn, pg) != 0)
    202 				goto properr;
    203 			if (scf_pg_get_property(pg, pn, prop) != 0)
    204 				goto properr;
    205 		} else {
    206 			if ((tmpinst = scf_instance_create(h)) == NULL)
    207 				goto scferr;
    208 			if (scf_service_get_instance(svc, in, tmpinst) != 0)
    209 				goto properr;
    210 
    211 			tmpsnap = libscf_get_running_snapshot(tmpinst);
    212 			if (tmpsnap == NULL)
    213 				goto scferr;
    214 
    215 			if (get_prop(tmpinst, tmpsnap, pgn, pn, pg, prop) != 0)
    216 				goto properr;
    217 		}
    218 	} else {
    219 		char *slash, *pgn, *pn;
    220 
    221 		/* Try prop or pg/prop in inst. */
    222 
    223 		prop = scf_property_create(h);
    224 		if (prop == NULL)
    225 			goto scferr;
    226 
    227 		pg = scf_pg_create(h);
    228 		if (pg == NULL)
    229 			goto scferr;
    230 
    231 		slash = strchr(spec, '/');
    232 		if (slash == NULL) {
    233 			pgn = "application";
    234 			pn = spec;
    235 		} else {
    236 			*slash = '\0';
    237 			pgn = spec;
    238 			pn = slash + 1;
    239 		}
    240 
    241 		if (get_prop(inst, snap, pgn, pn, pg, prop) != 0)
    242 			goto properr;
    243 	}
    244 
    245 	iter = scf_iter_create(h);
    246 	if (iter == NULL)
    247 		goto scferr;
    248 
    249 
    250 	if (scf_iter_property_values(iter, prop) == -1)
    251 		goto scferr;
    252 
    253 	val = scf_value_create(h);
    254 	if (val == NULL)
    255 		goto scferr;
    256 
    257 	ret = scf_iter_next_value(iter, val);
    258 	if (ret == 0) {
    259 		*retstr = safe_strdup("");
    260 		goto out;
    261 	} else if (ret == -1) {
    262 		goto scferr;
    263 	}
    264 
    265 	str = val_to_str(val);
    266 	if (str == NULL)
    267 		goto err;
    268 
    269 	qstr = quote_for_shell(str);
    270 	free(str);
    271 	str = qstr;
    272 	if (qstr == NULL)
    273 		goto err;
    274 
    275 	strl = strlen(str);
    276 
    277 	while ((ret = scf_iter_next_value(iter, val)) == 1) {
    278 		char *nv, *qnv;
    279 		size_t nl;
    280 		void *p;
    281 
    282 		/* Append sep & val_to_str(val) to str. */
    283 
    284 		nv = val_to_str(val);
    285 		if (nv == NULL) {
    286 			free(str);
    287 			goto err;
    288 		}
    289 		qnv = quote_for_shell(nv);
    290 		free(nv);
    291 		if (qnv == NULL) {
    292 			free(str);
    293 			goto err;
    294 		}
    295 		nv = qnv;
    296 
    297 		nl = strl + 1 + strlen(nv);
    298 		p = realloc(str, nl + 1);
    299 		if (p == NULL) {
    300 			free(str);
    301 			free(nv);
    302 			goto err;
    303 		}
    304 		str = p;
    305 
    306 		str[strl] = sep;
    307 		(void) strcpy(&str[strl + 1], nv);
    308 
    309 		free(nv);
    310 
    311 		strl = nl;
    312 	}
    313 	if (ret == -1) {
    314 		free(str);
    315 		goto scferr;
    316 	}
    317 
    318 	*retstr = str;
    319 
    320 out:
    321 	scf_value_destroy(val);
    322 	scf_iter_destroy(iter);
    323 	scf_property_destroy(prop);
    324 	scf_pg_destroy(pg);
    325 	scf_instance_destroy(tmpinst);
    326 	scf_snapshot_destroy(tmpsnap);
    327 	scf_service_destroy(svc);
    328 	scf_scope_destroy(scope);
    329 	free(spec);
    330 	return (ret);
    331 scferr:
    332 	*retstr = safe_strdup(scf_strerror(scf_error()));
    333 	ret = -1;
    334 	goto out;
    335 properr:
    336 	ret = -1;
    337 	if (scf_error() != SCF_ERROR_NOT_FOUND)
    338 		goto scferr;
    339 	*retstr = uu_msprintf("property \"%s\" not found", prop_spec);
    340 	if (*retstr != NULL)
    341 		goto out;
    342 err:
    343 	*retstr = safe_strdup(strerror(errno));
    344 	ret = -1;
    345 	goto out;
    346 }
    347 
    348 /*
    349  * Interpret the token at the beginning of str (which should be just
    350  * after the escape character), and set *retstr to point at it.  Returns
    351  * the number of characters swallowed.  On error, this returns -1, and
    352  * *retstr is set to an error string.
    353  *
    354  * *retstr should always be freed by the caller.
    355  */
    356 static int
    357 expand_token(const char *str, scf_instance_t *inst, scf_snapshot_t *snap,
    358     int method_type, char **retstr)
    359 {
    360 	scf_handle_t *h = scf_instance_handle(inst);
    361 
    362 	switch (str[0]) {
    363 	case 's': {		/* service */
    364 		scf_service_t *svc;
    365 		char *sname;
    366 		ssize_t sname_len, szret;
    367 		int ret;
    368 
    369 		svc = scf_service_create(h);
    370 		if (svc == NULL) {
    371 			*retstr = safe_strdup(strerror(scf_error()));
    372 			return (-1);
    373 		}
    374 
    375 		ret = scf_instance_get_parent(inst, svc);
    376 		if (ret != 0) {
    377 			int err = scf_error();
    378 			scf_service_destroy(svc);
    379 			*retstr = safe_strdup(scf_strerror(err));
    380 			return (-1);
    381 		}
    382 
    383 		sname_len = scf_service_get_name(svc, NULL, 0);
    384 		if (sname_len < 0) {
    385 			int err = scf_error();
    386 			scf_service_destroy(svc);
    387 			*retstr = safe_strdup(scf_strerror(err));
    388 			return (-1);
    389 		}
    390 
    391 		sname = malloc(sname_len + 1);
    392 		if (sname == NULL) {
    393 			int err = scf_error();
    394 			scf_service_destroy(svc);
    395 			*retstr = safe_strdup(scf_strerror(err));
    396 			return (-1);
    397 		}
    398 
    399 		szret = scf_service_get_name(svc, sname, sname_len + 1);
    400 
    401 		if (szret < 0) {
    402 			int err = scf_error();
    403 			free(sname);
    404 			scf_service_destroy(svc);
    405 			*retstr = safe_strdup(scf_strerror(err));
    406 			return (-1);
    407 		}
    408 
    409 		scf_service_destroy(svc);
    410 		*retstr = sname;
    411 		return (1);
    412 	}
    413 
    414 	case 'i': {	/* instance */
    415 		char *iname;
    416 		ssize_t iname_len, szret;
    417 
    418 		iname_len = scf_instance_get_name(inst, NULL, 0);
    419 		if (iname_len < 0) {
    420 			*retstr = safe_strdup(scf_strerror(scf_error()));
    421 			return (-1);
    422 		}
    423 
    424 		iname = malloc(iname_len + 1);
    425 		if (iname == NULL) {
    426 			*retstr = safe_strdup(strerror(errno));
    427 			return (-1);
    428 		}
    429 
    430 		szret = scf_instance_get_name(inst, iname, iname_len + 1);
    431 		if (szret < 0) {
    432 			free(iname);
    433 			*retstr = safe_strdup(scf_strerror(scf_error()));
    434 			return (-1);
    435 		}
    436 
    437 		*retstr = iname;
    438 		return (1);
    439 	}
    440 
    441 	case 'f': {	/* fmri */
    442 		char *fmri;
    443 		ssize_t fmri_len;
    444 		int ret;
    445 
    446 		fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
    447 		if (fmri_len == -1) {
    448 			*retstr = safe_strdup(scf_strerror(scf_error()));
    449 			return (-1);
    450 		}
    451 
    452 		fmri = malloc(fmri_len + 1);
    453 		if (fmri == NULL) {
    454 			*retstr = safe_strdup(strerror(errno));
    455 			return (-1);
    456 		}
    457 
    458 		ret = scf_instance_to_fmri(inst, fmri, fmri_len + 1);
    459 		if (ret == -1) {
    460 			free(fmri);
    461 			*retstr = safe_strdup(scf_strerror(scf_error()));
    462 			return (-1);
    463 		}
    464 
    465 		*retstr = fmri;
    466 		return (1);
    467 	}
    468 
    469 	case 'm': {	/* method */
    470 		char *str = NULL;
    471 		switch (method_type) {
    472 		case METHOD_START:
    473 			str = "start";
    474 			break;
    475 		case METHOD_STOP:
    476 			str = "stop";
    477 			break;
    478 		case METHOD_REFRESH:
    479 			str = "refresh";
    480 			break;
    481 		default:
    482 			assert(0);
    483 			return (-1);
    484 		}
    485 		*retstr = safe_strdup(str);
    486 		return (1);
    487 	}
    488 
    489 	case 'r':	/* restarter */
    490 		*retstr = safe_strdup("svc.startd");
    491 		return (1);
    492 
    493 	case '{': {
    494 		/* prop_spec[,:]?  See get_prop_val_str() for prop_spec. */
    495 
    496 		char *close;
    497 		size_t len;
    498 		char *buf;
    499 		char sep;
    500 		int ret;
    501 		int skip;
    502 
    503 		close = strchr(str + 1, '}');
    504 		if (close == NULL) {
    505 			*retstr = safe_strdup("parse error");
    506 			return (-1);
    507 		}
    508 
    509 		len = close - (str + 1);	/* between the {}'s */
    510 		skip = len + 2;			/* including the {}'s */
    511 
    512 		/*
    513 		 * If the last character is , or :, use it as the separator.
    514 		 * Otherwise default to space.
    515 		 */
    516 		sep = *(close - 1);
    517 		if (sep == ',' || sep == ':')
    518 			--len;
    519 		else
    520 			sep = ' ';
    521 
    522 		buf = malloc(len + 1);
    523 		if (buf == NULL) {
    524 			*retstr = safe_strdup(strerror(errno));
    525 			return (-1);
    526 		}
    527 
    528 		(void) strlcpy(buf, str + 1, len + 1);
    529 
    530 		ret = get_prop_val_str(inst, snap, buf, sep, retstr);
    531 
    532 		if (ret != 0) {
    533 			free(buf);
    534 			return (-1);
    535 		}
    536 
    537 		free(buf);
    538 		return (skip);
    539 	}
    540 
    541 	default:
    542 		*retstr = safe_strdup("unknown method token");
    543 		return (-1);
    544 	}
    545 }
    546 
    547 /*
    548  * Expand method tokens in the given string, and place the result in
    549  * *retstr.  Tokens begin with the ESCAPE character.  Returns 0 on
    550  * success.  On failure, returns -1 and an error string is placed in
    551  * *retstr.  Caller should free *retstr.
    552  */
    553 #define	ESCAPE	'%'
    554 
    555 int
    556 expand_method_tokens(const char *str, scf_instance_t *inst,
    557     scf_snapshot_t *snap, int method_type, char **retstr)
    558 {
    559 	char *expanded;
    560 	size_t exp_sz;
    561 	const char *sp;
    562 	int ei;
    563 
    564 	if (scf_instance_handle(inst) == NULL) {
    565 		*retstr = safe_strdup(scf_strerror(scf_error()));
    566 		return (-1);
    567 	}
    568 
    569 	exp_sz = strlen(str) + 1;
    570 	expanded = malloc(exp_sz);
    571 	if (expanded == NULL) {
    572 		*retstr = safe_strdup(strerror(errno));
    573 		return (-1);
    574 	}
    575 
    576 	/*
    577 	 * Copy str into expanded, expanding %-tokens & realloc()ing as we go.
    578 	 */
    579 
    580 	sp = str;
    581 	ei = 0;
    582 
    583 	for (;;) {
    584 		char *esc;
    585 		size_t len;
    586 
    587 		esc = strchr(sp, ESCAPE);
    588 		if (esc == NULL) {
    589 			(void) strcpy(expanded + ei, sp);
    590 			*retstr = expanded;
    591 			return (0);
    592 		}
    593 
    594 		/* Copy up to the escape character. */
    595 		len = esc - sp;
    596 
    597 		(void) strncpy(expanded + ei, sp, len);
    598 
    599 		sp += len;
    600 		ei += len;
    601 
    602 		if (sp[1] == '\0') {
    603 			expanded[ei] = '\0';
    604 			*retstr = expanded;
    605 			return (0);
    606 		}
    607 
    608 		if (sp[1] == ESCAPE) {
    609 			expanded[ei] = ESCAPE;
    610 
    611 			sp += 2;
    612 			ei++;
    613 		} else {
    614 			char *tokval;
    615 			int skip;
    616 			char *p;
    617 
    618 			skip = expand_token(sp + 1, inst, snap,
    619 			    method_type, &tokval);
    620 			if (skip == -1) {
    621 				free(expanded);
    622 				*retstr = tokval;
    623 				return (-1);
    624 			}
    625 
    626 			len = strlen(tokval);
    627 			exp_sz += len;
    628 			p = realloc(expanded, exp_sz);
    629 			if (p == NULL) {
    630 				*retstr = safe_strdup(strerror(errno));
    631 				free(expanded);
    632 				free(tokval);
    633 				return (-1);
    634 			}
    635 			expanded = p;
    636 
    637 			(void) strcpy(expanded + ei, tokval);
    638 			sp += 1 + skip;
    639 			ei += len;
    640 
    641 			free(tokval);
    642 		}
    643 	}
    644 
    645 	/* NOTREACHED */
    646 }
    647