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