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 * 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 * 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 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 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 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 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