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