16786 usba: possible memory leak
[illumos-gate.git] / usr / src / cmd / svc / startd / expand.c
blob0716a4e6a90f2dfb6d91ff94a2159b204d63c530
1 /*
2 * CDDL HEADER START
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.
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]
20 * CDDL HEADER END
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <assert.h>
28 #include <libscf.h>
29 #include <libscf_priv.h>
30 #include <libuutil.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <strings.h>
34 #include <errno.h>
36 #include "startd.h"
39 * Return an allocated copy of str, with the Bourne shell's metacharacters
40 * escaped by '\'. Returns NULL on (allocation) failure.
42 static char *
43 quote_for_shell(const char *str)
45 const char *sp;
46 char *dst, *dp;
47 size_t dst_len;
49 const char * const metachars = ";&()|^<>\n \t\\\"\'`";
51 dst_len = 0;
52 for (sp = str; *sp != '\0'; ++sp) {
53 ++dst_len;
55 if (strchr(metachars, *sp) != NULL)
56 ++dst_len;
59 if (sp - str == dst_len)
60 return (safe_strdup(str));
62 dst = malloc(dst_len + 1);
63 if (dst == NULL)
64 return (NULL);
66 for (dp = dst, sp = str; *sp != '\0'; ++dp, ++sp) {
67 if (strchr(metachars, *sp) != NULL)
68 *dp++ = '\\';
70 *dp = *sp;
72 *dp = '\0';
74 return (dst);
78 * Return an allocated string representation of the value v.
79 * Return NULL on error.
81 static char *
82 val_to_str(scf_value_t *v)
84 char *buf;
85 ssize_t buflen, ret;
87 buflen = scf_value_get_as_string(v, NULL, 0);
88 assert(buflen >= 0);
90 buf = malloc(buflen + 1);
91 if (buf == NULL)
92 return (NULL);
94 ret = scf_value_get_as_string(v, buf, buflen + 1);
95 assert(ret == buflen);
97 return (buf);
101 * Look up a property in the given snapshot, or the editing one
102 * if not found. Returns scf_error() on failure, or 0 otherwise.
104 static int
105 get_prop(const scf_instance_t *inst, scf_snapshot_t *snap,
106 const char *pgn, const char *pn, scf_propertygroup_t *pg,
107 scf_property_t *prop)
109 int ret;
111 ret = scf_instance_get_pg_composed(inst, snap, pgn, pg);
112 if (ret != 0) {
113 snap = NULL;
114 if (scf_error() == SCF_ERROR_NOT_FOUND)
115 ret = scf_instance_get_pg_composed(inst, snap, pgn, pg);
116 if (ret != 0)
117 return (scf_error());
120 if (scf_pg_get_property(pg, pn, prop) == 0)
121 return (0);
123 if (snap == NULL)
124 return (scf_error());
126 ret = scf_instance_get_pg_composed(inst, NULL, pgn, pg);
127 if (ret != 0)
128 return (scf_error());
130 if (scf_pg_get_property(pg, pn, prop) == 0)
131 return (0);
133 return (scf_error());
137 * Get an allocated string representation of the values of the property
138 * specified by inst & prop_spec and store it in *retstr. prop_spec may
139 * be a full property FMRI, or a "property-group/property" pair relative
140 * to inst, or the name of a property in inst's "application" property
141 * group. In the latter two cases, the property is looked up in inst's
142 * snap snapshot. In the first case, the target instance's running
143 * snapshot will be used. In any case, if the property or its group
144 * can't be found, the "editing" snapshot will be checked. Multiple
145 * values will be separated by sep.
147 * On error, non-zero is returned, and *retstr is set to an error
148 * string.
150 * *retstr should always be freed by the caller.
152 static int
153 get_prop_val_str(const scf_instance_t *inst, scf_snapshot_t *snap,
154 const char *prop_spec, char sep, char **retstr)
156 scf_handle_t *h = scf_instance_handle(inst);
157 scf_scope_t *scope = NULL;
158 scf_service_t *svc = NULL;
159 scf_instance_t *tmpinst = NULL;
160 scf_snapshot_t *tmpsnap = NULL;
161 scf_propertygroup_t *pg = NULL;
162 scf_iter_t *iter = NULL;
163 scf_property_t *prop = NULL;
164 scf_value_t *val = NULL;
165 char *spec;
166 char *str, *qstr;
167 size_t strl;
168 int ret;
170 spec = safe_strdup(prop_spec);
172 if (strstr(spec, ":properties") != NULL) {
173 const char *scn, *sn, *in, *pgn, *pn;
175 if (scf_parse_svc_fmri(spec, &scn, &sn, &in, &pgn,
176 &pn) != 0)
177 goto scferr;
179 if (sn == NULL || pgn == NULL || pn == NULL) {
180 free(spec);
181 *retstr = safe_strdup("parse error");
182 return (-1);
185 if ((scope = scf_scope_create(h)) == NULL ||
186 (svc = scf_service_create(h)) == NULL ||
187 (pg = scf_pg_create(h)) == NULL ||
188 (prop = scf_property_create(h)) == NULL)
189 goto scferr;
191 if (scf_handle_get_scope(h, scn == NULL ? SCF_SCOPE_LOCAL : scn,
192 scope) != 0)
193 goto properr;
195 if (scf_scope_get_service(scope, sn, svc) != 0)
196 goto properr;
198 if (in == NULL) {
199 if (scf_service_get_pg(svc, pgn, pg) != 0)
200 goto properr;
201 if (scf_pg_get_property(pg, pn, prop) != 0)
202 goto properr;
203 } else {
204 if ((tmpinst = scf_instance_create(h)) == NULL)
205 goto scferr;
206 if (scf_service_get_instance(svc, in, tmpinst) != 0)
207 goto properr;
209 tmpsnap = libscf_get_running_snapshot(tmpinst);
210 if (tmpsnap == NULL)
211 goto scferr;
213 if (get_prop(tmpinst, tmpsnap, pgn, pn, pg, prop) != 0)
214 goto properr;
216 } else {
217 char *slash, *pgn, *pn;
219 /* Try prop or pg/prop in inst. */
221 prop = scf_property_create(h);
222 if (prop == NULL)
223 goto scferr;
225 pg = scf_pg_create(h);
226 if (pg == NULL)
227 goto scferr;
229 slash = strchr(spec, '/');
230 if (slash == NULL) {
231 pgn = "application";
232 pn = spec;
233 } else {
234 *slash = '\0';
235 pgn = spec;
236 pn = slash + 1;
239 if (get_prop(inst, snap, pgn, pn, pg, prop) != 0)
240 goto properr;
243 iter = scf_iter_create(h);
244 if (iter == NULL)
245 goto scferr;
248 if (scf_iter_property_values(iter, prop) == -1)
249 goto scferr;
251 val = scf_value_create(h);
252 if (val == NULL)
253 goto scferr;
255 ret = scf_iter_next_value(iter, val);
256 if (ret == 0) {
257 *retstr = safe_strdup("");
258 goto out;
259 } else if (ret == -1) {
260 goto scferr;
263 str = val_to_str(val);
264 if (str == NULL)
265 goto err;
267 qstr = quote_for_shell(str);
268 free(str);
269 str = qstr;
270 if (qstr == NULL)
271 goto err;
273 strl = strlen(str);
275 while ((ret = scf_iter_next_value(iter, val)) == 1) {
276 char *nv, *qnv;
277 size_t nl;
278 void *p;
280 /* Append sep & val_to_str(val) to str. */
282 nv = val_to_str(val);
283 if (nv == NULL) {
284 free(str);
285 goto err;
287 qnv = quote_for_shell(nv);
288 free(nv);
289 if (qnv == NULL) {
290 free(str);
291 goto err;
293 nv = qnv;
295 nl = strl + 1 + strlen(nv);
296 p = realloc(str, nl + 1);
297 if (p == NULL) {
298 free(str);
299 free(nv);
300 goto err;
302 str = p;
304 str[strl] = sep;
305 (void) strcpy(&str[strl + 1], nv);
307 free(nv);
309 strl = nl;
311 if (ret == -1) {
312 free(str);
313 goto scferr;
316 *retstr = str;
318 out:
319 scf_value_destroy(val);
320 scf_iter_destroy(iter);
321 scf_property_destroy(prop);
322 scf_pg_destroy(pg);
323 scf_instance_destroy(tmpinst);
324 scf_snapshot_destroy(tmpsnap);
325 scf_service_destroy(svc);
326 scf_scope_destroy(scope);
327 free(spec);
328 return (ret);
329 scferr:
330 *retstr = safe_strdup(scf_strerror(scf_error()));
331 ret = -1;
332 goto out;
333 properr:
334 ret = -1;
335 if (scf_error() != SCF_ERROR_NOT_FOUND)
336 goto scferr;
337 *retstr = uu_msprintf("property \"%s\" not found", prop_spec);
338 if (*retstr != NULL)
339 goto out;
340 err:
341 *retstr = safe_strdup(strerror(errno));
342 ret = -1;
343 goto out;
347 * Interpret the token at the beginning of str (which should be just
348 * after the escape character), and set *retstr to point at it. Returns
349 * the number of characters swallowed. On error, this returns -1, and
350 * *retstr is set to an error string.
352 * *retstr should always be freed by the caller.
354 static int
355 expand_token(const char *str, scf_instance_t *inst, scf_snapshot_t *snap,
356 int method_type, char **retstr)
358 scf_handle_t *h = scf_instance_handle(inst);
360 switch (str[0]) {
361 case 's': { /* service */
362 scf_service_t *svc;
363 char *sname;
364 ssize_t sname_len, szret;
365 int ret;
367 svc = scf_service_create(h);
368 if (svc == NULL) {
369 *retstr = safe_strdup(strerror(scf_error()));
370 return (-1);
373 ret = scf_instance_get_parent(inst, svc);
374 if (ret != 0) {
375 int err = scf_error();
376 scf_service_destroy(svc);
377 *retstr = safe_strdup(scf_strerror(err));
378 return (-1);
381 sname_len = scf_service_get_name(svc, NULL, 0);
382 if (sname_len < 0) {
383 int err = scf_error();
384 scf_service_destroy(svc);
385 *retstr = safe_strdup(scf_strerror(err));
386 return (-1);
389 sname = malloc(sname_len + 1);
390 if (sname == NULL) {
391 int err = scf_error();
392 scf_service_destroy(svc);
393 *retstr = safe_strdup(scf_strerror(err));
394 return (-1);
397 szret = scf_service_get_name(svc, sname, sname_len + 1);
399 if (szret < 0) {
400 int err = scf_error();
401 free(sname);
402 scf_service_destroy(svc);
403 *retstr = safe_strdup(scf_strerror(err));
404 return (-1);
407 scf_service_destroy(svc);
408 *retstr = sname;
409 return (1);
412 case 'i': { /* instance */
413 char *iname;
414 ssize_t iname_len, szret;
416 iname_len = scf_instance_get_name(inst, NULL, 0);
417 if (iname_len < 0) {
418 *retstr = safe_strdup(scf_strerror(scf_error()));
419 return (-1);
422 iname = malloc(iname_len + 1);
423 if (iname == NULL) {
424 *retstr = safe_strdup(strerror(errno));
425 return (-1);
428 szret = scf_instance_get_name(inst, iname, iname_len + 1);
429 if (szret < 0) {
430 free(iname);
431 *retstr = safe_strdup(scf_strerror(scf_error()));
432 return (-1);
435 *retstr = iname;
436 return (1);
439 case 'f': { /* fmri */
440 char *fmri;
441 ssize_t fmri_len;
442 int ret;
444 fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
445 if (fmri_len == -1) {
446 *retstr = safe_strdup(scf_strerror(scf_error()));
447 return (-1);
450 fmri = malloc(fmri_len + 1);
451 if (fmri == NULL) {
452 *retstr = safe_strdup(strerror(errno));
453 return (-1);
456 ret = scf_instance_to_fmri(inst, fmri, fmri_len + 1);
457 if (ret == -1) {
458 free(fmri);
459 *retstr = safe_strdup(scf_strerror(scf_error()));
460 return (-1);
463 *retstr = fmri;
464 return (1);
467 case 'm': { /* method */
468 char *str = NULL;
469 switch (method_type) {
470 case METHOD_START:
471 str = "start";
472 break;
473 case METHOD_STOP:
474 str = "stop";
475 break;
476 case METHOD_REFRESH:
477 str = "refresh";
478 break;
479 default:
480 assert(0);
481 return (-1);
483 *retstr = safe_strdup(str);
484 return (1);
487 case 'r': /* restarter */
488 *retstr = safe_strdup("svc.startd");
489 return (1);
491 case '{': {
492 /* prop_spec[,:]? See get_prop_val_str() for prop_spec. */
494 char *close;
495 size_t len;
496 char *buf;
497 char sep;
498 int ret;
499 int skip;
501 close = strchr(str + 1, '}');
502 if (close == NULL) {
503 *retstr = safe_strdup("parse error");
504 return (-1);
507 len = close - (str + 1); /* between the {}'s */
508 skip = len + 2; /* including the {}'s */
511 * If the last character is , or :, use it as the separator.
512 * Otherwise default to space.
514 sep = *(close - 1);
515 if (sep == ',' || sep == ':')
516 --len;
517 else
518 sep = ' ';
520 buf = malloc(len + 1);
521 if (buf == NULL) {
522 *retstr = safe_strdup(strerror(errno));
523 return (-1);
526 (void) strlcpy(buf, str + 1, len + 1);
528 ret = get_prop_val_str(inst, snap, buf, sep, retstr);
530 if (ret != 0) {
531 free(buf);
532 return (-1);
535 free(buf);
536 return (skip);
539 default:
540 *retstr = safe_strdup("unknown method token");
541 return (-1);
546 * Expand method tokens in the given string, and place the result in
547 * *retstr. Tokens begin with the ESCAPE character. Returns 0 on
548 * success. On failure, returns -1 and an error string is placed in
549 * *retstr. Caller should free *retstr.
551 #define ESCAPE '%'
554 expand_method_tokens(const char *str, scf_instance_t *inst,
555 scf_snapshot_t *snap, int method_type, char **retstr)
557 char *expanded;
558 size_t exp_sz;
559 const char *sp;
560 int ei;
562 if (scf_instance_handle(inst) == NULL) {
563 *retstr = safe_strdup(scf_strerror(scf_error()));
564 return (-1);
567 exp_sz = strlen(str) + 1;
568 expanded = malloc(exp_sz);
569 if (expanded == NULL) {
570 *retstr = safe_strdup(strerror(errno));
571 return (-1);
575 * Copy str into expanded, expanding %-tokens & realloc()ing as we go.
578 sp = str;
579 ei = 0;
581 for (;;) {
582 char *esc;
583 size_t len;
585 esc = strchr(sp, ESCAPE);
586 if (esc == NULL) {
587 (void) strcpy(expanded + ei, sp);
588 *retstr = expanded;
589 return (0);
592 /* Copy up to the escape character. */
593 len = esc - sp;
595 (void) strncpy(expanded + ei, sp, len);
597 sp += len;
598 ei += len;
600 if (sp[1] == '\0') {
601 expanded[ei] = '\0';
602 *retstr = expanded;
603 return (0);
606 if (sp[1] == ESCAPE) {
607 expanded[ei] = ESCAPE;
609 sp += 2;
610 ei++;
611 } else {
612 char *tokval;
613 int skip;
614 char *p;
616 skip = expand_token(sp + 1, inst, snap,
617 method_type, &tokval);
618 if (skip == -1) {
619 free(expanded);
620 *retstr = tokval;
621 return (-1);
624 len = strlen(tokval);
625 exp_sz += len;
626 p = realloc(expanded, exp_sz);
627 if (p == NULL) {
628 *retstr = safe_strdup(strerror(errno));
629 free(expanded);
630 free(tokval);
631 return (-1);
633 expanded = p;
635 (void) strcpy(expanded + ei, tokval);
636 sp += 1 + skip;
637 ei += len;
639 free(tokval);
643 /* NOTREACHED */