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 *
quote_for_shell(const char * str)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 *
val_to_str(scf_value_t * v)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
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)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
get_prop_val_str(const scf_instance_t * inst,scf_snapshot_t * snap,const char * prop_spec,char sep,char ** retstr)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
expand_token(const char * str,scf_instance_t * inst,scf_snapshot_t * snap,int method_type,char ** retstr)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
expand_method_tokens(const char * str,scf_instance_t * inst,scf_snapshot_t * snap,int method_type,char ** retstr)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