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 (c) 2011, Joyent, Inc. All rights reserved.
  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                         free(buf);
 635                 }
 636 
 637                 if (scf_pg_get_property(pg, spn->spn_comp1, prop) == 0) {
 638                         if (!quiet)
 639                                 display_prop(pg, prop);
 640                         continue;
 641                 }
 642 
 643                 if (scf_error() != SCF_ERROR_NOT_FOUND)
 644                         scfdie();
 645 
 646                 if (PRINT_NOPROP_ERRORS) {
 647                         char *buf;
 648 
 649                         buf = safe_malloc(max_scf_fmri_length + 1);
 650                         if (scf_pg_to_fmri(pg, buf, max_scf_fmri_length + 1) ==
 651                             -1)
 652                                 scfdie();
 653 
 654                         uu_warn(gettext("Couldn't find property `%s' in "
 655                             "property group `%s'.\n"), spn->spn_comp1, buf);
 656 
 657                         free(buf);
 658                 }
 659 
 660                 noprop_common_action();
 661         }
 662 }
 663 
 664 /*
 665  * If there are -p options, show the error.  Otherwise just call
 666  * display_prop().
 667  */
 668 static void
 669 process_prop(scf_propertygroup_t *pg, scf_property_t *prop)
 670 {
 671         if (uu_list_first(prop_list) != NULL) {
 672                 uu_warn(gettext("The -p option cannot be used with property "
 673                     "operands.\n"));
 674                 usage();
 675         }
 676 
 677         if (quiet)
 678                 return;
 679 
 680         display_prop(pg, prop);
 681 }
 682 
 683 /* Decode an operand & dispatch. */
 684 /* ARGSUSED */
 685 static int
 686 process_fmri(void *unused, scf_walkinfo_t *wip)
 687 {
 688         scf_entityp_t ent;
 689 
 690         /* Multiple matches imply multiple entities. */
 691         if (wip->count > 1)
 692                 types = fmris = 1;
 693 
 694         if (wip->prop != NULL) {
 695                 process_prop(wip->pg, wip->prop);
 696         } else if (wip->pg != NULL) {
 697                 process_pg(wip->pg);
 698         } else if (wip->inst != NULL) {
 699                 SCF_ENTITY_SET_TO_INSTANCE(ent, wip->inst);
 700                 process_ent(ent);
 701         } else {
 702                 /* scf_walk_fmri() won't let this happen */
 703                 assert(wip->svc != NULL);
 704                 SCF_ENTITY_SET_TO_SERVICE(ent, wip->svc);
 705                 process_ent(ent);
 706         }
 707 
 708         return (0);
 709 }
 710 
 711 static void
 712 add_prop(char *property)
 713 {
 714         svcprop_prop_node_t *p, *last;
 715         char *slash;
 716 
 717         const char * const invalid_component_emsg =
 718             gettext("Invalid component name `%s'.\n");
 719 
 720         /* FMRI syntax knowledge. */
 721         slash = strchr(property, '/');
 722         if (slash != NULL) {
 723                 if (strchr(slash + 1, '/') != NULL) {
 724                         uu_warn(gettext("-p argument `%s' has too many "
 725                             "components.\n"), property);
 726                         usage();
 727                 }
 728         }
 729 
 730         if (slash != NULL)
 731                 *slash = '\0';
 732 
 733         p = safe_malloc(sizeof (svcprop_prop_node_t));
 734         uu_list_node_init(p, &p->spn_list_node, prop_pool);
 735 
 736         p->spn_comp1 = property;
 737         p->spn_comp2 = (slash == NULL) ? NULL : slash + 1;
 738 
 739         if (uu_check_name(p->spn_comp1, UU_NAME_DOMAIN) == -1)
 740                 uu_xdie(UU_EXIT_USAGE, invalid_component_emsg, p->spn_comp1);
 741         if (p->spn_comp2 != NULL &&
 742             uu_check_name(p->spn_comp2, UU_NAME_DOMAIN) == -1)
 743                 uu_xdie(UU_EXIT_USAGE, invalid_component_emsg, p->spn_comp2);
 744 
 745         last = uu_list_last(prop_list);
 746         if (last != NULL) {
 747                 if ((last->spn_comp2 == NULL) ^ (p->spn_comp2 == NULL)) {
 748                         /*
 749                          * The -p options have mixed numbers of components.
 750                          * If they both turn out to be valid, then the
 751                          * single-component ones will specify property groups,
 752                          * so we need to turn on types to keep the output of
 753                          * display_prop() consistent with display_pg().
 754                          */
 755                         types = 1;
 756                 }
 757         }
 758 
 759         (void) uu_list_insert_after(prop_list, NULL, p);
 760 }
 761 
 762 
 763 /*
 764  * Wait for a property group or property change.
 765  *
 766  * Extract a pg and optionally a property name from fmri & prop_list.
 767  * _scf_pg_wait() for the pg, and display_pg(pg) or display_prop(pg, prop)
 768  * when it returns.
 769  */
 770 /* ARGSUSED */
 771 static int
 772 do_wait(void *unused, scf_walkinfo_t *wip)
 773 {
 774         scf_property_t *prop;
 775         scf_propertygroup_t *lpg, *pg;
 776         const char *propname;
 777         svcprop_prop_node_t *p;
 778 
 779         const char *emsg_not_found = gettext("Not found.\n");
 780 
 781         if ((lpg = scf_pg_create(hndl)) == NULL ||
 782             (prop = scf_property_create(hndl)) == NULL)
 783                 scfdie();
 784 
 785         if (wip->prop != NULL) {
 786                 if (uu_list_numnodes(prop_list) > 0)
 787                         uu_xdie(UU_EXIT_USAGE, gettext("-p cannot be used with "
 788                             "property FMRIs.\n"));
 789                 pg = wip->pg;
 790 
 791                 assert(strrchr(wip->fmri, '/') != NULL);
 792                 propname = strrchr(wip->fmri, '/') + 1;
 793 
 794         } else if (wip->pg != NULL) {
 795                 p = uu_list_first(prop_list);
 796 
 797                 if (p != NULL) {
 798                         if (p->spn_comp2 != NULL)
 799                                 uu_xdie(UU_EXIT_USAGE, gettext("-p argument "
 800                                     "\"%s/%s\" has too many components for "
 801                                     "property group %s.\n"),
 802                                     p->spn_comp1, p->spn_comp2, wip->fmri);
 803 
 804                         propname = p->spn_comp1;
 805 
 806                         if (scf_pg_get_property(wip->pg, propname, prop) !=
 807                             SCF_SUCCESS) {
 808                                 switch (scf_error()) {
 809                                 case SCF_ERROR_INVALID_ARGUMENT:
 810                                         uu_xdie(UU_EXIT_USAGE,
 811                                             gettext("Invalid property name "
 812                                             "\"%s\".\n"), propname);
 813 
 814                                         /* NOTREACHED */
 815 
 816                                 case SCF_ERROR_NOT_FOUND:
 817                                         die(emsg_not_found);
 818 
 819                                 default:
 820                                         scfdie();
 821                                 }
 822                         }
 823                 } else {
 824                         propname = NULL;
 825                 }
 826 
 827                 pg = wip->pg;
 828 
 829         } else if (wip->inst != NULL) {
 830 
 831                 p = uu_list_first(prop_list);
 832                 if (p == NULL)
 833                         uu_xdie(UU_EXIT_USAGE,
 834                             gettext("Cannot wait for an instance.\n"));
 835 
 836                 if (scf_instance_get_pg(wip->inst, p->spn_comp1, lpg) !=
 837                     SCF_SUCCESS) {
 838                         switch (scf_error()) {
 839                         case SCF_ERROR_INVALID_ARGUMENT:
 840                                 uu_xdie(UU_EXIT_USAGE, gettext("Invalid "
 841                                     "property group name \"%s\".\n"),
 842                                     p->spn_comp1);
 843                                 /* NOTREACHED */
 844 
 845                         case SCF_ERROR_NOT_FOUND:
 846                                 die(emsg_not_found);
 847 
 848                         default:
 849                                 scfdie();
 850                         }
 851                 }
 852 
 853                 propname = p->spn_comp2;
 854 
 855                 if (propname != NULL) {
 856                         if (scf_pg_get_property(lpg, propname, prop) !=
 857                             SCF_SUCCESS) {
 858                                 switch (scf_error()) {
 859                                 case SCF_ERROR_INVALID_ARGUMENT:
 860                                         uu_xdie(UU_EXIT_USAGE,
 861                                             gettext("Invalid property name "
 862                                             "\"%s\".\n"), propname);
 863                                         /* NOTREACHED */
 864 
 865                                 case SCF_ERROR_NOT_FOUND:
 866                                         die(emsg_not_found);
 867 
 868                                 default:
 869                                         scfdie();
 870                                 }
 871                         }
 872                 }
 873 
 874                 pg = lpg;
 875 
 876         } else if (wip->svc != NULL) {
 877 
 878                 p = uu_list_first(prop_list);
 879                 if (p == NULL)
 880                         uu_xdie(UU_EXIT_USAGE,
 881                             gettext("Cannot wait for a service.\n"));
 882 
 883                 if (scf_service_get_pg(wip->svc, p->spn_comp1, lpg) !=
 884                     SCF_SUCCESS) {
 885                         switch (scf_error()) {
 886                         case SCF_ERROR_INVALID_ARGUMENT:
 887                                 uu_xdie(UU_EXIT_USAGE, gettext("Invalid "
 888                                     "property group name \"%s\".\n"),
 889                                     p->spn_comp1);
 890                                 /* NOTREACHED */
 891 
 892                         case SCF_ERROR_NOT_FOUND:
 893                                 die(emsg_not_found);
 894 
 895                         default:
 896                                 scfdie();
 897                         }
 898                 }
 899 
 900                 propname = p->spn_comp2;
 901 
 902                 if (propname != NULL) {
 903                         if (scf_pg_get_property(lpg, propname, prop) !=
 904                             SCF_SUCCESS) {
 905                                 switch (scf_error()) {
 906                                 case SCF_ERROR_INVALID_ARGUMENT:
 907                                         uu_xdie(UU_EXIT_USAGE,
 908                                             gettext("Invalid property name "
 909                                             "\"%s\".\n"), propname);
 910 
 911                                         /* NOTREACHED */
 912 
 913                                 case SCF_ERROR_NOT_FOUND:
 914                                         die(emsg_not_found);
 915 
 916                                 default:
 917                                         scfdie();
 918                                 }
 919                         }
 920                 }
 921 
 922                 pg = lpg;
 923 
 924         } else {
 925                 uu_xdie(UU_EXIT_USAGE, gettext("FMRI must specify an entity, "
 926                     "property group, or property.\n"));
 927         }
 928 
 929         for (;;) {
 930                 int ret;
 931 
 932                 ret = _scf_pg_wait(pg, -1);
 933                 if (ret != SCF_SUCCESS)
 934                         scfdie();
 935 
 936                 ret = scf_pg_update(pg);
 937                 if (ret < 0) {
 938                         if (scf_error() != SCF_ERROR_DELETED)
 939                                 scfdie();
 940 
 941                         die(emsg_not_found);
 942                 }
 943                 if (ret == SCF_COMPLETE)
 944                         break;
 945         }
 946 
 947         if (propname != NULL) {
 948                 if (scf_pg_get_property(pg, propname, prop) == SCF_SUCCESS) {
 949                         if (!quiet)
 950                                 display_prop(pg, prop);
 951                 } else {
 952                         if (scf_error() != SCF_ERROR_NOT_FOUND)
 953                                 scfdie();
 954 
 955                         if (PRINT_NOPROP_ERRORS)
 956                                 uu_warn(emsg_not_found);
 957 
 958                         return_code = UU_EXIT_FATAL;
 959                 }
 960         } else {
 961                 if (!quiet)
 962                         display_pg(pg);
 963         }
 964 
 965         scf_property_destroy(prop);
 966         scf_pg_destroy(lpg);
 967 
 968         return (0);
 969 }
 970 
 971 /*
 972  * These functions replace uu_warn() and uu_die() when the quiet (-q) option is
 973  * used, and silently ignore any output.
 974  */
 975 
 976 /*ARGSUSED*/
 977 static void
 978 quiet_warn(const char *fmt, ...)
 979 {
 980         /* Do nothing */
 981 }
 982 
 983 /*ARGSUSED*/
 984 static __NORETURN void
 985 quiet_die(const char *fmt, ...)
 986 {
 987         exit(UU_EXIT_FATAL);
 988 }
 989 
 990 int
 991 main(int argc, char *argv[])
 992 {
 993         int c;
 994         scf_walk_callback callback;
 995         int flags;
 996         int err;
 997 
 998         (void) setlocale(LC_ALL, "");
 999         (void) textdomain(TEXT_DOMAIN);
1000 
1001         return_code = UU_EXIT_OK;
1002 
1003         (void) uu_setpname(argv[0]);
1004 
1005         prop_pool = uu_list_pool_create("properties",
1006             sizeof (svcprop_prop_node_t),
1007             offsetof(svcprop_prop_node_t, spn_list_node), NULL, 0);
1008         if (prop_pool == NULL)
1009                 uu_die("%s\n", uu_strerror(uu_error()));
1010 
1011         prop_list = uu_list_create(prop_pool, NULL, 0);
1012 
1013         hndl = scf_handle_create(SCF_VERSION);
1014         if (hndl == NULL)
1015                 scfdie();
1016 
1017         while ((c = getopt(argc, argv, "Ccfp:qs:tvwz:")) != -1) {
1018                 switch (c) {
1019                 case 'C':
1020                         if (cflag || sflag || wait)
1021                                 usage();        /* Not with -c, -s or -w */
1022                         Cflag++;
1023                         snapshot = NULL;
1024                         break;
1025 
1026                 case 'c':
1027                         if (Cflag || sflag || wait)
1028                                 usage();        /* Not with -C, -s or -w */
1029                         cflag++;
1030                         snapshot = NULL;
1031                         break;
1032 
1033                 case 'f':
1034                         types = 1;
1035                         fmris = 1;
1036                         break;
1037 
1038                 case 'p':
1039                         add_prop(optarg);
1040                         break;
1041 
1042                 case 'q':
1043                         quiet = 1;
1044                         warn = quiet_warn;
1045                         die = quiet_die;
1046                         break;
1047 
1048                 case 's':
1049                         if (Cflag || cflag || wait)
1050                                 usage();        /* Not with -C, -c or -w */
1051                         snapshot = optarg;
1052                         sflag++;
1053                         break;
1054 
1055                 case 't':
1056                         types = 1;
1057                         break;
1058 
1059                 case 'v':
1060                         verbose = 1;
1061                         break;
1062 
1063                 case 'w':
1064                         if (Cflag || cflag || sflag)
1065                                 usage();        /* Not with -C, -c or -s */
1066                         wait = 1;
1067                         break;
1068 
1069                 case 'z': {
1070                         scf_value_t *zone;
1071                         scf_handle_t *h = hndl;
1072 
1073                         if (getzoneid() != GLOBAL_ZONEID)
1074                                 uu_die(gettext("svcprop -z may only be used "
1075                                     "from the global zone\n"));
1076 
1077                         if ((zone = scf_value_create(h)) == NULL)
1078                                 scfdie();
1079 
1080                         if (scf_value_set_astring(zone, optarg) != SCF_SUCCESS)
1081                                 scfdie();
1082 
1083                         if (scf_handle_decorate(h, "zone", zone) != SCF_SUCCESS)
1084                                 uu_die(gettext("invalid zone '%s'\n"), optarg);
1085 
1086                         scf_value_destroy(zone);
1087                         break;
1088                 }
1089 
1090                 case '?':
1091                         switch (optopt) {
1092                         case 'p':
1093                                 usage();
1094 
1095                         default:
1096                                 break;
1097                         }
1098 
1099                         /* FALLTHROUGH */
1100 
1101                 default:
1102                         usage();
1103                 }
1104         }
1105 
1106         if (optind == argc)
1107                 usage();
1108 
1109         max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
1110         max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1111         max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
1112         if (max_scf_name_length == -1 || max_scf_value_length == -1 ||
1113             max_scf_fmri_length == -1)
1114                 scfdie();
1115 
1116         if (scf_handle_bind(hndl) == -1)
1117                 die(gettext("Could not connect to configuration repository: "
1118                     "%s.\n"), scf_strerror(scf_error()));
1119 
1120         flags = SCF_WALK_PROPERTY | SCF_WALK_SERVICE | SCF_WALK_EXPLICIT;
1121 
1122         if (wait) {
1123                 if (uu_list_numnodes(prop_list) > 1)
1124                         usage();
1125 
1126                 if (argc - optind > 1)
1127                         usage();
1128 
1129                 callback = do_wait;
1130 
1131         } else {
1132                 callback = process_fmri;
1133 
1134                 flags |= SCF_WALK_MULTIPLE;
1135         }
1136 
1137         if ((err = scf_walk_fmri(hndl, argc - optind, argv + optind, flags,
1138             callback, NULL, &return_code, warn)) != 0) {
1139                 warn(gettext("failed to iterate over instances: %s\n"),
1140                     scf_strerror(err));
1141                 return_code = UU_EXIT_FATAL;
1142         }
1143 
1144         scf_handle_destroy(hndl);
1145 
1146         return (return_code);
1147 }