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 #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>
35
36 #include "startd.h"
37
38 /*
39 * Return an allocated copy of str, with the Bourne shell's metacharacters
40 * escaped by '\'. Returns NULL on (allocation) failure.
41 */
42 static char *
quote_for_shell(const char * str)43 quote_for_shell(const char *str)
44 {
45 const char *sp;
46 char *dst, *dp;
47 size_t dst_len;
48
49 const char * const metachars = ";&()|^<>\n \t\\\"\'`";
50
51 dst_len = 0;
52 for (sp = str; *sp != '\0'; ++sp) {
53 ++dst_len;
54
55 if (strchr(metachars, *sp) != NULL)
56 ++dst_len;
57 }
58
59 if (sp - str == dst_len)
60 return (safe_strdup(str));
61
62 dst = malloc(dst_len + 1);
63 if (dst == NULL)
64 return (NULL);
65
66 for (dp = dst, sp = str; *sp != '\0'; ++dp, ++sp) {
67 if (strchr(metachars, *sp) != NULL)
68 *dp++ = '\\';
69
70 *dp = *sp;
71 }
72 *dp = '\0';
73
74 return (dst);
75 }
76
77 /*
78 * Return an allocated string representation of the value v.
79 * Return NULL on error.
80 */
81 static char *
val_to_str(scf_value_t * v)82 val_to_str(scf_value_t *v)
83 {
84 char *buf;
85 ssize_t buflen, ret;
86
87 buflen = scf_value_get_as_string(v, NULL, 0);
88 assert(buflen >= 0);
89
90 buf = malloc(buflen + 1);
91 if (buf == NULL)
92 return (NULL);
93
94 ret = scf_value_get_as_string(v, buf, buflen + 1);
95 assert(ret == buflen);
96
97 return (buf);
98 }
99
100 /*
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.
103 */
104 static int
get_prop(const scf_instance_t * inst,scf_snapshot_t * snap,const char * pgn,const char * pn,scf_propertygroup_t * pg,scf_property_t * prop)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)
108 {
109 int ret;
110
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());
118 }
119
120 if (scf_pg_get_property(pg, pn, prop) == 0)
121 return (0);
122
123 if (snap == NULL)
124 return (scf_error());
125
126 ret = scf_instance_get_pg_composed(inst, NULL, pgn, pg);
127 if (ret != 0)
128 return (scf_error());
129
130 if (scf_pg_get_property(pg, pn, prop) == 0)
131 return (0);
132
133 return (scf_error());
134 }
135
136 /*
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.
146 *
147 * On error, non-zero is returned, and *retstr is set to an error
148 * string.
149 *
150 * *retstr should always be freed by the caller.
151 */
152 static int
get_prop_val_str(const scf_instance_t * inst,scf_snapshot_t * snap,const char * prop_spec,char sep,char ** retstr)153 get_prop_val_str(const scf_instance_t *inst, scf_snapshot_t *snap,
154 const char *prop_spec, char sep, char **retstr)
155 {
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;
169
170 spec = safe_strdup(prop_spec);
171
172 if (strstr(spec, ":properties") != NULL) {
173 const char *scn, *sn, *in, *pgn, *pn;
174
175 if (scf_parse_svc_fmri(spec, &scn, &sn, &in, &pgn,
176 &pn) != 0)
177 goto scferr;
178
179 if (sn == NULL || pgn == NULL || pn == NULL) {
180 free(spec);
181 *retstr = safe_strdup("parse error");
182 return (-1);
183 }
184
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;
190
191 if (scf_handle_get_scope(h, scn == NULL ? SCF_SCOPE_LOCAL : scn,
192 scope) != 0)
193 goto properr;
194
195 if (scf_scope_get_service(scope, sn, svc) != 0)
196 goto properr;
197
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;
208
209 tmpsnap = libscf_get_running_snapshot(tmpinst);
210 if (tmpsnap == NULL)
211 goto scferr;
212
213 if (get_prop(tmpinst, tmpsnap, pgn, pn, pg, prop) != 0)
214 goto properr;
215 }
216 } else {
217 char *slash, *pgn, *pn;
218
219 /* Try prop or pg/prop in inst. */
220
221 prop = scf_property_create(h);
222 if (prop == NULL)
223 goto scferr;
224
225 pg = scf_pg_create(h);
226 if (pg == NULL)
227 goto scferr;
228
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;
237 }
238
239 if (get_prop(inst, snap, pgn, pn, pg, prop) != 0)
240 goto properr;
241 }
242
243 iter = scf_iter_create(h);
244 if (iter == NULL)
245 goto scferr;
246
247
248 if (scf_iter_property_values(iter, prop) == -1)
249 goto scferr;
250
251 val = scf_value_create(h);
252 if (val == NULL)
253 goto scferr;
254
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;
261 }
262
263 str = val_to_str(val);
264 if (str == NULL)
265 goto err;
266
267 qstr = quote_for_shell(str);
268 free(str);
269 str = qstr;
270 if (qstr == NULL)
271 goto err;
272
273 strl = strlen(str);
274
275 while ((ret = scf_iter_next_value(iter, val)) == 1) {
276 char *nv, *qnv;
277 size_t nl;
278 void *p;
279
280 /* Append sep & val_to_str(val) to str. */
281
282 nv = val_to_str(val);
283 if (nv == NULL) {
284 free(str);
285 goto err;
286 }
287 qnv = quote_for_shell(nv);
288 free(nv);
289 if (qnv == NULL) {
290 free(str);
291 goto err;
292 }
293 nv = qnv;
294
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;
301 }
302 str = p;
303
304 str[strl] = sep;
305 (void) strcpy(&str[strl + 1], nv);
306
307 free(nv);
308
309 strl = nl;
310 }
311 if (ret == -1) {
312 free(str);
313 goto scferr;
314 }
315
316 *retstr = str;
317
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;
344 }
345
346 /*
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.
351 *
352 * *retstr should always be freed by the caller.
353 */
354 static int
expand_token(const char * str,scf_instance_t * inst,scf_snapshot_t * snap,int method_type,char ** retstr)355 expand_token(const char *str, scf_instance_t *inst, scf_snapshot_t *snap,
356 int method_type, char **retstr)
357 {
358 scf_handle_t *h = scf_instance_handle(inst);
359
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;
366
367 svc = scf_service_create(h);
368 if (svc == NULL) {
369 *retstr = safe_strdup(strerror(scf_error()));
370 return (-1);
371 }
372
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);
379 }
380
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);
387 }
388
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);
395 }
396
397 szret = scf_service_get_name(svc, sname, sname_len + 1);
398
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);
405 }
406
407 scf_service_destroy(svc);
408 *retstr = sname;
409 return (1);
410 }
411
412 case 'i': { /* instance */
413 char *iname;
414 ssize_t iname_len, szret;
415
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);
420 }
421
422 iname = malloc(iname_len + 1);
423 if (iname == NULL) {
424 *retstr = safe_strdup(strerror(errno));
425 return (-1);
426 }
427
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);
433 }
434
435 *retstr = iname;
436 return (1);
437 }
438
439 case 'f': { /* fmri */
440 char *fmri;
441 ssize_t fmri_len;
442 int ret;
443
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);
448 }
449
450 fmri = malloc(fmri_len + 1);
451 if (fmri == NULL) {
452 *retstr = safe_strdup(strerror(errno));
453 return (-1);
454 }
455
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);
461 }
462
463 *retstr = fmri;
464 return (1);
465 }
466
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);
482 }
483 *retstr = safe_strdup(str);
484 return (1);
485 }
486
487 case 'r': /* restarter */
488 *retstr = safe_strdup("svc.startd");
489 return (1);
490
491 case '{': {
492 /* prop_spec[,:]? See get_prop_val_str() for prop_spec. */
493
494 char *close;
495 size_t len;
496 char *buf;
497 char sep;
498 int ret;
499 int skip;
500
501 close = strchr(str + 1, '}');
502 if (close == NULL) {
503 *retstr = safe_strdup("parse error");
504 return (-1);
505 }
506
507 len = close - (str + 1); /* between the {}'s */
508 skip = len + 2; /* including the {}'s */
509
510 /*
511 * If the last character is , or :, use it as the separator.
512 * Otherwise default to space.
513 */
514 sep = *(close - 1);
515 if (sep == ',' || sep == ':')
516 --len;
517 else
518 sep = ' ';
519
520 buf = malloc(len + 1);
521 if (buf == NULL) {
522 *retstr = safe_strdup(strerror(errno));
523 return (-1);
524 }
525
526 (void) strlcpy(buf, str + 1, len + 1);
527
528 ret = get_prop_val_str(inst, snap, buf, sep, retstr);
529
530 if (ret != 0) {
531 free(buf);
532 return (-1);
533 }
534
535 free(buf);
536 return (skip);
537 }
538
539 default:
540 *retstr = safe_strdup("unknown method token");
541 return (-1);
542 }
543 }
544
545 /*
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.
550 */
551 #define ESCAPE '%'
552
553 int
expand_method_tokens(const char * str,scf_instance_t * inst,scf_snapshot_t * snap,int method_type,char ** retstr)554 expand_method_tokens(const char *str, scf_instance_t *inst,
555 scf_snapshot_t *snap, int method_type, char **retstr)
556 {
557 char *expanded;
558 size_t exp_sz;
559 const char *sp;
560 int ei;
561
562 if (scf_instance_handle(inst) == NULL) {
563 *retstr = safe_strdup(scf_strerror(scf_error()));
564 return (-1);
565 }
566
567 exp_sz = strlen(str) + 1;
568 expanded = malloc(exp_sz);
569 if (expanded == NULL) {
570 *retstr = safe_strdup(strerror(errno));
571 return (-1);
572 }
573
574 /*
575 * Copy str into expanded, expanding %-tokens & realloc()ing as we go.
576 */
577
578 sp = str;
579 ei = 0;
580
581 for (;;) {
582 char *esc;
583 size_t len;
584
585 esc = strchr(sp, ESCAPE);
586 if (esc == NULL) {
587 (void) strcpy(expanded + ei, sp);
588 *retstr = expanded;
589 return (0);
590 }
591
592 /* Copy up to the escape character. */
593 len = esc - sp;
594
595 (void) strncpy(expanded + ei, sp, len);
596
597 sp += len;
598 ei += len;
599
600 if (sp[1] == '\0') {
601 expanded[ei] = '\0';
602 *retstr = expanded;
603 return (0);
604 }
605
606 if (sp[1] == ESCAPE) {
607 expanded[ei] = ESCAPE;
608
609 sp += 2;
610 ei++;
611 } else {
612 char *tokval;
613 int skip;
614 char *p;
615
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);
622 }
623
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);
632 }
633 expanded = p;
634
635 (void) strcpy(expanded + ei, tokval);
636 sp += 1 + skip;
637 ei += len;
638
639 free(tokval);
640 }
641 }
642
643 /* NOTREACHED */
644 }
645