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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Copyright 2019 Joyent, Inc.
  29  */
  30 
  31 /*
  32  * svcprop - report service configuration properties
  33  */
  34 
  35 #include <locale.h>
  36 #include <libintl.h>
  37 #include <libscf.h>
  38 #include <libscf_priv.h>
  39 #include <libuutil.h>
  40 #include <stddef.h>
  41 #include <stdio.h>
  42 #include <stdlib.h>
  43 #include <unistd.h>
  44 #include <strings.h>
  45 #include <assert.h>
  46 #include <zone.h>
  47 
  48 #ifndef TEXT_DOMAIN
  49 #define TEXT_DOMAIN     "SUNW_OST_OSCMD"
  50 #endif /* TEXT_DOMAIN */
  51 
  52 /*
  53  * Error functions.  These can change if the quiet (-q) option is used.
  54  */
  55 static void (*warn)(const char *, ...) = uu_warn;
  56 static __NORETURN void (*die)(const char *, ...) = uu_die;
  57 
  58 /*
  59  * Entity encapsulation.  This allows me to treat services and instances
  60  * similarly, and avoid duplicating process_ent().
  61  */
  62 typedef struct {
  63         char type;                      /* !=0: service, 0: instance */
  64         union {
  65                 scf_service_t *svc;
  66                 scf_instance_t *inst;
  67         } u;
  68 } scf_entityp_t;
  69 
  70 #define ENT_INSTANCE    0
  71 
  72 #define SCF_ENTITY_SET_TO_SERVICE(ent, s)       { ent.type = 1; ent.u.svc = s; }
  73 
  74 #define SCF_ENTITY_SET_TO_INSTANCE(ent, i)      \
  75         { ent.type = ENT_INSTANCE; ent.u.inst = i; }
  76 
  77 #define scf_entity_get_pg(ent, name, pg) \
  78         (ent.type ? scf_service_get_pg(ent.u.svc, name, pg) : \
  79         scf_instance_get_pg(ent.u.inst, name, pg))
  80 
  81 #define scf_entity_to_fmri(ent, buf, buf_sz) \
  82         (ent.type ? scf_service_to_fmri(ent.u.svc, buf, buf_sz) : \
  83         scf_instance_to_fmri(ent.u.inst, buf, buf_sz))
  84 
  85 #define SCF_ENTITY_TYPE_NAME(ent)       (ent.type ? "service" : "instance")
  86 
  87 /*
  88  * Data structure for -p arguments.  Since they may be name or name/name, we
  89  * just track the components.
  90  */
  91 typedef struct svcprop_prop_node {
  92         uu_list_node_t  spn_list_node;
  93         const char      *spn_comp1;
  94         const char      *spn_comp2;
  95 } svcprop_prop_node_t;
  96 
  97 static uu_list_pool_t   *prop_pool;
  98 static uu_list_t        *prop_list;
  99 
 100 static scf_handle_t *hndl;
 101 static ssize_t max_scf_name_length;
 102 static ssize_t max_scf_value_length;
 103 static ssize_t max_scf_fmri_length;
 104 
 105 /* Options */
 106 static int quiet = 0;                   /* No output. Nothing found, exit(1) */
 107 static int types = 0;                   /* Display types of properties. */
 108 static int verbose = 0;                 /* Print not found errors to stderr. */
 109 static int fmris = 0;                   /* Display full FMRIs for properties. */
 110 static int wait = 0;                    /* Wait mode. */
 111 static char *snapshot = "running";      /* Snapshot to use. */
 112 static int Cflag = 0;                   /* C option supplied */
 113 static int cflag = 0;                   /* c option supplied */
 114 static int sflag = 0;                   /* s option supplied */
 115 static int return_code;                 /* main's return code */
 116 
 117 #define PRINT_NOPROP_ERRORS     (!quiet || verbose)
 118 
 119 /*
 120  * For unexpected libscf errors.  The ending newline is necessary to keep
 121  * uu_die() from appending the errno error.
 122  */
 123 static void
 124 scfdie(void)
 125 {
 126         die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
 127             scf_strerror(scf_error()));
 128 }
 129 
 130 static void *
 131 safe_malloc(size_t sz)
 132 {
 133         void *p;
 134 
 135         p = malloc(sz);
 136         if (p == NULL)
 137                 die(gettext("Could not allocate memory"));
 138 
 139         return (p);
 140 }
 141 
 142 static void
 143 usage(void)
 144 {
 145         (void) fprintf(stderr, gettext("Usage: %1$s [-fqtv] "
 146             "[-C | -c | -s snapshot] [-z zone] "
 147             "[-p [name/]name]... \n"
 148             "         {FMRI | pattern}...\n"
 149             "       %1$s -w [-fqtv] [-z zone] [-p [name/]name] "
 150             "{FMRI | pattern}\n"), uu_getpname());
 151         exit(UU_EXIT_USAGE);
 152 }
 153 
 154 /*
 155  * Return an allocated copy of str, with the Bourne shell's metacharacters
 156  * escaped by '\'.
 157  *
 158  * What about unicode?
 159  */
 160 static char *
 161 quote_for_shell(const char *str)
 162 {
 163         const char *sp;
 164         char *dst, *dp;
 165         size_t dst_len;
 166 
 167         const char * const metachars = ";&()|^<>\n \t\\\"\'`";
 168 
 169         if (str[0] == '\0')
 170                 return (strdup("\"\""));
 171 
 172         dst_len = 0;
 173         for (sp = str; *sp != '\0'; ++sp) {
 174                 ++dst_len;
 175 
 176                 if (strchr(metachars, *sp) != NULL)
 177                         ++dst_len;
 178         }
 179 
 180         if (sp - str == dst_len)
 181                 return (strdup(str));
 182 
 183         dst = safe_malloc(dst_len + 1);
 184 
 185         for (dp = dst, sp = str; *sp != '\0'; ++dp, ++sp) {
 186                 if (strchr(metachars, *sp) != NULL)
 187                         *dp++ = '\\';
 188 
 189                 *dp = *sp;
 190         }
 191         *dp = '\0';
 192 
 193         return (dst);
 194 }
 195 
 196 static void
 197 print_value(scf_value_t *val)
 198 {
 199         char *buf, *qbuf;
 200         ssize_t bufsz, r;
 201 
 202         bufsz = scf_value_get_as_string(val, NULL, 0) + 1;
 203         if (bufsz - 1 < 0)
 204                 scfdie();
 205 
 206         buf = safe_malloc(bufsz);
 207 
 208         r = scf_value_get_as_string(val, buf, bufsz);
 209         assert(r + 1 == bufsz);
 210 
 211         qbuf = quote_for_shell(buf);
 212         (void) fputs(qbuf, stdout);
 213 
 214         free(qbuf);
 215         free(buf);
 216 }
 217 
 218 /*
 219  * Display a property's values on a line.  If types is true, prepend
 220  * identification (the FMRI if fmris is true, pg/prop otherwise) and the type
 221  * of the property.
 222  */
 223 static void
 224 display_prop(scf_propertygroup_t *pg, scf_property_t *prop)
 225 {
 226         scf_value_t *val;
 227         scf_iter_t *iter;
 228         int ret, first, err;
 229 
 230         const char * const permission_denied_emsg =
 231             gettext("Permission denied.\n");
 232 
 233         if (types) {
 234                 scf_type_t ty;
 235                 char *buf;
 236                 size_t buf_sz;
 237 
 238                 if (fmris) {
 239                         buf_sz = max_scf_fmri_length + 1;
 240                         buf = safe_malloc(buf_sz);
 241 
 242                         if (scf_property_to_fmri(prop, buf, buf_sz) == -1)
 243                                 scfdie();
 244                         (void) fputs(buf, stdout);
 245 
 246                         free(buf);
 247                 } else {
 248                         buf_sz = max_scf_name_length + 1;
 249                         buf = safe_malloc(buf_sz);
 250 
 251                         if (scf_pg_get_name(pg, buf, buf_sz) < 0)
 252                                 scfdie();
 253                         (void) fputs(buf, stdout);
 254                         (void) putchar('/');
 255 
 256                         if (scf_property_get_name(prop, buf, buf_sz) < 0)
 257                                 scfdie();
 258                         (void) fputs(buf, stdout);
 259 
 260                         free(buf);
 261                 }
 262 
 263                 (void) putchar(' ');
 264 
 265                 if (scf_property_type(prop, &ty) == -1)
 266                         scfdie();
 267                 (void) fputs(scf_type_to_string(ty), stdout);
 268                 (void) putchar(' ');
 269         }
 270 
 271         if ((iter = scf_iter_create(hndl)) == NULL ||
 272             (val = scf_value_create(hndl)) == NULL)
 273                 scfdie();
 274 
 275         if (scf_iter_property_values(iter, prop) == -1)
 276                 scfdie();
 277 
 278         first = 1;
 279         while ((ret = scf_iter_next_value(iter, val)) == 1) {
 280                 if (first)
 281                         first = 0;
 282                 else
 283                         (void) putchar(' ');
 284                 print_value(val);
 285         }
 286         if (ret == -1) {
 287                 err = scf_error();
 288                 if (err == SCF_ERROR_PERMISSION_DENIED) {
 289                         if (uu_list_numnodes(prop_list) > 0)
 290                                 die(permission_denied_emsg);
 291                 } else {
 292                         scfdie();
 293                 }
 294         }
 295 
 296         (void) putchar('\n');
 297 
 298         scf_iter_destroy(iter);
 299         (void) scf_value_destroy(val);
 300 }
 301 
 302 /*
 303  * display_prop() all of the properties in the given property group.  Force
 304  * types to true so identification will be displayed.
 305  */
 306 static void
 307 display_pg(scf_propertygroup_t *pg)
 308 {
 309         scf_property_t *prop;
 310         scf_iter_t *iter;
 311         int ret;
 312 
 313         types = 1;      /* Always display types for whole propertygroups. */
 314 
 315         if ((prop = scf_property_create(hndl)) == NULL ||
 316             (iter = scf_iter_create(hndl)) == NULL)
 317                 scfdie();
 318 
 319         if (scf_iter_pg_properties(iter, pg) == -1)
 320                 scfdie();
 321 
 322         while ((ret = scf_iter_next_property(iter, prop)) == 1)
 323                 display_prop(pg, prop);
 324         if (ret == -1)
 325                 scfdie();
 326 
 327         scf_iter_destroy(iter);
 328         scf_property_destroy(prop);
 329 }
 330 
 331 /*
 332  * Common code to execute when a nonexistant property is encountered.
 333  */
 334 static void
 335 noprop_common_action()
 336 {
 337         if (!PRINT_NOPROP_ERRORS)
 338                 /* We're not printing errors, so we can cut out early. */
 339                 exit(UU_EXIT_FATAL);
 340 
 341         return_code = UU_EXIT_FATAL;
 342 }
 343 
 344 /*
 345  * Iterate the properties of a service or an instance when no snapshot
 346  * is specified.
 347  */
 348 static int
 349 scf_iter_entity_pgs(scf_iter_t *iter, scf_entityp_t ent)
 350 {
 351         int ret = 0;
 352 
 353         if (ent.type) {
 354                 /*
 355                  * If we are displaying properties for a service,
 356                  * treat it as though it were a composed, current
 357                  * lookup. (implicit cflag) However, if a snapshot
 358                  * was specified, fail.
 359                  */
 360                 if (sflag)
 361                         die(gettext("Only instances have "
 362                             "snapshots.\n"));
 363                 ret = scf_iter_service_pgs(iter, ent.u.svc);
 364         } else {
 365                 if (Cflag)
 366                         ret = scf_iter_instance_pgs(iter, ent.u.inst);
 367                 else
 368                         ret = scf_iter_instance_pgs_composed(iter, ent.u.inst,
 369                             NULL);
 370         }
 371         return (ret);
 372 }
 373 
 374 /*
 375  * Return a snapshot for the supplied instance and snapshot name.
 376  */
 377 static scf_snapshot_t *
 378 get_snapshot(const scf_instance_t *inst, const char *snapshot)
 379 {
 380         scf_snapshot_t *snap = scf_snapshot_create(hndl);
 381 
 382         if (snap == NULL)
 383                 scfdie();
 384 
 385         if (scf_instance_get_snapshot(inst, snapshot, snap) == -1) {
 386                 switch (scf_error()) {
 387                 case SCF_ERROR_INVALID_ARGUMENT:
 388                         die(gettext("Invalid snapshot name.\n"));
 389                         /* NOTREACHED */
 390 
 391                 case SCF_ERROR_NOT_FOUND:
 392                         if (sflag == 0) {
 393                                 scf_snapshot_destroy(snap);
 394                                 snap = NULL;
 395                         } else
 396                                 die(gettext("No such snapshot.\n"));
 397                         break;
 398 
 399                 default:
 400                         scfdie();
 401                 }
 402         }
 403 
 404         return (snap);
 405 }
 406 
 407 /*
 408  * Entity (service or instance): If there are -p options,
 409  * display_{pg,prop}() the named property groups and/or properties.  Otherwise
 410  * display_pg() all property groups.
 411  */
 412 static void
 413 process_ent(scf_entityp_t ent)
 414 {
 415         scf_snapshot_t *snap = NULL;
 416         scf_propertygroup_t *pg;
 417         scf_property_t *prop;
 418         scf_iter_t *iter;
 419         svcprop_prop_node_t *spn;
 420         int ret, err;
 421 
 422         if (uu_list_numnodes(prop_list) == 0) {
 423                 if (quiet)
 424                         return;
 425 
 426                 if ((pg = scf_pg_create(hndl)) == NULL ||
 427                     (iter = scf_iter_create(hndl)) == NULL)
 428                         scfdie();
 429 
 430                 if (cflag || Cflag || ent.type != ENT_INSTANCE) {
 431                         if (scf_iter_entity_pgs(iter, ent) == -1)
 432                                 scfdie();
 433                 } else {
 434                         if (snapshot != NULL)
 435                                 snap = get_snapshot(ent.u.inst, snapshot);
 436 
 437                         if (scf_iter_instance_pgs_composed(iter, ent.u.inst,
 438                             snap) == -1)
 439                                 scfdie();
 440                         if (snap)
 441                                 scf_snapshot_destroy(snap);
 442                 }
 443 
 444                 while ((ret = scf_iter_next_pg(iter, pg)) == 1)
 445                         display_pg(pg);
 446                 if (ret == -1)
 447                         scfdie();
 448 
 449                 /*
 450                  * In normal usage, i.e. against the running snapshot,
 451                  * we must iterate over the current non-persistent
 452                  * pg's.
 453                  */
 454                 if (sflag == 0 && snap != NULL) {
 455                         scf_iter_reset(iter);
 456                         if (scf_iter_instance_pgs_composed(iter, ent.u.inst,
 457                             NULL) == -1)
 458                                 scfdie();
 459                         while ((ret = scf_iter_next_pg(iter, pg)) == 1) {
 460                                 uint32_t flags;
 461 
 462                                 if (scf_pg_get_flags(pg, &flags) == -1)
 463                                         scfdie();
 464                                 if (flags & SCF_PG_FLAG_NONPERSISTENT)
 465                                         display_pg(pg);
 466                         }
 467                 }
 468                 if (ret == -1)
 469                         scfdie();
 470 
 471                 scf_iter_destroy(iter);
 472                 scf_pg_destroy(pg);
 473 
 474                 return;
 475         }
 476 
 477         if ((pg = scf_pg_create(hndl)) == NULL ||
 478             (prop = scf_property_create(hndl)) == NULL)
 479                 scfdie();
 480 
 481         if (ent.type == ENT_INSTANCE && snapshot != NULL)
 482                 snap = get_snapshot(ent.u.inst, snapshot);
 483 
 484         for (spn = uu_list_first(prop_list);
 485             spn != NULL;
 486             spn = uu_list_next(prop_list, spn)) {
 487                 if (ent.type == ENT_INSTANCE) {
 488                         if (Cflag)
 489                                 ret = scf_instance_get_pg(ent.u.inst,
 490                                     spn->spn_comp1, pg);
 491                         else
 492                                 ret = scf_instance_get_pg_composed(ent.u.inst,
 493                                     snap, spn->spn_comp1, pg);
 494                         err = scf_error();
 495 
 496                         /*
 497                          * If we didn't find it in the specified snapshot, use
 498                          * the current values if the pg is nonpersistent.
 499                          */
 500                         if (ret == -1 && !Cflag &&snap != NULL && err ==
 501                             SCF_ERROR_NOT_FOUND) {
 502                                 ret = scf_instance_get_pg_composed(
 503                                     ent.u.inst, NULL, spn->spn_comp1,
 504                                     pg);
 505 
 506                                 if (ret == 0) {
 507                                         uint32_t flags;
 508 
 509                                         if (scf_pg_get_flags(pg, &flags) == -1)
 510                                                 scfdie();
 511                                         if ((flags & SCF_PG_FLAG_NONPERSISTENT)
 512                                             == 0) {
 513                                                 ret = -1;
 514                                         }
 515                                 }
 516                         }
 517                 } else {
 518                         /*
 519                          * If we are displaying properties for a service,
 520                          * treat it as though it were a composed, current
 521                          * lookup. (implicit cflag) However, if a snapshot
 522                          * was specified, fail.
 523                          */
 524                         if (sflag)
 525                                 die(gettext("Only instances have "
 526                                     "snapshots.\n"));
 527                         ret = scf_entity_get_pg(ent, spn->spn_comp1, pg);
 528                         err = scf_error();
 529                 }
 530                 if (ret == -1) {
 531                         if (err != SCF_ERROR_NOT_FOUND)
 532                                 scfdie();
 533 
 534                         if (PRINT_NOPROP_ERRORS) {
 535                                 char *buf;
 536 
 537                                 buf = safe_malloc(max_scf_fmri_length + 1);
 538                                 if (scf_entity_to_fmri(ent, buf,
 539                                     max_scf_fmri_length + 1) == -1)
 540                                         scfdie();
 541 
 542                                 uu_warn(gettext("Couldn't find property group "
 543                                     "`%s' for %s `%s'.\n"), spn->spn_comp1,
 544                                     SCF_ENTITY_TYPE_NAME(ent), buf);
 545 
 546                                 free(buf);
 547                         }
 548 
 549                         noprop_common_action();
 550 
 551                         continue;
 552                 }
 553 
 554                 if (spn->spn_comp2 == NULL) {
 555                         if (!quiet)
 556                                 display_pg(pg);
 557                         continue;
 558                 }
 559 
 560                 if (scf_pg_get_property(pg, spn->spn_comp2, prop) == -1) {
 561                         if (scf_error() != SCF_ERROR_NOT_FOUND)
 562                                 scfdie();
 563 
 564                         if (PRINT_NOPROP_ERRORS) {
 565                                 char *buf;
 566 
 567                                 buf = safe_malloc(max_scf_fmri_length + 1);
 568                                 if (scf_entity_to_fmri(ent, buf,
 569                                     max_scf_fmri_length + 1) == -1)
 570                                         scfdie();
 571 
 572                                 /* FMRI syntax knowledge */
 573                                 uu_warn(gettext("Couldn't find property "
 574                                     "`%s/%s' for %s `%s'.\n"), spn->spn_comp1,
 575                                     spn->spn_comp2, SCF_ENTITY_TYPE_NAME(ent),
 576                                     buf);
 577 
 578                                 free(buf);
 579                         }
 580 
 581                         noprop_common_action();
 582 
 583                         continue;
 584                 }
 585 
 586                 if (!quiet)
 587                         display_prop(pg, prop);
 588         }
 589 
 590         scf_property_destroy(prop);
 591         scf_pg_destroy(pg);
 592         if (snap)
 593                 scf_snapshot_destroy(snap);
 594 }
 595 
 596 /*
 597  * Without -p options, just call display_pg().  Otherwise display_prop() the
 598  * named properties of the property group.
 599  */
 600 static void
 601 process_pg(scf_propertygroup_t *pg)
 602 {
 603         scf_property_t *prop;
 604         svcprop_prop_node_t *spn;
 605 
 606         if (uu_list_first(prop_list) == NULL) {
 607                 if (quiet)
 608                         return;
 609 
 610                 display_pg(pg);
 611                 return;
 612         }
 613 
 614         prop = scf_property_create(hndl);
 615         if (prop == NULL)
 616                 scfdie();
 617 
 618         for (spn = uu_list_first(prop_list);
 619             spn != NULL;
 620             spn = uu_list_next(prop_list, spn)) {
 621                 if (spn->spn_comp2 != NULL) {
 622                         char *buf;
 623 
 624                         buf = safe_malloc(max_scf_fmri_length + 1);
 625                         if (scf_pg_to_fmri(pg, buf, max_scf_fmri_length + 1) ==
 626                             -1)
 627                                 scfdie();
 628 
 629                         uu_xdie(UU_EXIT_USAGE, gettext("-p argument `%s/%s' "
 630                             "has too many components for property "
 631                             "group `%s'.\n"), spn->spn_comp1, spn->spn_comp2,
 632                             buf);
 633                 }
 634 
 635                 if (scf_pg_get_property(pg, spn->spn_comp1, prop) == 0) {
 636                         if (!quiet)
 637                                 display_prop(pg, prop);
 638                         continue;
 639                 }
 640 
 641                 if (scf_error() != SCF_ERROR_NOT_FOUND)
 642                         scfdie();
 643 
 644                 if (PRINT_NOPROP_ERRORS) {
 645                         char *buf;
 646 
 647                         buf = safe_malloc(max_scf_fmri_length + 1);
 648                         if (scf_pg_to_fmri(pg, buf, max_scf_fmri_length + 1) ==
 649                             -1)
 650                                 scfdie();
 651 
 652                         uu_warn(gettext("Couldn't find property `%s' in "
 653                             "property group `%s'.\n"), spn->spn_comp1, buf);
 654 
 655                         free(buf);
 656                 }
 657 
 658                 noprop_common_action();
 659         }
 660 }
 661 
 662 /*
 663  * If there are -p options, show the error.  Otherwise just call
 664  * display_prop().
 665  */
 666 static void
 667 process_prop(scf_propertygroup_t *pg, scf_property_t *prop)
 668 {
 669         if (uu_list_first(prop_list) != NULL) {
 670                 uu_warn(gettext("The -p option cannot be used with property "
 671                     "operands.\n"));
 672                 usage();
 673         }
 674 
 675         if (quiet)
 676                 return;
 677 
 678         display_prop(pg, prop);
 679 }
 680 
 681 /* Decode an operand & dispatch. */
 682 /* ARGSUSED */
 683 static int
 684 process_fmri(void *unused, scf_walkinfo_t *wip)
 685 {
 686         scf_entityp_t ent;
 687 
 688         /* Multiple matches imply multiple entities. */
 689         if (wip->count > 1)
 690                 types = fmris = 1;
 691 
 692         if (wip->prop != NULL) {
 693                 process_prop(wip->pg, wip->prop);
 694         } else if (wip->pg != NULL) {
 695                 process_pg(wip->pg);
 696         } else if (wip->inst != NULL) {
 697                 SCF_ENTITY_SET_TO_INSTANCE(ent, wip->inst);
 698                 process_ent(ent);
 699         } else {
 700                 /* scf_walk_fmri() won't let this happen */
 701                 assert(wip->svc != NULL);
 702                 SCF_ENTITY_SET_TO_SERVICE(ent, wip->svc);
 703                 process_ent(ent);
 704         }
 705 
 706         return (0);
 707 }
 708 
 709 static void
 710 add_prop(char *property)
 711 {
 712         svcprop_prop_node_t *p, *last;
 713         char *slash;
 714 
 715         const char * const invalid_component_emsg =
 716             gettext("Invalid component name `%s'.\n");
 717 
 718         /* FMRI syntax knowledge. */
 719         slash = strchr(property, '/');
 720         if (slash != NULL) {
 721                 if (strchr(slash + 1, '/') != NULL) {
 722                         uu_warn(gettext("-p argument `%s' has too many "
 723                             "components.\n"), property);
 724                         usage();
 725                 }
 726         }
 727 
 728         if (slash != NULL)
 729                 *slash = '\0';
 730 
 731         p = safe_malloc(sizeof (svcprop_prop_node_t));
 732         uu_list_node_init(p, &p->spn_list_node, prop_pool);
 733 
 734         p->spn_comp1 = property;
 735         p->spn_comp2 = (slash == NULL) ? NULL : slash + 1;
 736 
 737         if (uu_check_name(p->spn_comp1, UU_NAME_DOMAIN) == -1)
 738                 uu_xdie(UU_EXIT_USAGE, invalid_component_emsg, p->spn_comp1);
 739         if (p->spn_comp2 != NULL &&
 740             uu_check_name(p->spn_comp2, UU_NAME_DOMAIN) == -1)
 741                 uu_xdie(UU_EXIT_USAGE, invalid_component_emsg, p->spn_comp2);
 742 
 743         last = uu_list_last(prop_list);
 744         if (last != NULL) {
 745                 if ((last->spn_comp2 == NULL) ^ (p->spn_comp2 == NULL)) {
 746                         /*
 747                          * The -p options have mixed numbers of components.
 748                          * If they both turn out to be valid, then the
 749                          * single-component ones will specify property groups,
 750                          * so we need to turn on types to keep the output of
 751                          * display_prop() consistent with display_pg().
 752                          */
 753                         types = 1;
 754                 }
 755         }
 756 
 757         (void) uu_list_insert_after(prop_list, NULL, p);
 758 }
 759 
 760 
 761 /*
 762  * Wait for a property group or property change.
 763  *
 764  * Extract a pg and optionally a property name from fmri & prop_list.
 765  * _scf_pg_wait() for the pg, and display_pg(pg) or display_prop(pg, prop)
 766  * when it returns.
 767  */
 768 /* ARGSUSED */
 769 static int
 770 do_wait(void *unused, scf_walkinfo_t *wip)
 771 {
 772         scf_property_t *prop;
 773         scf_propertygroup_t *lpg, *pg;
 774         const char *propname;
 775         svcprop_prop_node_t *p;
 776 
 777         const char *emsg_not_found = gettext("Not found.\n");
 778 
 779         if ((lpg = scf_pg_create(hndl)) == NULL ||
 780             (prop = scf_property_create(hndl)) == NULL)
 781                 scfdie();
 782 
 783         if (wip->prop != NULL) {
 784                 if (uu_list_numnodes(prop_list) > 0)
 785                         uu_xdie(UU_EXIT_USAGE, gettext("-p cannot be used with "
 786                             "property FMRIs.\n"));
 787                 pg = wip->pg;
 788 
 789                 assert(strrchr(wip->fmri, '/') != NULL);
 790                 propname = strrchr(wip->fmri, '/') + 1;
 791 
 792         } else if (wip->pg != NULL) {
 793                 p = uu_list_first(prop_list);
 794 
 795                 if (p != NULL) {
 796                         if (p->spn_comp2 != NULL)
 797                                 uu_xdie(UU_EXIT_USAGE, gettext("-p argument "
 798                                     "\"%s/%s\" has too many components for "
 799                                     "property group %s.\n"),
 800                                     p->spn_comp1, p->spn_comp2, wip->fmri);
 801 
 802                         propname = p->spn_comp1;
 803 
 804                         if (scf_pg_get_property(wip->pg, propname, prop) !=
 805                             SCF_SUCCESS) {
 806                                 switch (scf_error()) {
 807                                 case SCF_ERROR_INVALID_ARGUMENT:
 808                                         uu_xdie(UU_EXIT_USAGE,
 809                                             gettext("Invalid property name "
 810                                             "\"%s\".\n"), propname);
 811 
 812                                         /* NOTREACHED */
 813 
 814                                 case SCF_ERROR_NOT_FOUND:
 815                                         die(emsg_not_found);
 816 
 817                                 default:
 818                                         scfdie();
 819                                 }
 820                         }
 821                 } else {
 822                         propname = NULL;
 823                 }
 824 
 825                 pg = wip->pg;
 826 
 827         } else if (wip->inst != NULL) {
 828 
 829                 p = uu_list_first(prop_list);
 830                 if (p == NULL)
 831                         uu_xdie(UU_EXIT_USAGE,
 832                             gettext("Cannot wait for an instance.\n"));
 833 
 834                 if (scf_instance_get_pg(wip->inst, p->spn_comp1, lpg) !=
 835                     SCF_SUCCESS) {
 836                         switch (scf_error()) {
 837                         case SCF_ERROR_INVALID_ARGUMENT:
 838                                 uu_xdie(UU_EXIT_USAGE, gettext("Invalid "
 839                                     "property group name \"%s\".\n"),
 840                                     p->spn_comp1);
 841                                 /* NOTREACHED */
 842 
 843                         case SCF_ERROR_NOT_FOUND:
 844                                 die(emsg_not_found);
 845 
 846                         default:
 847                                 scfdie();
 848                         }
 849                 }
 850 
 851                 propname = p->spn_comp2;
 852 
 853                 if (propname != NULL) {
 854                         if (scf_pg_get_property(lpg, propname, prop) !=
 855                             SCF_SUCCESS) {
 856                                 switch (scf_error()) {
 857                                 case SCF_ERROR_INVALID_ARGUMENT:
 858                                         uu_xdie(UU_EXIT_USAGE,
 859                                             gettext("Invalid property name "
 860                                             "\"%s\".\n"), propname);
 861                                         /* NOTREACHED */
 862 
 863                                 case SCF_ERROR_NOT_FOUND:
 864                                         die(emsg_not_found);
 865 
 866                                 default:
 867                                         scfdie();
 868                                 }
 869                         }
 870                 }
 871 
 872                 pg = lpg;
 873 
 874         } else if (wip->svc != NULL) {
 875 
 876                 p = uu_list_first(prop_list);
 877                 if (p == NULL)
 878                         uu_xdie(UU_EXIT_USAGE,
 879                             gettext("Cannot wait for a service.\n"));
 880 
 881                 if (scf_service_get_pg(wip->svc, p->spn_comp1, lpg) !=
 882                     SCF_SUCCESS) {
 883                         switch (scf_error()) {
 884                         case SCF_ERROR_INVALID_ARGUMENT:
 885                                 uu_xdie(UU_EXIT_USAGE, gettext("Invalid "
 886                                     "property group name \"%s\".\n"),
 887                                     p->spn_comp1);
 888                                 /* NOTREACHED */
 889 
 890                         case SCF_ERROR_NOT_FOUND:
 891                                 die(emsg_not_found);
 892 
 893                         default:
 894                                 scfdie();
 895                         }
 896                 }
 897 
 898                 propname = p->spn_comp2;
 899 
 900                 if (propname != NULL) {
 901                         if (scf_pg_get_property(lpg, propname, prop) !=
 902                             SCF_SUCCESS) {
 903                                 switch (scf_error()) {
 904                                 case SCF_ERROR_INVALID_ARGUMENT:
 905                                         uu_xdie(UU_EXIT_USAGE,
 906                                             gettext("Invalid property name "
 907                                             "\"%s\".\n"), propname);
 908 
 909                                         /* NOTREACHED */
 910 
 911                                 case SCF_ERROR_NOT_FOUND:
 912                                         die(emsg_not_found);
 913 
 914                                 default:
 915                                         scfdie();
 916                                 }
 917                         }
 918                 }
 919 
 920                 pg = lpg;
 921 
 922         } else {
 923                 uu_xdie(UU_EXIT_USAGE, gettext("FMRI must specify an entity, "
 924                     "property group, or property.\n"));
 925         }
 926 
 927         for (;;) {
 928                 int ret;
 929 
 930                 ret = _scf_pg_wait(pg, -1);
 931                 if (ret != SCF_SUCCESS)
 932                         scfdie();
 933 
 934                 ret = scf_pg_update(pg);
 935                 if (ret < 0) {
 936                         if (scf_error() != SCF_ERROR_DELETED)
 937                                 scfdie();
 938 
 939                         die(emsg_not_found);
 940                 }
 941                 if (ret == SCF_COMPLETE)
 942                         break;
 943         }
 944 
 945         if (propname != NULL) {
 946                 if (scf_pg_get_property(pg, propname, prop) == SCF_SUCCESS) {
 947                         if (!quiet)
 948                                 display_prop(pg, prop);
 949                 } else {
 950                         if (scf_error() != SCF_ERROR_NOT_FOUND)
 951                                 scfdie();
 952 
 953                         if (PRINT_NOPROP_ERRORS)
 954                                 uu_warn(emsg_not_found);
 955 
 956                         return_code = UU_EXIT_FATAL;
 957                 }
 958         } else {
 959                 if (!quiet)
 960                         display_pg(pg);
 961         }
 962 
 963         scf_property_destroy(prop);
 964         scf_pg_destroy(lpg);
 965 
 966         return (0);
 967 }
 968 
 969 /*
 970  * These functions replace uu_warn() and uu_die() when the quiet (-q) option is
 971  * used, and silently ignore any output.
 972  */
 973 
 974 /*ARGSUSED*/
 975 static void
 976 quiet_warn(const char *fmt, ...)
 977 {
 978         /* Do nothing */
 979 }
 980 
 981 /*ARGSUSED*/
 982 static __NORETURN void
 983 quiet_die(const char *fmt, ...)
 984 {
 985         exit(UU_EXIT_FATAL);
 986 }
 987 
 988 int
 989 main(int argc, char *argv[])
 990 {
 991         int c;
 992         scf_walk_callback callback;
 993         int flags;
 994         int err;
 995 
 996         (void) setlocale(LC_ALL, "");
 997         (void) textdomain(TEXT_DOMAIN);
 998 
 999         return_code = UU_EXIT_OK;
1000 
1001         (void) uu_setpname(argv[0]);
1002 
1003         prop_pool = uu_list_pool_create("properties",
1004             sizeof (svcprop_prop_node_t),
1005             offsetof(svcprop_prop_node_t, spn_list_node), NULL, 0);
1006         if (prop_pool == NULL)
1007                 uu_die("%s\n", uu_strerror(uu_error()));
1008 
1009         prop_list = uu_list_create(prop_pool, NULL, 0);
1010 
1011         hndl = scf_handle_create(SCF_VERSION);
1012         if (hndl == NULL)
1013                 scfdie();
1014 
1015         while ((c = getopt(argc, argv, "Ccfp:qs:tvwz:")) != -1) {
1016                 switch (c) {
1017                 case 'C':
1018                         if (cflag || sflag || wait)
1019                                 usage();        /* Not with -c, -s or -w */
1020                         Cflag++;
1021                         snapshot = NULL;
1022                         break;
1023 
1024                 case 'c':
1025                         if (Cflag || sflag || wait)
1026                                 usage();        /* Not with -C, -s or -w */
1027                         cflag++;
1028                         snapshot = NULL;
1029                         break;
1030 
1031                 case 'f':
1032                         types = 1;
1033                         fmris = 1;
1034                         break;
1035 
1036                 case 'p':
1037                         add_prop(optarg);
1038                         break;
1039 
1040                 case 'q':
1041                         quiet = 1;
1042                         warn = quiet_warn;
1043                         die = quiet_die;
1044                         break;
1045 
1046                 case 's':
1047                         if (Cflag || cflag || wait)
1048                                 usage();        /* Not with -C, -c or -w */
1049                         snapshot = optarg;
1050                         sflag++;
1051                         break;
1052 
1053                 case 't':
1054                         types = 1;
1055                         break;
1056 
1057                 case 'v':
1058                         verbose = 1;
1059                         break;
1060 
1061                 case 'w':
1062                         if (Cflag || cflag || sflag)
1063                                 usage();        /* Not with -C, -c or -s */
1064                         wait = 1;
1065                         break;
1066 
1067                 case 'z': {
1068                         scf_value_t *zone;
1069                         scf_handle_t *h = hndl;
1070 
1071                         if (getzoneid() != GLOBAL_ZONEID)
1072                                 uu_die(gettext("svcprop -z may only be used "
1073                                     "from the global zone\n"));
1074 
1075                         if ((zone = scf_value_create(h)) == NULL)
1076                                 scfdie();
1077 
1078                         if (scf_value_set_astring(zone, optarg) != SCF_SUCCESS)
1079                                 scfdie();
1080 
1081                         if (scf_handle_decorate(h, "zone", zone) != SCF_SUCCESS)
1082                                 uu_die(gettext("invalid zone '%s'\n"), optarg);
1083 
1084                         scf_value_destroy(zone);
1085                         break;
1086                 }
1087 
1088                 case '?':
1089                         switch (optopt) {
1090                         case 'p':
1091                                 usage();
1092 
1093                         default:
1094                                 break;
1095                         }
1096 
1097                         /* FALLTHROUGH */
1098 
1099                 default:
1100                         usage();
1101                 }
1102         }
1103 
1104         if (optind == argc)
1105                 usage();
1106 
1107         max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
1108         max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1109         max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
1110         if (max_scf_name_length == -1 || max_scf_value_length == -1 ||
1111             max_scf_fmri_length == -1)
1112                 scfdie();
1113 
1114         if (scf_handle_bind(hndl) == -1)
1115                 die(gettext("Could not connect to configuration repository: "
1116                     "%s.\n"), scf_strerror(scf_error()));
1117 
1118         flags = SCF_WALK_PROPERTY | SCF_WALK_SERVICE | SCF_WALK_EXPLICIT;
1119 
1120         if (wait) {
1121                 if (uu_list_numnodes(prop_list) > 1)
1122                         usage();
1123 
1124                 if (argc - optind > 1)
1125                         usage();
1126 
1127                 callback = do_wait;
1128 
1129         } else {
1130                 callback = process_fmri;
1131 
1132                 flags |= SCF_WALK_MULTIPLE;
1133         }
1134 
1135         if ((err = scf_walk_fmri(hndl, argc - optind, argv + optind, flags,
1136             callback, NULL, &return_code, warn)) != 0) {
1137                 warn(gettext("failed to iterate over instances: %s\n"),
1138                     scf_strerror(err));
1139                 return_code = UU_EXIT_FATAL;
1140         }
1141 
1142         scf_handle_destroy(hndl);
1143 
1144         return (return_code);
1145 }