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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright (c) 2011, Joyent, Inc. All rights reserved.
  25  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  26  * Copyright (c) 2015, 2016 by Delphix. All rights reserved.
  27  */
  28 
  29 /*
  30  * svcs - display attributes of service instances
  31  *
  32  * We have two output formats and six instance selection mechanisms.  The
  33  * primary output format is a line of attributes (selected by -o), possibly
  34  * followed by process description lines (if -p is specified), for each
  35  * instance selected.  The columns available to display are described by the
  36  * struct column columns array.  The columns to actually display are kept in
  37  * the opt_columns array as indicies into the columns array.  The selection
  38  * mechanisms available for this format are service FMRIs (selects all child
  39  * instances), instance FMRIs, instance FMRI glob patterns, instances with
  40  * a certain restarter (-R), dependencies of instances (-d), and dependents of
  41  * instances (-D).  Since the lines must be sorted (per -sS), we'll just stick
  42  * each into a data structure and print them in order when we're done.  To
  43  * avoid listing the same instance twice (when -d and -D aren't given), we'll
  44  * use a hash table of FMRIs to record that we've listed (added to the tree)
  45  * an instance.
  46  *
  47  * The secondary output format (-l "long") is a paragraph of text for the
  48  * services or instances selected.  Not needing to be sorted, it's implemented
  49  * by just calling print_detailed() for each FMRI given.
  50  */
  51 
  52 #include "svcs.h"
  53 #include "notify_params.h"
  54 
  55 /* Get the byteorder macros to ease sorting. */
  56 #include <sys/types.h>
  57 #include <netinet/in.h>
  58 #include <inttypes.h>
  59 
  60 #include <sys/contract.h>
  61 #include <sys/ctfs.h>
  62 #include <sys/stat.h>
  63 
  64 #include <assert.h>
  65 #include <errno.h>
  66 #include <fcntl.h>
  67 #include <fnmatch.h>
  68 #include <libcontract.h>
  69 #include <libcontract_priv.h>
  70 #include <libintl.h>
  71 #include <libscf.h>
  72 #include <libscf_priv.h>
  73 #include <libuutil.h>
  74 #include <libnvpair.h>
  75 #include <locale.h>
  76 #include <procfs.h>
  77 #include <stdarg.h>
  78 #include <stdio.h>
  79 #include <stdlib.h>
  80 #include <strings.h>
  81 #include <time.h>
  82 #include <libzonecfg.h>
  83 #include <zone.h>
  84 
  85 #ifndef TEXT_DOMAIN
  86 #define TEXT_DOMAIN     "SUNW_OST_OSCMD"
  87 #endif /* TEXT_DOMAIN */
  88 
  89 #define LEGACY_UNKNOWN  "unknown"
  90 
  91 /* Flags for pg_get_single_val() */
  92 #define EMPTY_OK        0x01
  93 #define MULTI_OK        0x02
  94 
  95 
  96 /*
  97  * An AVL-storable node for output lines and the keys to sort them by.
  98  */
  99 struct avl_string {
 100         uu_avl_node_t   node;
 101         char            *key;
 102         char            *str;
 103 };
 104 
 105 /*
 106  * For lists of parsed restarter FMRIs.
 107  */
 108 struct pfmri_list {
 109         const char              *scope;
 110         const char              *service;
 111         const char              *instance;
 112         struct pfmri_list       *next;
 113 };
 114 
 115 
 116 /*
 117  * Globals
 118  */
 119 scf_handle_t *h;
 120 static scf_propertygroup_t *g_pg;
 121 static scf_property_t *g_prop;
 122 static scf_value_t *g_val;
 123 
 124 static size_t line_sz;                  /* Bytes in the header line. */
 125 static size_t sortkey_sz;               /* Bytes in sort keys. */
 126 static uu_avl_pool_t *lines_pool;
 127 static uu_avl_t *lines;                 /* Output lines. */
 128 int exit_status;
 129 ssize_t max_scf_name_length;
 130 ssize_t max_scf_value_length;
 131 ssize_t max_scf_fmri_length;
 132 static ssize_t max_scf_type_length;
 133 static time_t now;
 134 static struct pfmri_list *restarters = NULL;
 135 static int first_paragraph = 1;         /* For -l mode. */
 136 static char *common_name_buf;           /* Sized for maximal length value. */
 137 char *locale;                           /* Current locale. */
 138 char *g_zonename;                       /* zone being operated upon */
 139 
 140 /*
 141  * Pathname storage for path generated from the fmri.
 142  * Used for reading the ctid and (start) pid files for an inetd service.
 143  */
 144 static char genfmri_filename[MAXPATHLEN] = "";
 145 
 146 /* Options */
 147 static int *opt_columns = NULL;         /* Indices into columns to display. */
 148 static int opt_cnum = 0;
 149 static int opt_processes = 0;           /* Print processes? */
 150 static int opt_scripted = 0;            /* No header, tabs as separators. */
 151 static int *opt_sort = NULL;            /* Indices into columns to sort. */
 152 static int opt_snum = 0;
 153 static int opt_nstate_shown = 0;        /* Will nstate be shown? */
 154 static int opt_verbose = 0;
 155 static char *opt_zone;                  /* zone selected, if any */
 156 
 157 /* Minimize string constants. */
 158 static const char * const scf_property_state = SCF_PROPERTY_STATE;
 159 static const char * const scf_property_next_state = SCF_PROPERTY_NEXT_STATE;
 160 static const char * const scf_property_contract = SCF_PROPERTY_CONTRACT;
 161 
 162 
 163 /*
 164  * Utility functions
 165  */
 166 
 167 /*
 168  * For unexpected libscf errors.  The ending newline is necessary to keep
 169  * uu_die() from appending the errno error.
 170  */
 171 #ifndef NDEBUG
 172 void
 173 do_scfdie(const char *file, int line)
 174 {
 175         uu_die(gettext("%s:%d: Unexpected libscf error: %s.  Exiting.\n"),
 176             file, line, scf_strerror(scf_error()));
 177 }
 178 #else
 179 void
 180 scfdie(void)
 181 {
 182         uu_die(gettext("Unexpected libscf error: %s.  Exiting.\n"),
 183             scf_strerror(scf_error()));
 184 }
 185 #endif
 186 
 187 void *
 188 safe_malloc(size_t sz)
 189 {
 190         void *ptr;
 191 
 192         ptr = malloc(sz);
 193         if (ptr == NULL)
 194                 uu_die(gettext("Out of memory"));
 195 
 196         return (ptr);
 197 }
 198 
 199 char *
 200 safe_strdup(const char *str)
 201 {
 202         char *cp;
 203 
 204         cp = strdup(str);
 205         if (cp == NULL)
 206                 uu_die(gettext("Out of memory.\n"));
 207 
 208         return (cp);
 209 }
 210 
 211 /*
 212  * FMRI hashtable.  For uniquifing listings.
 213  */
 214 
 215 struct ht_elem {
 216         const char      *fmri;
 217         struct ht_elem  *next;
 218 };
 219 
 220 static struct ht_elem   **ht_buckets = NULL;
 221 static uint_t           ht_buckets_num = 0;
 222 static uint_t           ht_num;
 223 
 224 static void
 225 ht_free(void)
 226 {
 227         struct ht_elem *elem, *next;
 228         int i;
 229 
 230         for (i = 0; i < ht_buckets_num; i++) {
 231                 for (elem = ht_buckets[i]; elem != NULL; elem = next) {
 232                         next = elem->next;
 233                         free((char *)elem->fmri);
 234                         free(elem);
 235                 }
 236         }
 237 
 238         free(ht_buckets);
 239         ht_buckets_num = 0;
 240         ht_buckets = NULL;
 241 }
 242 
 243 static void
 244 ht_init(void)
 245 {
 246         assert(ht_buckets == NULL);
 247 
 248         ht_buckets_num = 8;
 249         ht_buckets = safe_malloc(sizeof (*ht_buckets) * ht_buckets_num);
 250         bzero(ht_buckets, sizeof (*ht_buckets) * ht_buckets_num);
 251         ht_num = 0;
 252 }
 253 
 254 static uint_t
 255 ht_hash_fmri(const char *fmri)
 256 {
 257         uint_t h = 0, g;
 258         const char *p, *k;
 259 
 260         /* All FMRIs begin with svc:/, so skip that part. */
 261         assert(strncmp(fmri, "svc:/", sizeof ("svc:/") - 1) == 0);
 262         k = fmri + sizeof ("svc:/") - 1;
 263 
 264         /*
 265          * Generic hash function from uts/common/os/modhash.c.
 266          */
 267         for (p = k; *p != '\0'; ++p) {
 268                 h = (h << 4) + *p;
 269                 if ((g = (h & 0xf0000000)) != 0) {
 270                         h ^= (g >> 24);
 271                         h ^= g;
 272                 }
 273         }
 274 
 275         return (h);
 276 }
 277 
 278 static void
 279 ht_grow()
 280 {
 281         uint_t new_ht_buckets_num;
 282         struct ht_elem **new_ht_buckets;
 283         int i;
 284 
 285         new_ht_buckets_num = ht_buckets_num * 2;
 286         assert(new_ht_buckets_num > ht_buckets_num);
 287         new_ht_buckets =
 288             safe_malloc(sizeof (*new_ht_buckets) * new_ht_buckets_num);
 289         bzero(new_ht_buckets, sizeof (*new_ht_buckets) * new_ht_buckets_num);
 290 
 291         for (i = 0; i < ht_buckets_num; ++i) {
 292                 struct ht_elem *elem, *next;
 293 
 294                 for (elem = ht_buckets[i]; elem != NULL; elem = next) {
 295                         uint_t h;
 296 
 297                         next = elem->next;
 298 
 299                         h = ht_hash_fmri(elem->fmri);
 300 
 301                         elem->next =
 302                             new_ht_buckets[h & (new_ht_buckets_num - 1)];
 303                         new_ht_buckets[h & (new_ht_buckets_num - 1)] = elem;
 304                 }
 305         }
 306 
 307         free(ht_buckets);
 308 
 309         ht_buckets = new_ht_buckets;
 310         ht_buckets_num = new_ht_buckets_num;
 311 }
 312 
 313 /*
 314  * Add an FMRI to the hash table.  Returns 1 if it was already there,
 315  * 0 otherwise.
 316  */
 317 static int
 318 ht_add(const char *fmri)
 319 {
 320         uint_t h;
 321         struct ht_elem *elem;
 322 
 323         h = ht_hash_fmri(fmri);
 324 
 325         elem = ht_buckets[h & (ht_buckets_num - 1)];
 326 
 327         for (; elem != NULL; elem = elem->next) {
 328                 if (strcmp(elem->fmri, fmri) == 0)
 329                         return (1);
 330         }
 331 
 332         /* Grow when average chain length is over 3. */
 333         if (ht_num > 3 * ht_buckets_num)
 334                 ht_grow();
 335 
 336         ++ht_num;
 337 
 338         elem = safe_malloc(sizeof (*elem));
 339         elem->fmri = strdup(fmri);
 340         elem->next = ht_buckets[h & (ht_buckets_num - 1)];
 341         ht_buckets[h & (ht_buckets_num - 1)] = elem;
 342 
 343         return (0);
 344 }
 345 
 346 
 347 
 348 /*
 349  * Convenience libscf wrapper functions.
 350  */
 351 
 352 /*
 353  * Get the single value of the named property in the given property group,
 354  * which must have type ty, and put it in *vp.  If ty is SCF_TYPE_ASTRING, vp
 355  * is taken to be a char **, and sz is the size of the buffer.  sz is unused
 356  * otherwise.  Return 0 on success, -1 if the property doesn't exist, has the
 357  * wrong type, or doesn't have a single value.  If flags has EMPTY_OK, don't
 358  * complain if the property has no values (but return nonzero).  If flags has
 359  * MULTI_OK and the property has multiple values, succeed with E2BIG.
 360  */
 361 int
 362 pg_get_single_val(scf_propertygroup_t *pg, const char *propname, scf_type_t ty,
 363     void *vp, size_t sz, uint_t flags)
 364 {
 365         char *buf, root[MAXPATHLEN];
 366         size_t buf_sz;
 367         int ret = -1, r;
 368         boolean_t multi = B_FALSE;
 369 
 370         assert((flags & ~(EMPTY_OK | MULTI_OK)) == 0);
 371 
 372         if (scf_pg_get_property(pg, propname, g_prop) == -1) {
 373                 if (scf_error() != SCF_ERROR_NOT_FOUND)
 374                         scfdie();
 375 
 376                 goto out;
 377         }
 378 
 379         if (scf_property_is_type(g_prop, ty) != SCF_SUCCESS) {
 380                 if (scf_error() == SCF_ERROR_TYPE_MISMATCH)
 381                         goto misconfigured;
 382                 scfdie();
 383         }
 384 
 385         if (scf_property_get_value(g_prop, g_val) != SCF_SUCCESS) {
 386                 switch (scf_error()) {
 387                 case SCF_ERROR_NOT_FOUND:
 388                         if (flags & EMPTY_OK)
 389                                 goto out;
 390                         goto misconfigured;
 391 
 392                 case SCF_ERROR_CONSTRAINT_VIOLATED:
 393                         if (flags & MULTI_OK) {
 394                                 multi = B_TRUE;
 395                                 break;
 396                         }
 397                         goto misconfigured;
 398 
 399                 case SCF_ERROR_PERMISSION_DENIED:
 400                 default:
 401                         scfdie();
 402                 }
 403         }
 404 
 405         switch (ty) {
 406         case SCF_TYPE_ASTRING:
 407                 r = scf_value_get_astring(g_val, vp, sz) > 0 ? SCF_SUCCESS : -1;
 408                 break;
 409 
 410         case SCF_TYPE_BOOLEAN:
 411                 r = scf_value_get_boolean(g_val, (uint8_t *)vp);
 412                 break;
 413 
 414         case SCF_TYPE_COUNT:
 415                 r = scf_value_get_count(g_val, (uint64_t *)vp);
 416                 break;
 417 
 418         case SCF_TYPE_INTEGER:
 419                 r = scf_value_get_integer(g_val, (int64_t *)vp);
 420                 break;
 421 
 422         case SCF_TYPE_TIME: {
 423                 int64_t sec;
 424                 int32_t ns;
 425                 r = scf_value_get_time(g_val, &sec, &ns);
 426                 ((struct timeval *)vp)->tv_sec = sec;
 427                 ((struct timeval *)vp)->tv_usec = ns / 1000;
 428                 break;
 429         }
 430 
 431         case SCF_TYPE_USTRING:
 432                 r = scf_value_get_ustring(g_val, vp, sz) > 0 ? SCF_SUCCESS : -1;
 433                 break;
 434 
 435         default:
 436 #ifndef NDEBUG
 437                 uu_warn("%s:%d: Unknown type %d.\n", __FILE__, __LINE__, ty);
 438 #endif
 439                 abort();
 440         }
 441         if (r != SCF_SUCCESS)
 442                 scfdie();
 443 
 444         ret = multi ? E2BIG : 0;
 445         goto out;
 446 
 447 misconfigured:
 448         buf_sz = max_scf_fmri_length + 1;
 449         buf = safe_malloc(buf_sz);
 450         if (scf_property_to_fmri(g_prop, buf, buf_sz) == -1)
 451                 scfdie();
 452 
 453         uu_warn(gettext("Property \"%s\" is misconfigured.\n"), buf);
 454 
 455         free(buf);
 456 
 457 out:
 458         if (ret != 0 || g_zonename == NULL ||
 459             (strcmp(propname, SCF_PROPERTY_LOGFILE) != 0 &&
 460             strcmp(propname, SCF_PROPERTY_ALT_LOGFILE) != 0))
 461                 return (ret);
 462 
 463         /*
 464          * If we're here, we have a log file and we have specified a zone.
 465          * As a convenience, we're going to prepend the zone path to the
 466          * name of the log file.
 467          */
 468         root[0] = '\0';
 469         (void) zone_get_rootpath(g_zonename, root, sizeof (root));
 470         (void) strlcat(root, vp, sizeof (root));
 471         (void) snprintf(vp, sz, "%s", root);
 472 
 473         return (ret);
 474 }
 475 
 476 static scf_snapshot_t *
 477 get_running_snapshot(scf_instance_t *inst)
 478 {
 479         scf_snapshot_t *snap;
 480 
 481         snap = scf_snapshot_create(h);
 482         if (snap == NULL)
 483                 scfdie();
 484 
 485         if (scf_instance_get_snapshot(inst, "running", snap) == 0)
 486                 return (snap);
 487 
 488         if (scf_error() != SCF_ERROR_NOT_FOUND)
 489                 scfdie();
 490 
 491         scf_snapshot_destroy(snap);
 492         return (NULL);
 493 }
 494 
 495 /*
 496  * As pg_get_single_val(), except look the property group up in an
 497  * instance.  If "use_running" is set, and the running snapshot exists,
 498  * do a composed lookup there.  Otherwise, do an (optionally composed)
 499  * lookup on the current values.  Note that lookups using snapshots are
 500  * always composed.
 501  */
 502 int
 503 inst_get_single_val(scf_instance_t *inst, const char *pgname,
 504     const char *propname, scf_type_t ty, void *vp, size_t sz, uint_t flags,
 505     int use_running, int composed)
 506 {
 507         scf_snapshot_t *snap = NULL;
 508         int r;
 509 
 510         if (use_running)
 511                 snap = get_running_snapshot(inst);
 512         if (composed || use_running)
 513                 r = scf_instance_get_pg_composed(inst, snap, pgname, g_pg);
 514         else
 515                 r = scf_instance_get_pg(inst, pgname, g_pg);
 516         if (snap)
 517                 scf_snapshot_destroy(snap);
 518         if (r == -1)
 519                 return (-1);
 520 
 521         r = pg_get_single_val(g_pg, propname, ty, vp, sz, flags);
 522 
 523         return (r);
 524 }
 525 
 526 static int
 527 instance_enabled(scf_instance_t *inst, boolean_t temp)
 528 {
 529         uint8_t b;
 530 
 531         if (inst_get_single_val(inst,
 532             temp ? SCF_PG_GENERAL_OVR : SCF_PG_GENERAL, SCF_PROPERTY_ENABLED,
 533             SCF_TYPE_BOOLEAN, &b, 0, 0, 0, 0) != 0)
 534                 return (-1);
 535 
 536         return (b ? 1 : 0);
 537 }
 538 
 539 /*
 540  * Get a string property from the restarter property group of the given
 541  * instance.  Return an empty string on normal problems.
 542  */
 543 static void
 544 get_restarter_string_prop(scf_instance_t *inst, const char *pname,
 545     char *buf, size_t buf_sz)
 546 {
 547         if (inst_get_single_val(inst, SCF_PG_RESTARTER, pname,
 548             SCF_TYPE_ASTRING, buf, buf_sz, 0, 0, 1) != 0)
 549                 *buf = '\0';
 550 }
 551 
 552 static int
 553 get_restarter_time_prop(scf_instance_t *inst, const char *pname,
 554     struct timeval *tvp, int ok_if_empty)
 555 {
 556         int r;
 557 
 558         r = inst_get_single_val(inst, SCF_PG_RESTARTER, pname, SCF_TYPE_TIME,
 559             tvp, NULL, ok_if_empty ? EMPTY_OK : 0, 0, 1);
 560 
 561         return (r == 0 ? 0 : -1);
 562 }
 563 
 564 static int
 565 get_restarter_count_prop(scf_instance_t *inst, const char *pname, uint64_t *cp,
 566     uint_t flags)
 567 {
 568         return (inst_get_single_val(inst, SCF_PG_RESTARTER, pname,
 569             SCF_TYPE_COUNT, cp, 0, flags, 0, 1));
 570 }
 571 
 572 
 573 /*
 574  * Generic functions
 575  */
 576 
 577 /*
 578  * Return an array of pids associated with the given contract id.
 579  * Returned pids are added to the end of the pidsp array.
 580  */
 581 static void
 582 ctid_to_pids(uint64_t c, pid_t **pidsp, uint_t *np)
 583 {
 584         ct_stathdl_t ctst;
 585         uint_t m;
 586         int fd;
 587         int r, err;
 588         pid_t *pids;
 589 
 590         fd = contract_open(c, NULL, "status", O_RDONLY);
 591         if (fd < 0)
 592                 return;
 593 
 594         err = ct_status_read(fd, CTD_ALL, &ctst);
 595         if (err != 0) {
 596                 uu_warn(gettext("Could not read status of contract "
 597                     "%ld: %s.\n"), c, strerror(err));
 598                 (void) close(fd);
 599                 return;
 600         }
 601 
 602         (void) close(fd);
 603 
 604         r = ct_pr_status_get_members(ctst, &pids, &m);
 605         assert(r == 0);
 606 
 607         if (m == 0) {
 608                 ct_status_free(ctst);
 609                 return;
 610         }
 611 
 612         *pidsp = realloc(*pidsp, (*np + m) * sizeof (*pidsp));
 613         if (*pidsp == NULL)
 614                 uu_die(gettext("Out of memory"));
 615 
 616         bcopy(pids, *pidsp + *np, m * sizeof (*pids));
 617         *np += m;
 618 
 619         ct_status_free(ctst);
 620 }
 621 
 622 static int
 623 propvals_to_pids(scf_propertygroup_t *pg, const char *pname, pid_t **pidsp,
 624     uint_t *np, scf_property_t *prop, scf_value_t *val, scf_iter_t *iter)
 625 {
 626         scf_type_t ty;
 627         uint64_t c;
 628         int r;
 629 
 630         if (scf_pg_get_property(pg, pname, prop) != 0) {
 631                 if (scf_error() != SCF_ERROR_NOT_FOUND)
 632                         scfdie();
 633 
 634                 return (ENOENT);
 635         }
 636 
 637         if (scf_property_type(prop, &ty) != 0)
 638                 scfdie();
 639 
 640         if (ty != SCF_TYPE_COUNT)
 641                 return (EINVAL);
 642 
 643         if (scf_iter_property_values(iter, prop) != 0)
 644                 scfdie();
 645 
 646         for (;;) {
 647                 r = scf_iter_next_value(iter, val);
 648                 if (r == -1)
 649                         scfdie();
 650                 if (r == 0)
 651                         break;
 652 
 653                 if (scf_value_get_count(val, &c) != 0)
 654                         scfdie();
 655 
 656                 ctid_to_pids(c, pidsp, np);
 657         }
 658 
 659         return (0);
 660 }
 661 
 662 /*
 663  * Check if instance has general/restarter property that matches
 664  * given string.  Restarter string must be in canonified form.
 665  * Returns 0 for success; -1 otherwise.
 666  */
 667 static int
 668 check_for_restarter(scf_instance_t *inst, const char *restarter)
 669 {
 670         char    *fmri_buf;
 671         char    *fmri_buf_canonified = NULL;
 672         int     ret = -1;
 673 
 674         if (inst == NULL)
 675                 return (-1);
 676 
 677         /* Get restarter */
 678         fmri_buf = safe_malloc(max_scf_fmri_length + 1);
 679         if (inst_get_single_val(inst, SCF_PG_GENERAL,
 680             SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, fmri_buf,
 681             max_scf_fmri_length + 1, 0, 0, 1) != 0)
 682                 goto out;
 683 
 684         fmri_buf_canonified = safe_malloc(max_scf_fmri_length + 1);
 685         if (scf_canonify_fmri(fmri_buf, fmri_buf_canonified,
 686             (max_scf_fmri_length + 1)) < 0)
 687                 goto out;
 688 
 689         if (strcmp(fmri_buf, restarter) == 0)
 690                 ret = 0;
 691 
 692 out:
 693         free(fmri_buf);
 694         if (fmri_buf_canonified)
 695                 free(fmri_buf_canonified);
 696         return (ret);
 697 }
 698 
 699 /*
 700  * Common code that is used by ctids_by_restarter and pids_by_restarter.
 701  * Checks for a common restarter and if one is available, it generates
 702  * the appropriate filename using wip->fmri and stores that in the
 703  * global genfmri_filename.
 704  *
 705  * Restarters currently supported are: svc:/network/inetd:default
 706  * If a restarter specific action is available, then restarter_spec
 707  * is set to 1.  If a restarter specific action is not available, then
 708  * restarter_spec is set to 0 and a -1 is returned.
 709  *
 710  * Returns:
 711  * 0 if success: restarter specific action found and filename generated
 712  * -1 if restarter specific action not found,
 713  *    if restarter specific action found but an error was encountered
 714  *    during the generation of the wip->fmri based filename
 715  */
 716 static int
 717 common_by_restarter(scf_instance_t *inst, const char *fmri,
 718     int *restarter_specp)
 719 {
 720         int             ret = -1;
 721         int             r;
 722 
 723         /* Check for inetd specific restarter */
 724         if (check_for_restarter(inst, "svc:/network/inetd:default") != 0) {
 725                 *restarter_specp = 0;
 726                 return (ret);
 727         }
 728 
 729         *restarter_specp = 1;
 730 
 731         /* Get the ctid filename associated with this instance */
 732         r = gen_filenms_from_fmri(fmri, "ctid", genfmri_filename, NULL);
 733 
 734         switch (r) {
 735         case 0:
 736                 break;
 737 
 738         case -1:
 739                 /*
 740                  * Unable to get filename from fmri.  Print warning
 741                  * and return failure with no ctids.
 742                  */
 743                 uu_warn(gettext("Unable to read contract ids for %s -- "
 744                     "FMRI is too long\n"), fmri);
 745                 return (ret);
 746 
 747         case -2:
 748                 /*
 749                  * The directory didn't exist, so no contracts.
 750                  * Return failure with no ctids.
 751                  */
 752                 return (ret);
 753 
 754         default:
 755                 uu_warn(gettext("%s:%d: gen_filenms_from_fmri() failed with "
 756                     "unknown error %d\n"), __FILE__, __LINE__, r);
 757                 abort();
 758         }
 759 
 760         return (0);
 761 
 762 }
 763 
 764 /*
 765  * Get or print a contract id using a restarter specific action.
 766  *
 767  * If the print_flag is not set, this routine gets the single contract
 768  * id associated with this instance.
 769  * If the print flag is set, then print each contract id found.
 770  *
 771  * Returns:
 772  * 0 if success: restarter specific action found and used with no error
 773  * -1 if restarter specific action not found
 774  * -1 if restarter specific action found, but there was a failure
 775  * -1 if print flag is not set and no contract id is found or multiple
 776  *    contract ids were found
 777  * E2BIG if print flag is not set, MULTI_OK bit in flag is set and multiple
 778  *    contract ids were found
 779  */
 780 static int
 781 ctids_by_restarter(scf_walkinfo_t *wip, uint64_t *cp, int print_flag,
 782     uint_t flags, int *restarter_specp, void (*callback_header)(),
 783     void (*callback_ctid)(uint64_t))
 784 {
 785         FILE            *fp;
 786         int             ret = -1;
 787         int             fscanf_ret;
 788         uint64_t        cp2;
 789         int             rest_ret;
 790 
 791         /* Check if callbacks are needed and were passed in */
 792         if (print_flag) {
 793                 if ((callback_header == NULL) || (callback_ctid == NULL))
 794                         return (ret);
 795         }
 796 
 797         /* Check for restarter specific action and generation of filename */
 798         rest_ret = common_by_restarter(wip->inst, wip->fmri, restarter_specp);
 799         if (rest_ret != 0)
 800                 return (rest_ret);
 801 
 802         /*
 803          * If fopen fails, then ctid file hasn't been created yet.
 804          * If print_flag is set, this is ok; otherwise fail.
 805          */
 806         if ((fp = fopen(genfmri_filename, "r")) == NULL) {
 807                 if (print_flag)
 808                         return (0);
 809                 goto out;
 810         }
 811 
 812         if (print_flag) {
 813                 /*
 814                  * Print all contract ids that are found.
 815                  * First callback to print ctid header.
 816                  */
 817                 callback_header();
 818 
 819                 /* fscanf may not set errno, so be sure to clear it first */
 820                 errno = 0;
 821                 while ((fscanf_ret = fscanf(fp, "%llu", cp)) == 1) {
 822                         /* Callback to print contract id */
 823                         callback_ctid(*cp);
 824                         errno = 0;
 825                 }
 826                 /* EOF is not a failure when no errno. */
 827                 if ((fscanf_ret != EOF) || (errno != 0)) {
 828                         uu_die(gettext("Unable to read ctid file for %s"),
 829                             wip->fmri);
 830                 }
 831                 (void) putchar('\n');
 832                 ret = 0;
 833         } else {
 834                 /* Must find 1 ctid or fail */
 835                 if (fscanf(fp, "%llu", cp) == 1) {
 836                         /* If 2nd ctid found - fail */
 837                         if (fscanf(fp, "%llu", &cp2) == 1) {
 838                                 if (flags & MULTI_OK)
 839                                         ret = E2BIG;
 840                         } else {
 841                                 /* Success - found only 1 ctid */
 842                                 ret = 0;
 843                         }
 844                 }
 845         }
 846         (void) fclose(fp);
 847 
 848 out:
 849         return (ret);
 850 }
 851 
 852 /*
 853  * Get the process ids associated with an instance using a restarter
 854  * specific action.
 855  *
 856  * Returns:
 857  *      0 if success: restarter specific action found and used with no error
 858  *      -1 restarter specific action not found or if failure
 859  */
 860 static int
 861 pids_by_restarter(scf_instance_t *inst, const char *fmri,
 862     pid_t **pids, uint_t *np, int *restarter_specp)
 863 {
 864         uint64_t        c;
 865         FILE            *fp;
 866         int             fscanf_ret;
 867         int             rest_ret;
 868 
 869         /* Check for restarter specific action and generation of filename */
 870         rest_ret = common_by_restarter(inst, fmri, restarter_specp);
 871         if (rest_ret != 0)
 872                 return (rest_ret);
 873 
 874         /*
 875          * If fopen fails with ENOENT then the ctid file hasn't been
 876          * created yet so return success.
 877          * For all other errors - fail with uu_die.
 878          */
 879         if ((fp = fopen(genfmri_filename, "r")) == NULL) {
 880                 if (errno == ENOENT)
 881                         return (0);
 882                 uu_die(gettext("Unable to open ctid file for %s"), fmri);
 883         }
 884 
 885         /* fscanf may not set errno, so be sure to clear it first */
 886         errno = 0;
 887         while ((fscanf_ret = fscanf(fp, "%llu", &c)) == 1) {
 888                 if (c == 0) {
 889                         (void) fclose(fp);
 890                         uu_die(gettext("ctid file for %s has corrupt data"),
 891                             fmri);
 892                 }
 893                 ctid_to_pids(c, pids, np);
 894                 errno = 0;
 895         }
 896         /* EOF is not a failure when no errno. */
 897         if ((fscanf_ret != EOF) || (errno != 0)) {
 898                 uu_die(gettext("Unable to read ctid file for %s"), fmri);
 899         }
 900 
 901         (void) fclose(fp);
 902         return (0);
 903 }
 904 
 905 static int
 906 instance_processes(scf_instance_t *inst, const char *fmri,
 907     pid_t **pids, uint_t *np)
 908 {
 909         scf_iter_t *iter;
 910         int ret;
 911         int restarter_spec;
 912 
 913         /* Use the restarter specific get pids routine, if available. */
 914         ret = pids_by_restarter(inst, fmri, pids, np, &restarter_spec);
 915         if (restarter_spec == 1)
 916                 return (ret);
 917 
 918         if ((iter = scf_iter_create(h)) == NULL)
 919                 scfdie();
 920 
 921         if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, g_pg) == 0) {
 922                 *pids = NULL;
 923                 *np = 0;
 924 
 925                 (void) propvals_to_pids(g_pg, scf_property_contract, pids, np,
 926                     g_prop, g_val, iter);
 927 
 928                 (void) propvals_to_pids(g_pg, SCF_PROPERTY_TRANSIENT_CONTRACT,
 929                     pids, np, g_prop, g_val, iter);
 930 
 931                 ret = 0;
 932         } else {
 933                 if (scf_error() != SCF_ERROR_NOT_FOUND)
 934                         scfdie();
 935 
 936                 ret = -1;
 937         }
 938 
 939         scf_iter_destroy(iter);
 940 
 941         return (ret);
 942 }
 943 
 944 static int
 945 get_psinfo(pid_t pid, psinfo_t *psip)
 946 {
 947         char path[100];
 948         int fd;
 949 
 950         (void) snprintf(path, sizeof (path), "/proc/%lu/psinfo", pid);
 951 
 952         fd = open64(path, O_RDONLY);
 953         if (fd < 0)
 954                 return (-1);
 955 
 956         if (read(fd, psip, sizeof (*psip)) < 0)
 957                 uu_die(gettext("Could not read info for process %lu"), pid);
 958 
 959         (void) close(fd);
 960 
 961         return (0);
 962 }
 963 
 964 
 965 
 966 /*
 967  * Column sprint and sortkey functions
 968  */
 969 
 970 struct column {
 971         const char *name;
 972         int width;
 973 
 974         /*
 975          * This function should write the value for the column into buf, and
 976          * grow or allocate buf accordingly.  It should always write at least
 977          * width bytes, blanking unused bytes with spaces.  If the field is
 978          * greater than the column width we allow it to overlap other columns.
 979          * In particular, it shouldn't write any null bytes.  (Though an extra
 980          * null byte past the end is currently tolerated.)  If the property
 981          * group is non-NULL, then we are dealing with a legacy service.
 982          */
 983         void (*sprint)(char **, scf_walkinfo_t *);
 984 
 985         int sortkey_width;
 986 
 987         /*
 988          * This function should write sortkey_width bytes into buf which will
 989          * cause memcmp() to sort it properly.  (Unlike sprint() above,
 990          * however, an extra null byte may overrun the buffer.)  The second
 991          * argument controls whether the results are sorted in forward or
 992          * reverse order.
 993          */
 994         void (*get_sortkey)(char *, int, scf_walkinfo_t *);
 995 };
 996 
 997 static void
 998 reverse_bytes(char *buf, size_t len)
 999 {
1000         int i;
1001 
1002         for (i = 0; i < len; ++i)
1003                 buf[i] = ~buf[i];
1004 }
1005 
1006 static void
1007 sprint_str(char **buf, const char *str, size_t width)
1008 {
1009         char *newbuf;
1010         size_t newsz = (*buf != NULL ? strlen(*buf) : 0) + 2;
1011 
1012         if (opt_scripted)
1013                 newsz += strlen(str);
1014         else
1015                 newsz += width;
1016 
1017         newbuf = safe_malloc(newsz);
1018 
1019         if (opt_scripted) {
1020                 (void) snprintf(newbuf, newsz, "%s%s%s",
1021                     *buf != NULL ? *buf : "",
1022                     *buf != NULL ? "\t" : "",
1023                     str);
1024         } else {
1025                 (void) snprintf(newbuf, newsz, "%s%-*s ",
1026                     *buf != NULL ? *buf : "", width, str);
1027         }
1028 
1029         free(*buf);
1030         *buf = newbuf;
1031 }
1032 
1033 /* CTID */
1034 #define CTID_COLUMN_WIDTH               6
1035 #define CTID_COLUMN_BUFSIZE             20      /* max ctid_t + space + \0 */
1036 
1037 static void
1038 sprint_ctid(char **buf, scf_walkinfo_t *wip)
1039 {
1040         int r;
1041         uint64_t c;
1042         char ctid_buf[CTID_COLUMN_BUFSIZE] = { 0 };
1043         char *cstr;
1044         int restarter_spec;
1045 
1046         /*
1047          * Use the restarter specific get pids routine, if available.
1048          * Only check for non-legacy services (wip->pg == 0).
1049          */
1050         if (wip->pg != NULL) {
1051                 r = pg_get_single_val(wip->pg, scf_property_contract,
1052                     SCF_TYPE_COUNT, &c, 0, EMPTY_OK | MULTI_OK);
1053         } else {
1054                 r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec,
1055                     NULL, NULL);
1056                 if (restarter_spec == 0) {
1057                         /* No restarter specific routine */
1058                         r = get_restarter_count_prop(wip->inst,
1059                             scf_property_contract, &c, EMPTY_OK | MULTI_OK);
1060                 }
1061         }
1062 
1063         if (r == 0 || r == E2BIG) {
1064                 if (r == E2BIG)
1065                         ctid_buf[CTID_COLUMN_BUFSIZE - 2] = '*';
1066                 cstr = ulltostr(c, &ctid_buf[CTID_COLUMN_BUFSIZE - 2]);
1067                 sprint_str(buf, cstr, CTID_COLUMN_WIDTH);
1068         } else {
1069                 sprint_str(buf, "-", CTID_COLUMN_WIDTH);
1070         }
1071 }
1072 
1073 #define CTID_SORTKEY_WIDTH              (sizeof (uint64_t))
1074 
1075 static void
1076 sortkey_ctid(char *buf, int reverse, scf_walkinfo_t *wip)
1077 {
1078         int r;
1079         uint64_t c;
1080         int restarter_spec;
1081 
1082         /*
1083          * Use the restarter specific get pids routine, if available.
1084          * Only check for non-legacy services (wip->pg == 0).
1085          */
1086         if (wip->pg != NULL) {
1087                 r = pg_get_single_val(wip->pg, scf_property_contract,
1088                     SCF_TYPE_COUNT, &c, 0, EMPTY_OK);
1089         } else {
1090                 r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec,
1091                     NULL, NULL);
1092                 if (restarter_spec == 0) {
1093                         /* No restarter specific routine */
1094                         r = get_restarter_count_prop(wip->inst,
1095                             scf_property_contract, &c, EMPTY_OK);
1096                 }
1097         }
1098 
1099         if (r == 0) {
1100                 /*
1101                  * Use the id itself, but it must be big-endian for this to
1102                  * work.
1103                  */
1104                 c = BE_64(c);
1105 
1106                 bcopy(&c, buf, CTID_SORTKEY_WIDTH);
1107         } else {
1108                 bzero(buf, CTID_SORTKEY_WIDTH);
1109         }
1110 
1111         if (reverse)
1112                 reverse_bytes(buf, CTID_SORTKEY_WIDTH);
1113 }
1114 
1115 /* DESC */
1116 #define DESC_COLUMN_WIDTH       100
1117 
1118 static void
1119 sprint_desc(char **buf, scf_walkinfo_t *wip)
1120 {
1121         char *x;
1122 
1123         if (common_name_buf == NULL)
1124                 common_name_buf = safe_malloc(max_scf_value_length + 1);
1125 
1126         bzero(common_name_buf, max_scf_value_length + 1);
1127 
1128         if (wip->pg != NULL) {
1129                 common_name_buf[0] = '-';
1130         } else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale,
1131             SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0,
1132             1, 1) == -1 &&
1133             inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C",
1134             SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0,
1135             1, 1) == -1) {
1136                 common_name_buf[0] = '-';
1137         }
1138 
1139         /*
1140          * Collapse multi-line tm_common_name values into a single line.
1141          */
1142         for (x = common_name_buf; *x != '\0'; x++)
1143                 if (*x == '\n')
1144                         *x = ' ';
1145 
1146         sprint_str(buf, common_name_buf, DESC_COLUMN_WIDTH);
1147 }
1148 
1149 /* ARGSUSED */
1150 static void
1151 sortkey_desc(char *buf, int reverse, scf_walkinfo_t *wip)
1152 {
1153         bzero(buf, DESC_COLUMN_WIDTH);
1154 }
1155 
1156 /* State columns (STATE, NSTATE, S, N, SN, STA, NSTA) */
1157 
1158 static char
1159 state_to_char(const char *state)
1160 {
1161         if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0)
1162                 return ('u');
1163 
1164         if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0)
1165                 return ('0');
1166 
1167         if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0)
1168                 return ('1');
1169 
1170         if (strcmp(state, SCF_STATE_STRING_MAINT) == 0)
1171                 return ('m');
1172 
1173         if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0)
1174                 return ('d');
1175 
1176         if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)
1177                 return ('D');
1178 
1179         if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0)
1180                 return ('L');
1181 
1182         return ('?');
1183 }
1184 
1185 /* Return true if inst is transitioning. */
1186 static int
1187 transitioning(scf_instance_t *inst)
1188 {
1189         char nstate_name[MAX_SCF_STATE_STRING_SZ];
1190 
1191         get_restarter_string_prop(inst, scf_property_next_state, nstate_name,
1192             sizeof (nstate_name));
1193 
1194         return (state_to_char(nstate_name) != '?');
1195 }
1196 
1197 /* ARGSUSED */
1198 static void
1199 sortkey_states(const char *pname, char *buf, int reverse, scf_walkinfo_t *wip)
1200 {
1201         char state_name[MAX_SCF_STATE_STRING_SZ];
1202 
1203         /*
1204          * Lower numbers are printed first, so these are arranged from least
1205          * interesting ("legacy run") to most interesting (unknown).
1206          */
1207         if (wip->pg == NULL) {
1208                 get_restarter_string_prop(wip->inst, pname, state_name,
1209                     sizeof (state_name));
1210 
1211                 if (strcmp(state_name, SCF_STATE_STRING_ONLINE) == 0)
1212                         *buf = 2;
1213                 else if (strcmp(state_name, SCF_STATE_STRING_DEGRADED) == 0)
1214                         *buf = 3;
1215                 else if (strcmp(state_name, SCF_STATE_STRING_OFFLINE) == 0)
1216                         *buf = 4;
1217                 else if (strcmp(state_name, SCF_STATE_STRING_MAINT) == 0)
1218                         *buf = 5;
1219                 else if (strcmp(state_name, SCF_STATE_STRING_DISABLED) == 0)
1220                         *buf = 1;
1221                 else if (strcmp(state_name, SCF_STATE_STRING_UNINIT) == 0)
1222                         *buf = 6;
1223                 else
1224                         *buf = 7;
1225         } else
1226                 *buf = 0;
1227 
1228         if (reverse)
1229                 *buf = 255 - *buf;
1230 }
1231 
1232 static void
1233 sprint_state(char **buf, scf_walkinfo_t *wip)
1234 {
1235         char state_name[MAX_SCF_STATE_STRING_SZ + 1];
1236 
1237         if (wip->pg == NULL) {
1238                 get_restarter_string_prop(wip->inst, scf_property_state,
1239                     state_name, sizeof (state_name));
1240 
1241                 /* Don't print blank fields, to ease parsing. */
1242                 if (state_name[0] == '\0') {
1243                         state_name[0] = '-';
1244                         state_name[1] = '\0';
1245                 }
1246 
1247                 if (!opt_nstate_shown && transitioning(wip->inst)) {
1248                         /* Append an asterisk if nstate is valid. */
1249                         (void) strcat(state_name, "*");
1250                 }
1251         } else
1252                 (void) strcpy(state_name, SCF_STATE_STRING_LEGACY);
1253 
1254         sprint_str(buf, state_name, MAX_SCF_STATE_STRING_SZ);
1255 }
1256 
1257 static void
1258 sortkey_state(char *buf, int reverse, scf_walkinfo_t *wip)
1259 {
1260         sortkey_states(scf_property_state, buf, reverse, wip);
1261 }
1262 
1263 static void
1264 sprint_nstate(char **buf, scf_walkinfo_t *wip)
1265 {
1266         char next_state_name[MAX_SCF_STATE_STRING_SZ + 1];
1267         boolean_t blank = 0;
1268 
1269         if (wip->pg == NULL) {
1270                 get_restarter_string_prop(wip->inst, scf_property_next_state,
1271                     next_state_name, sizeof (next_state_name));
1272 
1273                 /* Don't print blank fields, to ease parsing. */
1274                 if (next_state_name[0] == '\0' ||
1275                     strcmp(next_state_name, SCF_STATE_STRING_NONE) == 0)
1276                         blank = 1;
1277         } else {
1278                 blank = 1;
1279         }
1280 
1281         if (blank) {
1282                 next_state_name[0] = '-';
1283                 next_state_name[1] = '\0';
1284         }
1285 
1286         sprint_str(buf, next_state_name, MAX_SCF_STATE_STRING_SZ);
1287 }
1288 
1289 static void
1290 sortkey_nstate(char *buf, int reverse, scf_walkinfo_t *wip)
1291 {
1292         sortkey_states(scf_property_next_state, buf, reverse, wip);
1293 }
1294 
1295 static void
1296 sprint_s(char **buf, scf_walkinfo_t *wip)
1297 {
1298         char state_name[MAX_SCF_STATE_STRING_SZ + 1];
1299         char tmp[3] = { 0 };
1300 
1301         if (wip->pg == NULL) {
1302                 get_restarter_string_prop(wip->inst, scf_property_state,
1303                     state_name, sizeof (state_name));
1304                 tmp[0] = state_to_char(state_name);
1305 
1306                 if (!opt_nstate_shown && transitioning(wip->inst))
1307                         tmp[1] = '*';
1308                 else
1309                         tmp[1] = ' ';
1310         } else {
1311                 tmp[0] = 'L';
1312                 tmp[1] = ' ';
1313         }
1314         tmp[2] = ' ';
1315 
1316         sprint_str(buf, tmp, 2);
1317 }
1318 
1319 static void
1320 sprint_n(char **buf, scf_walkinfo_t *wip)
1321 {
1322         char nstate_name[MAX_SCF_STATE_STRING_SZ + 1];
1323         char tmp[2] = { 0 };
1324 
1325         if (wip->pg == NULL) {
1326                 get_restarter_string_prop(wip->inst, scf_property_next_state,
1327                     nstate_name, sizeof (nstate_name));
1328 
1329                 if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0)
1330                         tmp[0] = '-';
1331                 else
1332                         tmp[0] = state_to_char(nstate_name);
1333         } else {
1334                 tmp[0] = '-';
1335         }
1336 
1337         sprint_str(buf, tmp, 1);
1338 }
1339 
1340 static void
1341 sprint_sn(char **buf, scf_walkinfo_t *wip)
1342 {
1343         char state_name[MAX_SCF_STATE_STRING_SZ + 1];
1344         char nstate_name[MAX_SCF_STATE_STRING_SZ + 1];
1345         char tmp[3] = { 0 };
1346 
1347         if (wip->pg == NULL) {
1348                 get_restarter_string_prop(wip->inst, scf_property_state,
1349                     state_name, sizeof (state_name));
1350                 get_restarter_string_prop(wip->inst, scf_property_next_state,
1351                     nstate_name, sizeof (nstate_name));
1352                 tmp[0] = state_to_char(state_name);
1353 
1354                 if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0)
1355                         tmp[1] = '-';
1356                 else
1357                         tmp[1] = state_to_char(nstate_name);
1358         } else {
1359                 tmp[0] = 'L';
1360                 tmp[1] = '-';
1361         }
1362 
1363         sprint_str(buf, tmp, 2);
1364 }
1365 
1366 /* ARGSUSED */
1367 static void
1368 sortkey_sn(char *buf, int reverse, scf_walkinfo_t *wip)
1369 {
1370         sortkey_state(buf, reverse, wip);
1371         sortkey_nstate(buf + 1, reverse, wip);
1372 }
1373 
1374 static const char *
1375 state_abbrev(const char *state)
1376 {
1377         if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0)
1378                 return ("UN");
1379         if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0)
1380                 return ("OFF");
1381         if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0)
1382                 return ("ON");
1383         if (strcmp(state, SCF_STATE_STRING_MAINT) == 0)
1384                 return ("MNT");
1385         if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0)
1386                 return ("DIS");
1387         if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)
1388                 return ("DGD");
1389         if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0)
1390                 return ("LRC");
1391 
1392         return ("?");
1393 }
1394 
1395 static void
1396 sprint_sta(char **buf, scf_walkinfo_t *wip)
1397 {
1398         char state_name[MAX_SCF_STATE_STRING_SZ + 1];
1399         char sta[5] = { 0 };
1400 
1401         if (wip->pg == NULL) {
1402                 get_restarter_string_prop(wip->inst, scf_property_state,
1403                     state_name, sizeof (state_name));
1404         } else {
1405                 (void) strcpy(state_name, SCF_STATE_STRING_LEGACY);
1406         }
1407 
1408         (void) strcpy(sta, state_abbrev(state_name));
1409 
1410         if (wip->pg == NULL && !opt_nstate_shown && transitioning(wip->inst))
1411                 (void) strcat(sta, "*");
1412 
1413         sprint_str(buf, sta, 4);
1414 }
1415 
1416 static void
1417 sprint_nsta(char **buf, scf_walkinfo_t *wip)
1418 {
1419         char state_name[MAX_SCF_STATE_STRING_SZ + 1];
1420 
1421         if (wip->pg == NULL) {
1422                 get_restarter_string_prop(wip->inst, scf_property_next_state,
1423                     state_name, sizeof (state_name));
1424         } else {
1425                 (void) strcpy(state_name, SCF_STATE_STRING_NONE);
1426         }
1427 
1428         if (strcmp(state_name, SCF_STATE_STRING_NONE) == 0)
1429                 sprint_str(buf, "-", 4);
1430         else
1431                 sprint_str(buf, state_abbrev(state_name), 4);
1432 }
1433 
1434 /* FMRI */
1435 #define FMRI_COLUMN_WIDTH       50
1436 static void
1437 sprint_fmri(char **buf, scf_walkinfo_t *wip)
1438 {
1439         char *fmri_buf = safe_malloc(max_scf_fmri_length + 1);
1440 
1441         if (wip->pg == NULL) {
1442                 if (scf_instance_to_fmri(wip->inst, fmri_buf,
1443                     max_scf_fmri_length + 1) == -1)
1444                         scfdie();
1445         } else {
1446                 (void) strcpy(fmri_buf, SCF_FMRI_LEGACY_PREFIX);
1447                 if (pg_get_single_val(wip->pg, SCF_LEGACY_PROPERTY_NAME,
1448                     SCF_TYPE_ASTRING, fmri_buf +
1449                     sizeof (SCF_FMRI_LEGACY_PREFIX) - 1,
1450                     max_scf_fmri_length + 1 -
1451                     (sizeof (SCF_FMRI_LEGACY_PREFIX) - 1), 0) != 0)
1452                         (void) strcat(fmri_buf, LEGACY_UNKNOWN);
1453         }
1454 
1455         sprint_str(buf, fmri_buf, FMRI_COLUMN_WIDTH);
1456         free(fmri_buf);
1457 }
1458 
1459 static void
1460 sortkey_fmri(char *buf, int reverse, scf_walkinfo_t *wip)
1461 {
1462         char *tmp = NULL;
1463 
1464         sprint_fmri(&tmp, wip);
1465         bcopy(tmp, buf, FMRI_COLUMN_WIDTH);
1466         free(tmp);
1467         if (reverse)
1468                 reverse_bytes(buf, FMRI_COLUMN_WIDTH);
1469 }
1470 
1471 /* Component columns */
1472 #define COMPONENT_COLUMN_WIDTH  20
1473 static void
1474 sprint_scope(char **buf, scf_walkinfo_t *wip)
1475 {
1476         char *scope_buf = safe_malloc(max_scf_name_length + 1);
1477 
1478         assert(wip->scope != NULL);
1479 
1480         if (scf_scope_get_name(wip->scope, scope_buf, max_scf_name_length) < 0)
1481                 scfdie();
1482 
1483         sprint_str(buf, scope_buf, COMPONENT_COLUMN_WIDTH);
1484         free(scope_buf);
1485 }
1486 
1487 static void
1488 sortkey_scope(char *buf, int reverse, scf_walkinfo_t *wip)
1489 {
1490         char *tmp = NULL;
1491 
1492         sprint_scope(&tmp, wip);
1493         bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
1494         free(tmp);
1495         if (reverse)
1496                 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
1497 }
1498 
1499 static void
1500 sprint_service(char **buf, scf_walkinfo_t *wip)
1501 {
1502         char *svc_buf = safe_malloc(max_scf_name_length + 1);
1503 
1504         if (wip->pg == NULL) {
1505                 if (scf_service_get_name(wip->svc, svc_buf,
1506                     max_scf_name_length + 1) < 0)
1507                         scfdie();
1508         } else {
1509                 if (pg_get_single_val(wip->pg, "name", SCF_TYPE_ASTRING,
1510                     svc_buf, max_scf_name_length + 1, EMPTY_OK) != 0)
1511                         (void) strcpy(svc_buf, LEGACY_UNKNOWN);
1512         }
1513 
1514         sprint_str(buf, svc_buf, COMPONENT_COLUMN_WIDTH);
1515         free(svc_buf);
1516 }
1517 
1518 static void
1519 sortkey_service(char *buf, int reverse, scf_walkinfo_t *wip)
1520 {
1521         char *tmp = NULL;
1522 
1523         sprint_service(&tmp, wip);
1524         bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
1525         free(tmp);
1526         if (reverse)
1527                 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
1528 }
1529 
1530 /* INST */
1531 static void
1532 sprint_instance(char **buf, scf_walkinfo_t *wip)
1533 {
1534         char *inst_buf = safe_malloc(max_scf_name_length + 1);
1535 
1536         if (wip->pg == NULL) {
1537                 if (scf_instance_get_name(wip->inst, inst_buf,
1538                     max_scf_name_length + 1) < 0)
1539                         scfdie();
1540         } else {
1541                 inst_buf[0] = '-';
1542                 inst_buf[1] = '\0';
1543         }
1544 
1545 
1546         sprint_str(buf, inst_buf, COMPONENT_COLUMN_WIDTH);
1547         free(inst_buf);
1548 }
1549 
1550 static void
1551 sortkey_instance(char *buf, int reverse, scf_walkinfo_t *wip)
1552 {
1553         char *tmp = NULL;
1554 
1555         sprint_instance(&tmp, wip);
1556         bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
1557         free(tmp);
1558         if (reverse)
1559                 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
1560 }
1561 
1562 /* STIME */
1563 #define STIME_COLUMN_WIDTH              8
1564 #define FORMAT_TIME                     "%k:%M:%S"
1565 #define FORMAT_DATE                     "%b_%d"
1566 #define FORMAT_YEAR                     "%Y"
1567 
1568 /*
1569  * sprint_stime() will allocate a new buffer and snprintf the services's
1570  * state timestamp.  If the timestamp is unavailable for some reason
1571  * a '-' is given instead.
1572  */
1573 static void
1574 sprint_stime(char **buf, scf_walkinfo_t *wip)
1575 {
1576         int r;
1577         struct timeval tv;
1578         time_t then;
1579         struct tm *tm;
1580         char st_buf[STIME_COLUMN_WIDTH + 1];
1581 
1582         if (wip->pg == NULL) {
1583                 r = get_restarter_time_prop(wip->inst,
1584                     SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
1585         } else {
1586                 r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP,
1587                     SCF_TYPE_TIME, &tv, NULL, 0);
1588         }
1589 
1590         if (r != 0) {
1591                 /*
1592                  * There's something amiss with our service
1593                  * so we'll print a '-' for STIME.
1594                  */
1595                 sprint_str(buf, "-", STIME_COLUMN_WIDTH);
1596         } else {
1597                 /* tv should be valid so we'll format it */
1598                 then = (time_t)tv.tv_sec;
1599 
1600                 tm = localtime(&then);
1601                 /*
1602                  * Print time if started within the past 24 hours, print date
1603                  * if within the past 12 months or, finally, print year if
1604                  * started greater than 12 months ago.
1605                  */
1606                 if (now - then < 24 * 60 * 60) {
1607                         (void) strftime(st_buf, sizeof (st_buf),
1608                             gettext(FORMAT_TIME), tm);
1609                 } else if (now - then < 12 * 30 * 24 * 60 * 60) {
1610                         (void) strftime(st_buf, sizeof (st_buf),
1611                             gettext(FORMAT_DATE), tm);
1612                 } else {
1613                         (void) strftime(st_buf, sizeof (st_buf),
1614                             gettext(FORMAT_YEAR), tm);
1615                 }
1616                 sprint_str(buf, st_buf, STIME_COLUMN_WIDTH);
1617         }
1618 }
1619 
1620 #define STIME_SORTKEY_WIDTH             (sizeof (uint64_t) + sizeof (uint32_t))
1621 
1622 /* ARGSUSED */
1623 static void
1624 sortkey_stime(char *buf, int reverse, scf_walkinfo_t *wip)
1625 {
1626         struct timeval tv;
1627         int r;
1628 
1629         if (wip->pg == NULL)
1630                 r = get_restarter_time_prop(wip->inst,
1631                     SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
1632         else
1633                 r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP,
1634                     SCF_TYPE_TIME, &tv, NULL, 0);
1635 
1636         if (r == 0) {
1637                 int64_t sec;
1638                 int32_t us;
1639 
1640                 /* Stick it straight into the buffer. */
1641                 sec = tv.tv_sec;
1642                 us = tv.tv_usec;
1643 
1644                 sec = BE_64(sec);
1645                 us = BE_32(us);
1646                 bcopy(&sec, buf, sizeof (sec));
1647                 bcopy(&us, buf + sizeof (sec), sizeof (us));
1648         } else {
1649                 bzero(buf, STIME_SORTKEY_WIDTH);
1650         }
1651 
1652         if (reverse)
1653                 reverse_bytes(buf, STIME_SORTKEY_WIDTH);
1654 }
1655 
1656 /* ZONE */
1657 #define ZONE_COLUMN_WIDTH       16
1658 /*ARGSUSED*/
1659 static void
1660 sprint_zone(char **buf, scf_walkinfo_t *wip)
1661 {
1662         char *zonename = g_zonename, b[ZONENAME_MAX];
1663 
1664         if (zonename == NULL) {
1665                 zoneid_t zoneid = getzoneid();
1666 
1667                 if (getzonenamebyid(zoneid, b, sizeof (b)) < 0)
1668                         uu_die(gettext("could not determine zone name"));
1669 
1670                 zonename = b;
1671         }
1672 
1673         sprint_str(buf, zonename, ZONE_COLUMN_WIDTH);
1674 }
1675 
1676 static void
1677 sortkey_zone(char *buf, int reverse, scf_walkinfo_t *wip)
1678 {
1679         char *tmp = NULL;
1680 
1681         sprint_zone(&tmp, wip);
1682         bcopy(tmp, buf, ZONE_COLUMN_WIDTH);
1683         free(tmp);
1684         if (reverse)
1685                 reverse_bytes(buf, ZONE_COLUMN_WIDTH);
1686 }
1687 
1688 /*
1689  * Information about columns which can be displayed.  If you add something,
1690  * check MAX_COLUMN_NAME_LENGTH_STR & update description_of_column() below.
1691  */
1692 static const struct column columns[] = {
1693         { "CTID", CTID_COLUMN_WIDTH, sprint_ctid,
1694                 CTID_SORTKEY_WIDTH, sortkey_ctid },
1695         { "DESC", DESC_COLUMN_WIDTH, sprint_desc,
1696                 DESC_COLUMN_WIDTH, sortkey_desc },
1697         { "FMRI", FMRI_COLUMN_WIDTH, sprint_fmri,
1698                 FMRI_COLUMN_WIDTH, sortkey_fmri },
1699         { "INST", COMPONENT_COLUMN_WIDTH, sprint_instance,
1700                 COMPONENT_COLUMN_WIDTH, sortkey_instance },
1701         { "N", 1,  sprint_n, 1, sortkey_nstate },
1702         { "NSTA", 4, sprint_nsta, 1, sortkey_nstate },
1703         { "NSTATE", MAX_SCF_STATE_STRING_SZ, sprint_nstate,
1704                 1, sortkey_nstate },
1705         { "S", 2, sprint_s, 1, sortkey_state },
1706         { "SCOPE", COMPONENT_COLUMN_WIDTH, sprint_scope,
1707                 COMPONENT_COLUMN_WIDTH, sortkey_scope },
1708         { "SN", 2, sprint_sn, 2, sortkey_sn },
1709         { "SVC", COMPONENT_COLUMN_WIDTH, sprint_service,
1710                 COMPONENT_COLUMN_WIDTH, sortkey_service },
1711         { "STA", 4, sprint_sta, 1, sortkey_state },
1712         { "STATE", MAX_SCF_STATE_STRING_SZ, sprint_state,
1713                 1, sortkey_state },
1714         { "STIME", STIME_COLUMN_WIDTH, sprint_stime,
1715                 STIME_SORTKEY_WIDTH, sortkey_stime },
1716         { "ZONE", ZONE_COLUMN_WIDTH, sprint_zone,
1717                 ZONE_COLUMN_WIDTH, sortkey_zone },
1718 };
1719 
1720 #define MAX_COLUMN_NAME_LENGTH_STR      "6"
1721 
1722 static const int ncolumns = sizeof (columns) / sizeof (columns[0]);
1723 
1724 /*
1725  * Necessary thanks to gettext() & xgettext.
1726  */
1727 static const char *
1728 description_of_column(int c)
1729 {
1730         const char *s = NULL;
1731 
1732         switch (c) {
1733         case 0:
1734                 s = gettext("contract ID for service (see contract(4))");
1735                 break;
1736         case 1:
1737                 s = gettext("human-readable description of the service");
1738                 break;
1739         case 2:
1740                 s = gettext("Fault Managed Resource Identifier for service");
1741                 break;
1742         case 3:
1743                 s = gettext("portion of the FMRI indicating service instance");
1744                 break;
1745         case 4:
1746                 s = gettext("abbreviation for next state (if in transition)");
1747                 break;
1748         case 5:
1749                 s = gettext("abbreviation for next state (if in transition)");
1750                 break;
1751         case 6:
1752                 s = gettext("name for next state (if in transition)");
1753                 break;
1754         case 7:
1755                 s = gettext("abbreviation for current state");
1756                 break;
1757         case 8:
1758                 s = gettext("name for scope associated with service");
1759                 break;
1760         case 9:
1761                 s = gettext("abbreviation for current state and next state");
1762                 break;
1763         case 10:
1764                 s = gettext("portion of the FMRI representing service name");
1765                 break;
1766         case 11:
1767                 s = gettext("abbreviation for current state");
1768                 break;
1769         case 12:
1770                 s = gettext("name for current state");
1771                 break;
1772         case 13:
1773                 s = gettext("time of last state change");
1774                 break;
1775         case 14:
1776                 s = gettext("name of zone");
1777                 break;
1778         }
1779 
1780         assert(s != NULL);
1781         return (s);
1782 }
1783 
1784 
1785 static void
1786 print_usage(const char *progname, FILE *f, boolean_t do_exit)
1787 {
1788         (void) fprintf(f, gettext("usage:\n"
1789             "%1$s\t[-aHpv?] [-o col[,col]...] [-R FMRI-instance]... "
1790             "[-sS col]...\n\t[-z zone|-Z] [FMRI|pattern]...\n"
1791             "%1$s\t{-d|-D} -Hpv? [-o col[,col]...] [-sS col]... [-z zone|-Z]\n"
1792             "\t[FMRI|pattern]...\n"
1793             "%1$s\t{-l|-L} [-v] [-z zone|-Z] {FMRI|pattern}...\n"
1794             "%1$s\t-x [-v] [-z zone|-Z] [FMRI]...\n"), progname);
1795 
1796         if (do_exit)
1797                 exit(UU_EXIT_USAGE);
1798 }
1799 
1800 #define argserr(progname)       print_usage(progname, stderr, B_TRUE)
1801 
1802 static void
1803 print_help(const char *progname)
1804 {
1805         int i;
1806 
1807         print_usage(progname, stdout, B_FALSE);
1808 
1809         (void) printf(gettext("\n"
1810         "\t-a  list all service instances rather than "
1811         "only those that are enabled\n"
1812         "\t-d  list dependencies of the specified service(s)\n"
1813         "\t-D  list dependents of the specified service(s)\n"
1814         "\t-H  omit header line from output\n"
1815         "\t-l  list detailed information about the specified service(s)\n"
1816         "\t-L  list the log file associated with the specified service(s)\n"
1817         "\t-o  list only the specified columns in the output\n"
1818         "\t-p  list process IDs and names associated with each service\n"
1819         "\t-R  list only those services with the specified restarter\n"
1820         "\t-s  sort output in ascending order by the specified column(s)\n"
1821         "\t-S  sort output in descending order by the specified column(s)\n"
1822         "\t-v  list verbose information appropriate to the type of output\n"
1823         "\t-x  explain the status of services that might require maintenance,\n"
1824         "\t    or explain the status of the specified service(s)\n"
1825         "\t-z  from global zone, show services in a specified zone\n"
1826         "\t-Z  from global zone, show services in all zones\n"
1827         "\n\t"
1828         "Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n"
1829         "\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n"
1830         "\n"
1831         "\t%1$s [opts] svc:/network/smtp:sendmail\n"
1832         "\t%1$s [opts] network/smtp:sendmail\n"
1833         "\t%1$s [opts] network/*mail\n"
1834         "\t%1$s [opts] network/smtp\n"
1835         "\t%1$s [opts] smtp:sendmail\n"
1836         "\t%1$s [opts] smtp\n"
1837         "\t%1$s [opts] sendmail\n"
1838         "\n\t"
1839         "Columns for output or sorting can be specified using these names:\n"
1840         "\n"), progname);
1841 
1842         for (i = 0; i < ncolumns; i++) {
1843                 (void) printf("\t%-" MAX_COLUMN_NAME_LENGTH_STR "s  %s\n",
1844                     columns[i].name, description_of_column(i));
1845         }
1846 }
1847 
1848 
1849 /*
1850  * A getsubopt()-like function which returns an index into the columns table.
1851  * On success, *optionp is set to point to the next sub-option, or the
1852  * terminating null if there are none.
1853  */
1854 static int
1855 getcolumnopt(char **optionp)
1856 {
1857         char *str = *optionp, *cp;
1858         int i;
1859 
1860         assert(optionp != NULL);
1861         assert(*optionp != NULL);
1862 
1863         cp = strchr(*optionp, ',');
1864         if (cp != NULL)
1865                 *cp = '\0';
1866 
1867         for (i = 0; i < ncolumns; ++i) {
1868                 if (strcasecmp(str, columns[i].name) == 0) {
1869                         if (cp != NULL)
1870                                 *optionp = cp + 1;
1871                         else
1872                                 *optionp = strchr(*optionp, '\0');
1873 
1874                         return (i);
1875                 }
1876         }
1877 
1878         return (-1);
1879 }
1880 
1881 static void
1882 print_header()
1883 {
1884         int i;
1885         char *line_buf, *cp;
1886 
1887         line_buf = safe_malloc(line_sz);
1888         cp = line_buf;
1889         for (i = 0; i < opt_cnum; ++i) {
1890                 const struct column * const colp = &columns[opt_columns[i]];
1891 
1892                 (void) snprintf(cp, colp->width + 1, "%-*s", colp->width,
1893                     colp->name);
1894                 cp += colp->width;
1895                 *cp++ = ' ';
1896         }
1897 
1898         /* Trim the trailing whitespace */
1899         --cp;
1900         while (*cp == ' ')
1901                 --cp;
1902         *(cp+1) = '\0';
1903         (void) puts(line_buf);
1904 
1905         free(line_buf);
1906 }
1907 
1908 
1909 
1910 /*
1911  * Long listing (-l) functions.
1912  */
1913 
1914 static int
1915 pidcmp(const void *l, const void *r)
1916 {
1917         pid_t lp = *(pid_t *)l, rp = *(pid_t *)r;
1918 
1919         if (lp < rp)
1920                 return (-1);
1921         if (lp > rp)
1922                 return (1);
1923         return (0);
1924 }
1925 
1926 /*
1927  * This is the strlen() of the longest label ("description"), plus intercolumn
1928  * space.
1929  */
1930 #define DETAILED_WIDTH  (11 + 2)
1931 
1932 /*
1933  * Callback routine to print header for contract id.
1934  * Called by ctids_by_restarter and print_detailed.
1935  */
1936 static void
1937 print_ctid_header()
1938 {
1939         (void) printf("%-*s", DETAILED_WIDTH, "contract_id");
1940 }
1941 
1942 /*
1943  * Callback routine to print a contract id.
1944  * Called by ctids_by_restarter and print_detailed.
1945  */
1946 static void
1947 print_ctid_detailed(uint64_t c)
1948 {
1949         (void) printf("%lu ", (ctid_t)c);
1950 }
1951 
1952 static void
1953 detailed_list_processes(scf_walkinfo_t *wip)
1954 {
1955         uint64_t c;
1956         pid_t *pids;
1957         uint_t i, n;
1958         psinfo_t psi;
1959 
1960         if (get_restarter_count_prop(wip->inst, scf_property_contract, &c,
1961             EMPTY_OK) != 0)
1962                 return;
1963 
1964         if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0)
1965                 return;
1966 
1967         qsort(pids, n, sizeof (*pids), pidcmp);
1968 
1969         for (i = 0; i < n; ++i) {
1970                 (void) printf("%-*s%lu", DETAILED_WIDTH, gettext("process"),
1971                     pids[i]);
1972 
1973                 if (get_psinfo(pids[i], &psi) == 0)
1974                         (void) printf(" %.*s", PRARGSZ, psi.pr_psargs);
1975 
1976                 (void) putchar('\n');
1977         }
1978 
1979         free(pids);
1980 }
1981 
1982 /*
1983  * Determines the state of a dependency.  If the FMRI specifies a file, then we
1984  * fake up a state based on whether we can access the file.
1985  */
1986 static void
1987 get_fmri_state(char *fmri, char *state, size_t state_sz)
1988 {
1989         char *lfmri;
1990         const char *svc_name, *inst_name, *pg_name, *path;
1991         scf_service_t *svc;
1992         scf_instance_t *inst;
1993         scf_iter_t *iter;
1994 
1995         lfmri = safe_strdup(fmri);
1996 
1997         /*
1998          * Check for file:// dependencies
1999          */
2000         if (scf_parse_file_fmri(lfmri, NULL, &path) == SCF_SUCCESS) {
2001                 struct stat64 statbuf;
2002                 const char *msg;
2003 
2004                 if (stat64(path, &statbuf) == 0)
2005                         msg = "online";
2006                 else if (errno == ENOENT)
2007                         msg = "absent";
2008                 else
2009                         msg = "unknown";
2010 
2011                 (void) strlcpy(state, msg, state_sz);
2012                 return;
2013         }
2014 
2015         /*
2016          * scf_parse_file_fmri() may have overwritten part of the string, so
2017          * copy it back.
2018          */
2019         (void) strcpy(lfmri, fmri);
2020 
2021         if (scf_parse_svc_fmri(lfmri, NULL, &svc_name, &inst_name,
2022             &pg_name, NULL) != SCF_SUCCESS) {
2023                 free(lfmri);
2024                 (void) strlcpy(state, "invalid", state_sz);
2025                 return;
2026         }
2027 
2028         free(lfmri);
2029 
2030         if (svc_name == NULL || pg_name != NULL) {
2031                 (void) strlcpy(state, "invalid", state_sz);
2032                 return;
2033         }
2034 
2035         if (inst_name != NULL) {
2036                 /* instance: get state */
2037                 inst = scf_instance_create(h);
2038                 if (inst == NULL)
2039                         scfdie();
2040 
2041                 if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
2042                     NULL, SCF_DECODE_FMRI_EXACT) == SCF_SUCCESS)
2043                         get_restarter_string_prop(inst, scf_property_state,
2044                             state, state_sz);
2045                 else {
2046                         switch (scf_error()) {
2047                         case SCF_ERROR_INVALID_ARGUMENT:
2048                                 (void) strlcpy(state, "invalid", state_sz);
2049                                 break;
2050                         case SCF_ERROR_NOT_FOUND:
2051                                 (void) strlcpy(state, "absent", state_sz);
2052                                 break;
2053 
2054                         default:
2055                                 scfdie();
2056                         }
2057                 }
2058 
2059                 scf_instance_destroy(inst);
2060                 return;
2061         }
2062 
2063         /*
2064          * service: If only one instance, use that state.  Otherwise, say
2065          * "multiple".
2066          */
2067         if ((svc = scf_service_create(h)) == NULL ||
2068             (inst = scf_instance_create(h)) == NULL ||
2069             (iter = scf_iter_create(h)) == NULL)
2070                 scfdie();
2071 
2072         if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL,
2073             SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
2074                 switch (scf_error()) {
2075                 case SCF_ERROR_INVALID_ARGUMENT:
2076                         (void) strlcpy(state, "invalid", state_sz);
2077                         goto out;
2078                 case SCF_ERROR_NOT_FOUND:
2079                         (void) strlcpy(state, "absent", state_sz);
2080                         goto out;
2081 
2082                 default:
2083                         scfdie();
2084                 }
2085         }
2086 
2087         if (scf_iter_service_instances(iter, svc) != SCF_SUCCESS)
2088                 scfdie();
2089 
2090         switch (scf_iter_next_instance(iter, inst)) {
2091         case 0:
2092                 (void) strlcpy(state, "absent", state_sz);
2093                 goto out;
2094 
2095         case 1:
2096                 break;
2097 
2098         default:
2099                 scfdie();
2100         }
2101 
2102         /* Get the state in case this is the only instance. */
2103         get_restarter_string_prop(inst, scf_property_state, state, state_sz);
2104 
2105         switch (scf_iter_next_instance(iter, inst)) {
2106         case 0:
2107                 break;
2108 
2109         case 1:
2110                 /* Nope, multiple instances. */
2111                 (void) strlcpy(state, "multiple", state_sz);
2112                 goto out;
2113 
2114         default:
2115                 scfdie();
2116         }
2117 
2118 out:
2119         scf_iter_destroy(iter);
2120         scf_instance_destroy(inst);
2121         scf_service_destroy(svc);
2122 }
2123 
2124 static void
2125 print_application_properties(scf_walkinfo_t *wip, scf_snapshot_t *snap)
2126 {
2127         scf_iter_t *pg_iter, *prop_iter, *val_iter;
2128         scf_propertygroup_t *pg;
2129         scf_property_t *prop;
2130         scf_value_t *val;
2131         scf_pg_tmpl_t *pt;
2132         scf_prop_tmpl_t *prt;
2133         char *pg_name_buf = safe_malloc(max_scf_name_length + 1);
2134         char *prop_name_buf = safe_malloc(max_scf_name_length + 1);
2135         char *snap_name = safe_malloc(max_scf_name_length + 1);
2136         char *val_buf = safe_malloc(max_scf_value_length + 1);
2137         char *desc, *cp;
2138         scf_type_t type;
2139         int i, j, k;
2140         uint8_t vis;
2141 
2142         if ((pg_iter = scf_iter_create(h)) == NULL ||
2143             (prop_iter = scf_iter_create(h)) == NULL ||
2144             (val_iter = scf_iter_create(h)) == NULL ||
2145             (val = scf_value_create(h)) == NULL ||
2146             (prop = scf_property_create(h)) == NULL ||
2147             (pt = scf_tmpl_pg_create(h)) == NULL ||
2148             (prt = scf_tmpl_prop_create(h)) == NULL ||
2149             (pg = scf_pg_create(h)) == NULL)
2150                 scfdie();
2151 
2152         if (scf_iter_instance_pgs_typed_composed(pg_iter, wip->inst, snap,
2153             SCF_PG_APP_DEFAULT) == -1)
2154                 scfdie();
2155 
2156         /*
2157          * Format for output:
2158          *      pg (pgtype)
2159          *       description
2160          *      pg/prop (proptype) = <value> <value>
2161          *       description
2162          */
2163         while ((i = scf_iter_next_pg(pg_iter, pg)) == 1) {
2164                 int tmpl = 0;
2165 
2166                 if (scf_pg_get_name(pg, pg_name_buf, max_scf_name_length) < 0)
2167                         scfdie();
2168                 if (scf_snapshot_get_name(snap, snap_name,
2169                     max_scf_name_length) < 0)
2170                         scfdie();
2171 
2172                 if (scf_tmpl_get_by_pg_name(wip->fmri, snap_name, pg_name_buf,
2173                     SCF_PG_APP_DEFAULT, pt, 0) == 0)
2174                         tmpl = 1;
2175                 else
2176                         tmpl = 0;
2177 
2178                 (void) printf("%s (%s)\n", pg_name_buf, SCF_PG_APP_DEFAULT);
2179 
2180                 if (tmpl == 1 && scf_tmpl_pg_description(pt, NULL, &desc) > 0) {
2181                         (void) printf("  %s\n", desc);
2182                         free(desc);
2183                 }
2184 
2185                 if (scf_iter_pg_properties(prop_iter, pg) == -1)
2186                         scfdie();
2187                 while ((j = scf_iter_next_property(prop_iter, prop)) == 1) {
2188                         if (scf_property_get_name(prop, prop_name_buf,
2189                             max_scf_name_length) < 0)
2190                                 scfdie();
2191                         if (scf_property_type(prop, &type) == -1)
2192                                 scfdie();
2193 
2194                         if ((tmpl == 1) &&
2195                             (scf_tmpl_get_by_prop(pt, prop_name_buf, prt,
2196                             0) != 0))
2197                                 tmpl = 0;
2198 
2199                         if (tmpl == 1 &&
2200                             scf_tmpl_prop_visibility(prt, &vis) != -1 &&
2201                             vis == SCF_TMPL_VISIBILITY_HIDDEN)
2202                                 continue;
2203 
2204                         (void) printf("%s/%s (%s) = ", pg_name_buf,
2205                             prop_name_buf, scf_type_to_string(type));
2206 
2207                         if (scf_iter_property_values(val_iter, prop) == -1)
2208                                 scfdie();
2209 
2210                         while ((k = scf_iter_next_value(val_iter, val)) == 1) {
2211                                 if (scf_value_get_as_string(val, val_buf,
2212                                     max_scf_value_length + 1) < 0)
2213                                         scfdie();
2214                                 if (strpbrk(val_buf, " \t\n\"()") != NULL) {
2215                                         (void) printf("\"");
2216                                         for (cp = val_buf; *cp != '\0'; ++cp) {
2217                                                 if (*cp == '"' || *cp == '\\')
2218                                                         (void) putc('\\',
2219                                                             stdout);
2220 
2221                                                 (void) putc(*cp, stdout);
2222                                         }
2223                                         (void) printf("\"");
2224                                 } else {
2225                                         (void) printf("%s ", val_buf);
2226                                 }
2227                         }
2228 
2229                         (void) printf("\n");
2230 
2231                         if (k == -1)
2232                                 scfdie();
2233 
2234                         if (tmpl == 1 && scf_tmpl_prop_description(prt, NULL,
2235                             &desc) > 0) {
2236                                 (void) printf("  %s\n", desc);
2237                                 free(desc);
2238                         }
2239                 }
2240                 if (j == -1)
2241                         scfdie();
2242         }
2243         if (i == -1)
2244                 scfdie();
2245 
2246 
2247         scf_iter_destroy(pg_iter);
2248         scf_iter_destroy(prop_iter);
2249         scf_iter_destroy(val_iter);
2250         scf_value_destroy(val);
2251         scf_property_destroy(prop);
2252         scf_tmpl_pg_destroy(pt);
2253         scf_tmpl_prop_destroy(prt);
2254         scf_pg_destroy(pg);
2255         free(pg_name_buf);
2256         free(prop_name_buf);
2257         free(snap_name);
2258         free(val_buf);
2259 }
2260 
2261 static void
2262 print_detailed_dependency(scf_propertygroup_t *pg)
2263 {
2264         scf_property_t *eprop;
2265         scf_iter_t *iter;
2266         scf_type_t ty;
2267         char *val_buf;
2268         int i;
2269 
2270         if ((eprop = scf_property_create(h)) == NULL ||
2271             (iter = scf_iter_create(h)) == NULL)
2272                 scfdie();
2273 
2274         val_buf = safe_malloc(max_scf_value_length + 1);
2275 
2276         if (scf_pg_get_property(pg, SCF_PROPERTY_ENTITIES, eprop) !=
2277             SCF_SUCCESS ||
2278             scf_property_type(eprop, &ty) != SCF_SUCCESS ||
2279             ty != SCF_TYPE_FMRI)
2280                 return;
2281 
2282         (void) printf("%-*s", DETAILED_WIDTH, gettext("dependency"));
2283 
2284         /* Print the grouping */
2285         if (pg_get_single_val(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
2286             val_buf, max_scf_value_length + 1, 0) == 0)
2287                 (void) fputs(val_buf, stdout);
2288         else
2289                 (void) putchar('?');
2290 
2291         (void) putchar('/');
2292 
2293         if (pg_get_single_val(pg, SCF_PROPERTY_RESTART_ON, SCF_TYPE_ASTRING,
2294             val_buf, max_scf_value_length + 1, 0) == 0)
2295                 (void) fputs(val_buf, stdout);
2296         else
2297                 (void) putchar('?');
2298 
2299         /* Print the dependency entities. */
2300         if (scf_iter_property_values(iter, eprop) == -1)
2301                 scfdie();
2302 
2303         while ((i = scf_iter_next_value(iter, g_val)) == 1) {
2304                 char state[MAX_SCF_STATE_STRING_SZ];
2305 
2306                 if (scf_value_get_astring(g_val, val_buf,
2307                     max_scf_value_length + 1) < 0)
2308                         scfdie();
2309 
2310                 (void) putchar(' ');
2311                 (void) fputs(val_buf, stdout);
2312 
2313                 /* Print the state. */
2314                 state[0] = '-';
2315                 state[1] = '\0';
2316 
2317                 get_fmri_state(val_buf, state, sizeof (state));
2318 
2319                 (void) printf(" (%s)", state);
2320         }
2321         if (i == -1)
2322                 scfdie();
2323 
2324         (void) putchar('\n');
2325 
2326         free(val_buf);
2327         scf_iter_destroy(iter);
2328         scf_property_destroy(eprop);
2329 }
2330 
2331 /* ARGSUSED */
2332 static int
2333 print_detailed(void *unused, scf_walkinfo_t *wip)
2334 {
2335         scf_snapshot_t *snap;
2336         scf_propertygroup_t *rpg;
2337         scf_iter_t *pg_iter;
2338 
2339         char *buf;
2340         char *timebuf;
2341         size_t tbsz;
2342         int ret;
2343         uint64_t c;
2344         int temp, perm;
2345         struct timeval tv;
2346         time_t stime;
2347         struct tm *tmp;
2348         int restarter_spec;
2349         int restarter_ret;
2350 
2351         const char * const fmt = "%-*s%s\n";
2352 
2353         assert(wip->pg == NULL);
2354 
2355         rpg = scf_pg_create(h);
2356         if (rpg == NULL)
2357                 scfdie();
2358 
2359         if (first_paragraph)
2360                 first_paragraph = 0;
2361         else
2362                 (void) putchar('\n');
2363 
2364         buf = safe_malloc(max_scf_fmri_length + 1);
2365 
2366         if (scf_instance_to_fmri(wip->inst, buf, max_scf_fmri_length + 1) != -1)
2367                 (void) printf(fmt, DETAILED_WIDTH, "fmri", buf);
2368 
2369         if (common_name_buf == NULL)
2370                 common_name_buf = safe_malloc(max_scf_value_length + 1);
2371 
2372         if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale,
2373             SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1)
2374             == 0)
2375                 (void) printf(fmt, DETAILED_WIDTH, gettext("name"),
2376                     common_name_buf);
2377         else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C",
2378             SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0, 1, 1)
2379             == 0)
2380                 (void) printf(fmt, DETAILED_WIDTH, gettext("name"),
2381                     common_name_buf);
2382 
2383         if (g_zonename != NULL)
2384                 (void) printf(fmt, DETAILED_WIDTH, gettext("zone"), g_zonename);
2385 
2386         /*
2387          * Synthesize an 'enabled' property that hides the enabled_ovr
2388          * implementation from the user.  If the service has been temporarily
2389          * set to a state other than its permanent value, alert the user with
2390          * a '(temporary)' message.
2391          */
2392         perm = instance_enabled(wip->inst, B_FALSE);
2393         temp = instance_enabled(wip->inst, B_TRUE);
2394         if (temp != -1) {
2395                 if (temp != perm)
2396                         (void) printf(gettext("%-*s%s (temporary)\n"),
2397                             DETAILED_WIDTH, gettext("enabled"),
2398                             temp ? gettext("true") : gettext("false"));
2399                 else
2400                         (void) printf(fmt, DETAILED_WIDTH,
2401                             gettext("enabled"), temp ? gettext("true") :
2402                             gettext("false"));
2403         } else if (perm != -1) {
2404                 (void) printf(fmt, DETAILED_WIDTH, gettext("enabled"),
2405                     perm ? gettext("true") : gettext("false"));
2406         }
2407 
2408         /*
2409          * Property values may be longer than max_scf_fmri_length, but these
2410          * shouldn't be, so we'll just reuse buf.  The user can use svcprop if
2411          * they suspect something fishy.
2412          */
2413         if (scf_instance_get_pg(wip->inst, SCF_PG_RESTARTER, rpg) != 0) {
2414                 if (scf_error() != SCF_ERROR_NOT_FOUND)
2415                         scfdie();
2416 
2417                 scf_pg_destroy(rpg);
2418                 rpg = NULL;
2419         }
2420 
2421         if (rpg) {
2422                 if (pg_get_single_val(rpg, scf_property_state, SCF_TYPE_ASTRING,
2423                     buf, max_scf_fmri_length + 1, 0) == 0)
2424                         (void) printf(fmt, DETAILED_WIDTH, gettext("state"),
2425                             buf);
2426 
2427                 if (pg_get_single_val(rpg, scf_property_next_state,
2428                     SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0)
2429                         (void) printf(fmt, DETAILED_WIDTH,
2430                             gettext("next_state"), buf);
2431 
2432                 if (pg_get_single_val(rpg, SCF_PROPERTY_STATE_TIMESTAMP,
2433                     SCF_TYPE_TIME, &tv, NULL, 0) == 0) {
2434                         stime = tv.tv_sec;
2435                         tmp = localtime(&stime);
2436                         for (tbsz = 50; ; tbsz *= 2) {
2437                                 timebuf = safe_malloc(tbsz);
2438                                 if (strftime(timebuf, tbsz, NULL, tmp) != 0)
2439                                         break;
2440                                 free(timebuf);
2441                         }
2442                         (void) printf(fmt, DETAILED_WIDTH,
2443                             gettext("state_time"),
2444                             timebuf);
2445                         free(timebuf);
2446                 }
2447 
2448                 if (pg_get_single_val(rpg, SCF_PROPERTY_ALT_LOGFILE,
2449                     SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0)
2450                         (void) printf(fmt, DETAILED_WIDTH,
2451                             gettext("alt_logfile"), buf);
2452 
2453                 if (pg_get_single_val(rpg, SCF_PROPERTY_LOGFILE,
2454                     SCF_TYPE_ASTRING, buf, max_scf_fmri_length + 1, 0) == 0)
2455                         (void) printf(fmt, DETAILED_WIDTH, gettext("logfile"),
2456                             buf);
2457         }
2458 
2459         if (inst_get_single_val(wip->inst, SCF_PG_GENERAL,
2460             SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, buf,
2461             max_scf_fmri_length + 1, 0, 0, 1) == 0)
2462                 (void) printf(fmt, DETAILED_WIDTH, gettext("restarter"), buf);
2463         else
2464                 (void) printf(fmt, DETAILED_WIDTH, gettext("restarter"),
2465                     SCF_SERVICE_STARTD);
2466 
2467         free(buf);
2468 
2469         /*
2470          * Use the restarter specific routine to print the ctids, if available.
2471          * If restarter specific action is available and it fails, then die.
2472          */
2473         restarter_ret = ctids_by_restarter(wip, &c, 1, 0,
2474             &restarter_spec, print_ctid_header, print_ctid_detailed);
2475         if (restarter_spec == 1) {
2476                 if (restarter_ret != 0)
2477                         uu_die(gettext("Unable to get restarter for %s"),
2478                             wip->fmri);
2479                 goto restarter_common;
2480         }
2481 
2482         if (rpg) {
2483                 scf_iter_t *iter;
2484 
2485                 if ((iter = scf_iter_create(h)) == NULL)
2486                         scfdie();
2487 
2488                 if (scf_pg_get_property(rpg, scf_property_contract, g_prop) ==
2489                     0) {
2490                         if (scf_property_is_type(g_prop, SCF_TYPE_COUNT) == 0) {
2491 
2492                                 /* Callback to print ctid header */
2493                                 print_ctid_header();
2494 
2495                                 if (scf_iter_property_values(iter, g_prop) != 0)
2496                                         scfdie();
2497 
2498                                 for (;;) {
2499                                         ret = scf_iter_next_value(iter, g_val);
2500                                         if (ret == -1)
2501                                                 scfdie();
2502                                         if (ret == 0)
2503                                                 break;
2504 
2505                                         if (scf_value_get_count(g_val, &c) != 0)
2506                                                 scfdie();
2507 
2508                                         /* Callback to print contract id. */
2509                                         print_ctid_detailed(c);
2510                                 }
2511 
2512                                 (void) putchar('\n');
2513                         } else {
2514                                 if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
2515                                         scfdie();
2516                         }
2517                 } else {
2518                         if (scf_error() != SCF_ERROR_NOT_FOUND)
2519                                 scfdie();
2520                 }
2521 
2522                 scf_iter_destroy(iter);
2523         } else {
2524                 if (scf_error() != SCF_ERROR_NOT_FOUND)
2525                         scfdie();
2526         }
2527 
2528 restarter_common:
2529         scf_pg_destroy(rpg);
2530 
2531         /* Dependencies. */
2532         if ((pg_iter = scf_iter_create(h)) == NULL)
2533                 scfdie();
2534 
2535         snap = get_running_snapshot(wip->inst);
2536 
2537         if (scf_iter_instance_pgs_typed_composed(pg_iter, wip->inst, snap,
2538             SCF_GROUP_DEPENDENCY) != SCF_SUCCESS)
2539                 scfdie();
2540 
2541         while ((ret = scf_iter_next_pg(pg_iter, g_pg)) == 1)
2542                 print_detailed_dependency(g_pg);
2543         if (ret == -1)
2544                 scfdie();
2545 
2546         scf_iter_destroy(pg_iter);
2547 
2548         if (opt_processes)
2549                 detailed_list_processes(wip);
2550 
2551         /* "application" type property groups */
2552         if (opt_verbose == 1)
2553                 print_application_properties(wip, snap);
2554 
2555         scf_snapshot_destroy(snap);
2556 
2557         return (0);
2558 }
2559 
2560 /* ARGSUSED */
2561 static int
2562 print_log(void *unused, scf_walkinfo_t *wip)
2563 {
2564         scf_propertygroup_t *rpg;
2565         char buf[MAXPATHLEN];
2566 
2567         if ((rpg = scf_pg_create(h)) == NULL)
2568                 scfdie();
2569 
2570         if (scf_instance_get_pg(wip->inst, SCF_PG_RESTARTER, rpg) != 0) {
2571                 if (scf_error() != SCF_ERROR_NOT_FOUND)
2572                         scfdie();
2573 
2574                 goto out;
2575         }
2576 
2577         if (pg_get_single_val(rpg, SCF_PROPERTY_LOGFILE,
2578             SCF_TYPE_ASTRING, buf, sizeof (buf), 0) == 0) {
2579                 (void) printf("%s\n", buf);
2580         }
2581 
2582 out:
2583         scf_pg_destroy(rpg);
2584 
2585         return (0);
2586 }
2587 
2588 int
2589 qsort_str_compare(const void *p1, const void *p2)
2590 {
2591         return (strcmp((const char *)p1, (const char *)p2));
2592 }
2593 
2594 /*
2595  * get_notify_param_classes()
2596  * return the fma classes that don't have a tag in fma_tags[], otherwise NULL
2597  */
2598 static char **
2599 get_notify_param_classes()
2600 {
2601         scf_handle_t            *h = _scf_handle_create_and_bind(SCF_VERSION);
2602         scf_instance_t          *inst = scf_instance_create(h);
2603         scf_snapshot_t          *snap = scf_snapshot_create(h);
2604         scf_snaplevel_t         *slvl = scf_snaplevel_create(h);
2605         scf_propertygroup_t     *pg = scf_pg_create(h);
2606         scf_iter_t              *iter = scf_iter_create(h);
2607         int size = 4;
2608         int n = 0;
2609         size_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
2610         int err;
2611         char *pgname = safe_malloc(sz);
2612         char **buf = safe_malloc(size * sizeof (char *));
2613 
2614         if (h == NULL || inst == NULL || snap == NULL || slvl == NULL ||
2615             pg == NULL || iter == NULL) {
2616                 uu_die(gettext("Failed object creation: %s\n"),
2617                     scf_strerror(scf_error()));
2618         }
2619 
2620         if (scf_handle_decode_fmri(h, SCF_NOTIFY_PARAMS_INST, NULL, NULL, inst,
2621             NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0)
2622                 uu_die(gettext("Failed to decode %s: %s\n"),
2623                     SCF_NOTIFY_PARAMS_INST, scf_strerror(scf_error()));
2624 
2625         if (scf_instance_get_snapshot(inst, "running", snap) != 0)
2626                 uu_die(gettext("Failed to get snapshot: %s\n"),
2627                     scf_strerror(scf_error()));
2628 
2629         if (scf_snapshot_get_base_snaplevel(snap, slvl) != 0)
2630                 uu_die(gettext("Failed to get base snaplevel: %s\n"),
2631                     scf_strerror(scf_error()));
2632 
2633         if (scf_iter_snaplevel_pgs_typed(iter, slvl,
2634             SCF_NOTIFY_PARAMS_PG_TYPE) != 0)
2635                 uu_die(gettext("Failed to get iterator: %s\n"),
2636                     scf_strerror(scf_error()));
2637 
2638         while ((err = scf_iter_next_pg(iter, pg)) == 1) {
2639                 char *c;
2640 
2641                 if (scf_pg_get_name(pg, pgname, sz) == -1)
2642                         uu_die(gettext("Failed to get pg name: %s\n"),
2643                             scf_strerror(scf_error()));
2644                 if ((c = strrchr(pgname, ',')) != NULL)
2645                         *c = '\0';
2646                 if (has_fma_tag(pgname))
2647                         continue;
2648                 if (!is_fma_token(pgname))
2649                         /*
2650                          * We don't emmit a warning here so that we don't
2651                          * pollute the output
2652                          */
2653                         continue;
2654 
2655                 if (n + 1 >= size) {
2656                         size *= 2;
2657                         buf = realloc(buf, size * sizeof (char *));
2658                         if (buf == NULL)
2659                                 uu_die(gettext("Out of memory.\n"));
2660                 }
2661                 buf[n] = safe_strdup(pgname);
2662                 ++n;
2663         }
2664         /*
2665          * NULL terminate buf
2666          */
2667         buf[n] = NULL;
2668         if (err == -1)
2669                 uu_die(gettext("Failed to iterate pgs: %s\n"),
2670                     scf_strerror(scf_error()));
2671 
2672         /* sort the classes */
2673         qsort((void *)buf, n, sizeof (char *), qsort_str_compare);
2674 
2675         free(pgname);
2676         scf_iter_destroy(iter);
2677         scf_pg_destroy(pg);
2678         scf_snaplevel_destroy(slvl);
2679         scf_snapshot_destroy(snap);
2680         scf_instance_destroy(inst);
2681         scf_handle_destroy(h);
2682 
2683         return (buf);
2684 }
2685 
2686 /*
2687  * get_fma_notify_params()
2688  * populates an nvlist_t with notifycation parameters for a given FMA class
2689  * returns 0 if the nvlist is populated, 1 otherwise;
2690  */
2691 int
2692 get_fma_notify_params(nvlist_t *nvl, const char *class)
2693 {
2694         if (_scf_get_fma_notify_params(class, nvl, 0) != 0) {
2695                 /*
2696                  * if the preferences have just been deleted
2697                  * or does not exist, just skip.
2698                  */
2699                 if (scf_error() != SCF_ERROR_NOT_FOUND &&
2700                     scf_error() != SCF_ERROR_DELETED)
2701                         uu_warn(gettext(
2702                             "Failed get_fma_notify_params %s\n"),
2703                             scf_strerror(scf_error()));
2704 
2705                 return (1);
2706         }
2707 
2708         return (0);
2709 }
2710 
2711 /*
2712  * print_notify_fma()
2713  * outputs the notification paramets of FMA events.
2714  * It first outputs classes in fma_tags[], then outputs the other classes
2715  * sorted alphabetically
2716  */
2717 static void
2718 print_notify_fma(void)
2719 {
2720         nvlist_t *nvl;
2721         char **tmp = NULL;
2722         char **classes, *p;
2723         const char *class;
2724         uint32_t i;
2725 
2726         if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
2727                 uu_die(gettext("Out of memory.\n"));
2728 
2729         for (i = 0; (class = get_fma_class(i)) != NULL; ++i) {
2730                 if (get_fma_notify_params(nvl, class) == 0)
2731                         listnotify_print(nvl, get_fma_tag(i));
2732         }
2733 
2734         if ((classes = get_notify_param_classes()) == NULL)
2735                 goto cleanup;
2736 
2737         tmp = classes;
2738         for (p = *tmp; p; ++tmp, p = *tmp) {
2739                 if (get_fma_notify_params(nvl, p) == 0)
2740                         listnotify_print(nvl, re_tag(p));
2741 
2742                 free(p);
2743         }
2744 
2745         free(classes);
2746 
2747 cleanup:
2748         nvlist_free(nvl);
2749 }
2750 
2751 /*
2752  * print_notify_fmri()
2753  * prints notifycation parameters for an SMF instance.
2754  */
2755 static void
2756 print_notify_fmri(const char *fmri)
2757 {
2758         nvlist_t *nvl;
2759 
2760         if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
2761                 uu_die(gettext("Out of memory.\n"));
2762 
2763         if (_scf_get_svc_notify_params(fmri, nvl, SCF_TRANSITION_ALL, 0, 0) !=
2764             SCF_SUCCESS) {
2765                 if (scf_error() != SCF_ERROR_NOT_FOUND &&
2766                     scf_error() != SCF_ERROR_DELETED)
2767                         uu_warn(gettext(
2768                             "Failed _scf_get_svc_notify_params: %s\n"),
2769                             scf_strerror(scf_error()));
2770         } else {
2771                 if (strcmp(SCF_INSTANCE_GLOBAL, fmri) == 0)
2772                         safe_printf(
2773                             gettext("System wide notification parameters:\n"));
2774                 safe_printf("%s:\n", fmri);
2775                 listnotify_print(nvl, NULL);
2776         }
2777         nvlist_free(nvl);
2778 }
2779 
2780 /*
2781  * print_notify_special()
2782  * prints notification parameters for FMA events and system wide SMF state
2783  * transitions parameters
2784  */
2785 static void
2786 print_notify_special()
2787 {
2788         safe_printf("Notification parameters for FMA Events\n");
2789         print_notify_fma();
2790         print_notify_fmri(SCF_INSTANCE_GLOBAL);
2791 }
2792 
2793 /*
2794  * print_notify()
2795  * callback function to print notification parameters for SMF state transition
2796  * instances. It skips global and notify-params instances as they should be
2797  * printed by print_notify_special()
2798  */
2799 /* ARGSUSED */
2800 static int
2801 print_notify(void *unused, scf_walkinfo_t *wip)
2802 {
2803         if (strcmp(SCF_INSTANCE_GLOBAL, wip->fmri) == 0 ||
2804             strcmp(SCF_NOTIFY_PARAMS_INST, wip->fmri) == 0)
2805                 return (0);
2806 
2807         print_notify_fmri(wip->fmri);
2808 
2809         return (0);
2810 }
2811 
2812 /*
2813  * Append a one-lined description of each process in inst's contract(s) and
2814  * return the augmented string.
2815  */
2816 static char *
2817 add_processes(scf_walkinfo_t *wip, char *line, scf_propertygroup_t *lpg)
2818 {
2819         pid_t *pids = NULL;
2820         uint_t i, n = 0;
2821 
2822         if (lpg == NULL) {
2823                 if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0)
2824                         return (line);
2825         } else {
2826                 /* Legacy services */
2827                 scf_iter_t *iter;
2828 
2829                 if ((iter = scf_iter_create(h)) == NULL)
2830                         scfdie();
2831 
2832                 (void) propvals_to_pids(lpg, scf_property_contract, &pids, &n,
2833                     g_prop, g_val, iter);
2834 
2835                 scf_iter_destroy(iter);
2836         }
2837 
2838         if (n == 0)
2839                 return (line);
2840 
2841         qsort(pids, n, sizeof (*pids), pidcmp);
2842 
2843         for (i = 0; i < n; ++i) {
2844                 char *cp, stime[9];
2845                 psinfo_t psi;
2846                 struct tm *tm;
2847                 int len = 1 + 15 + 8 + 3 + 6 + 1 + PRFNSZ;
2848 
2849                 if (get_psinfo(pids[i], &psi) != 0)
2850                         continue;
2851 
2852                 line = realloc(line, strlen(line) + len);
2853                 if (line == NULL)
2854                         uu_die(gettext("Out of memory.\n"));
2855 
2856                 cp = strchr(line, '\0');
2857 
2858                 tm = localtime(&psi.pr_start.tv_sec);
2859 
2860                 /*
2861                  * Print time if started within the past 24 hours, print date
2862                  * if within the past 12 months, print year if started greater
2863                  * than 12 months ago.
2864                  */
2865                 if (now - psi.pr_start.tv_sec < 24 * 60 * 60)
2866                         (void) strftime(stime, sizeof (stime),
2867                             gettext(FORMAT_TIME), tm);
2868                 else if (now - psi.pr_start.tv_sec < 12 * 30 * 24 * 60 * 60)
2869                         (void) strftime(stime, sizeof (stime),
2870                             gettext(FORMAT_DATE), tm);
2871                 else
2872                         (void) strftime(stime, sizeof (stime),
2873                             gettext(FORMAT_YEAR), tm);
2874 
2875                 (void) snprintf(cp, len, "\n               %-8s   %6ld %.*s",
2876                     stime, pids[i], PRFNSZ, psi.pr_fname);
2877         }
2878 
2879         free(pids);
2880 
2881         return (line);
2882 }
2883 
2884 /*ARGSUSED*/
2885 static int
2886 list_instance(void *unused, scf_walkinfo_t *wip)
2887 {
2888         struct avl_string *lp;
2889         char *cp;
2890         int i;
2891         uu_avl_index_t idx;
2892 
2893         /*
2894          * If the user has specified a restarter, check for a match first
2895          */
2896         if (restarters != NULL) {
2897                 struct pfmri_list *rest;
2898                 int match;
2899                 char *restarter_fmri;
2900                 const char *scope_name, *svc_name, *inst_name, *pg_name;
2901 
2902                 /* legacy services don't have restarters */
2903                 if (wip->pg != NULL)
2904                         return (0);
2905 
2906                 restarter_fmri = safe_malloc(max_scf_fmri_length + 1);
2907 
2908                 if (inst_get_single_val(wip->inst, SCF_PG_GENERAL,
2909                     SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, restarter_fmri,
2910                     max_scf_fmri_length + 1, 0, 0, 1) != 0)
2911                         (void) strcpy(restarter_fmri, SCF_SERVICE_STARTD);
2912 
2913                 if (scf_parse_svc_fmri(restarter_fmri, &scope_name, &svc_name,
2914                     &inst_name, &pg_name, NULL) != SCF_SUCCESS) {
2915                         free(restarter_fmri);
2916                         return (0);
2917                 }
2918 
2919                 match = 0;
2920                 for (rest = restarters; rest != NULL; rest = rest->next) {
2921                         if (strcmp(rest->scope, scope_name) == 0 &&
2922                             strcmp(rest->service, svc_name) == 0 &&
2923                             strcmp(rest->instance, inst_name) == 0)
2924                                 match = 1;
2925                 }
2926 
2927                 free(restarter_fmri);
2928 
2929                 if (!match)
2930                         return (0);
2931         }
2932 
2933         if (wip->pg == NULL && ht_buckets != NULL && ht_add(wip->fmri)) {
2934                 /* It was already there. */
2935                 return (0);
2936         }
2937 
2938         lp = safe_malloc(sizeof (*lp));
2939 
2940         lp->str = NULL;
2941         for (i = 0; i < opt_cnum; ++i) {
2942                 columns[opt_columns[i]].sprint(&lp->str, wip);
2943         }
2944         cp = lp->str + strlen(lp->str);
2945         cp--;
2946         while (*cp == ' ')
2947                 cp--;
2948         *(cp+1) = '\0';
2949 
2950         /* If we're supposed to list the processes, too, do that now. */
2951         if (opt_processes)
2952                 lp->str = add_processes(wip, lp->str, wip->pg);
2953 
2954         /* Create the sort key. */
2955         cp = lp->key = safe_malloc(sortkey_sz);
2956         for (i = 0; i < opt_snum; ++i) {
2957                 int j = opt_sort[i] & 0xff;
2958 
2959                 assert(columns[j].get_sortkey != NULL);
2960                 columns[j].get_sortkey(cp, opt_sort[i] & ~0xff, wip);
2961                 cp += columns[j].sortkey_width;
2962         }
2963 
2964         /* Insert into AVL tree. */
2965         uu_avl_node_init(lp, &lp->node, lines_pool);
2966         (void) uu_avl_find(lines, lp, NULL, &idx);
2967         uu_avl_insert(lines, lp, idx);
2968 
2969         return (0);
2970 }
2971 
2972 static int
2973 list_if_enabled(void *unused, scf_walkinfo_t *wip)
2974 {
2975         if (wip->pg != NULL ||
2976             instance_enabled(wip->inst, B_FALSE) == 1 ||
2977             instance_enabled(wip->inst, B_TRUE) == 1)
2978                 return (list_instance(unused, wip));
2979 
2980         return (0);
2981 }
2982 
2983 /*
2984  * Service FMRI selection: Lookup and call list_instance() for the instances.
2985  * Instance FMRI selection: Lookup and call list_instance().
2986  *
2987  * Note: This is shoehorned into a walk_dependencies() callback prototype so
2988  * it can be used in list_dependencies.
2989  */
2990 static int
2991 list_svc_or_inst_fmri(void *complain, scf_walkinfo_t *wip)
2992 {
2993         char *fmri;
2994         const char *svc_name, *inst_name, *pg_name, *save;
2995         scf_iter_t *iter;
2996         int ret;
2997 
2998         fmri = safe_strdup(wip->fmri);
2999 
3000         if (scf_parse_svc_fmri(fmri, NULL, &svc_name, &inst_name, &pg_name,
3001             NULL) != SCF_SUCCESS) {
3002                 if (complain)
3003                         uu_warn(gettext("FMRI \"%s\" is invalid.\n"),
3004                             wip->fmri);
3005                 exit_status = UU_EXIT_FATAL;
3006                 free(fmri);
3007                 return (0);
3008         }
3009 
3010         /*
3011          * Yes, this invalidates *_name, but we only care whether they're NULL
3012          * or not.
3013          */
3014         free(fmri);
3015 
3016         if (svc_name == NULL || pg_name != NULL) {
3017                 if (complain)
3018                         uu_warn(gettext("FMRI \"%s\" does not designate a "
3019                             "service or instance.\n"), wip->fmri);
3020                 return (0);
3021         }
3022 
3023         if (inst_name != NULL) {
3024                 /* instance */
3025                 if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc,
3026                     wip->inst, NULL, NULL, 0) != SCF_SUCCESS) {
3027                         if (scf_error() != SCF_ERROR_NOT_FOUND)
3028                                 scfdie();
3029 
3030                         if (complain)
3031                                 uu_warn(gettext(
3032                                     "Instance \"%s\" does not exist.\n"),
3033                                     wip->fmri);
3034                         return (0);
3035                 }
3036 
3037                 return (list_instance(NULL, wip));
3038         }
3039 
3040         /* service: Walk the instances. */
3041         if (scf_handle_decode_fmri(h, wip->fmri, wip->scope, wip->svc, NULL,
3042             NULL, NULL, 0) != SCF_SUCCESS) {
3043                 if (scf_error() != SCF_ERROR_NOT_FOUND)
3044                         scfdie();
3045 
3046                 if (complain)
3047                         uu_warn(gettext("Service \"%s\" does not exist.\n"),
3048                             wip->fmri);
3049 
3050                 exit_status = UU_EXIT_FATAL;
3051 
3052                 return (0);
3053         }
3054 
3055         iter = scf_iter_create(h);
3056         if (iter == NULL)
3057                 scfdie();
3058 
3059         if (scf_iter_service_instances(iter, wip->svc) != SCF_SUCCESS)
3060                 scfdie();
3061 
3062         if ((fmri = malloc(max_scf_fmri_length + 1)) == NULL) {
3063                 scf_iter_destroy(iter);
3064                 exit_status = UU_EXIT_FATAL;
3065                 return (0);
3066         }
3067 
3068         save = wip->fmri;
3069         wip->fmri = fmri;
3070         while ((ret = scf_iter_next_instance(iter, wip->inst)) == 1) {
3071                 if (scf_instance_to_fmri(wip->inst, fmri,
3072                     max_scf_fmri_length + 1) <= 0)
3073                         scfdie();
3074                 (void) list_instance(NULL, wip);
3075         }
3076         free(fmri);
3077         wip->fmri = save;
3078         if (ret == -1)
3079                 scfdie();
3080 
3081         exit_status = UU_EXIT_OK;
3082 
3083         scf_iter_destroy(iter);
3084 
3085         return (0);
3086 }
3087 
3088 /*
3089  * Dependency selection: Straightforward since each instance lists the
3090  * services it depends on.
3091  */
3092 
3093 static void
3094 walk_dependencies(scf_walkinfo_t *wip, scf_walk_callback callback, void *data)
3095 {
3096         scf_snapshot_t *snap;
3097         scf_iter_t *iter, *viter;
3098         int ret, vret;
3099         char *dep;
3100 
3101         assert(wip->inst != NULL);
3102 
3103         if ((iter = scf_iter_create(h)) == NULL ||
3104             (viter = scf_iter_create(h)) == NULL)
3105                 scfdie();
3106 
3107         snap = get_running_snapshot(wip->inst);
3108 
3109         if (scf_iter_instance_pgs_typed_composed(iter, wip->inst, snap,
3110             SCF_GROUP_DEPENDENCY) != SCF_SUCCESS)
3111                 scfdie();
3112 
3113         dep = safe_malloc(max_scf_value_length + 1);
3114 
3115         while ((ret = scf_iter_next_pg(iter, g_pg)) == 1) {
3116                 scf_type_t ty;
3117 
3118                 /* Ignore exclude_any dependencies. */
3119                 if (scf_pg_get_property(g_pg, SCF_PROPERTY_GROUPING, g_prop) !=
3120                     SCF_SUCCESS) {
3121                         if (scf_error() != SCF_ERROR_NOT_FOUND)
3122                                 scfdie();
3123 
3124                         continue;
3125                 }
3126 
3127                 if (scf_property_type(g_prop, &ty) != SCF_SUCCESS)
3128                         scfdie();
3129 
3130                 if (ty != SCF_TYPE_ASTRING)
3131                         continue;
3132 
3133                 if (scf_property_get_value(g_prop, g_val) != SCF_SUCCESS) {
3134                         if (scf_error() != SCF_ERROR_CONSTRAINT_VIOLATED)
3135                                 scfdie();
3136 
3137                         continue;
3138                 }
3139 
3140                 if (scf_value_get_astring(g_val, dep,
3141                     max_scf_value_length + 1) < 0)
3142                         scfdie();
3143 
3144                 if (strcmp(dep, SCF_DEP_EXCLUDE_ALL) == 0)
3145                         continue;
3146 
3147                 if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENTITIES, g_prop) !=
3148                     SCF_SUCCESS) {
3149                         if (scf_error() != SCF_ERROR_NOT_FOUND)
3150                                 scfdie();
3151 
3152                         continue;
3153                 }
3154 
3155                 if (scf_iter_property_values(viter, g_prop) != SCF_SUCCESS)
3156                         scfdie();
3157 
3158                 while ((vret = scf_iter_next_value(viter, g_val)) == 1) {
3159                         if (scf_value_get_astring(g_val, dep,
3160                             max_scf_value_length + 1) < 0)
3161                                 scfdie();
3162 
3163                         wip->fmri = dep;
3164                         if (callback(data, wip) != 0)
3165                                 goto out;
3166                 }
3167                 if (vret == -1)
3168                         scfdie();
3169         }
3170         if (ret == -1)
3171                 scfdie();
3172 
3173 out:
3174         scf_iter_destroy(viter);
3175         scf_iter_destroy(iter);
3176         scf_snapshot_destroy(snap);
3177 }
3178 
3179 static int
3180 list_dependencies(void *data, scf_walkinfo_t *wip)
3181 {
3182         walk_dependencies(wip, list_svc_or_inst_fmri, data);
3183         return (0);
3184 }
3185 
3186 
3187 /*
3188  * Dependent selection: The "providing" service's or instance's FMRI is parsed
3189  * into the provider_* variables, the instances are walked, and any instance
3190  * which lists an FMRI which parses to these components is selected.  This is
3191  * inefficient in the face of multiple operands, but that should be uncommon.
3192  */
3193 
3194 static char *provider_scope;
3195 static char *provider_svc;
3196 static char *provider_inst;     /* NULL for services */
3197 
3198 /*ARGSUSED*/
3199 static int
3200 check_against_provider(void *arg, scf_walkinfo_t *wip)
3201 {
3202         char *cfmri;
3203         const char *scope_name, *svc_name, *inst_name, *pg_name;
3204         int *matchp = arg;
3205 
3206         cfmri = safe_strdup(wip->fmri);
3207 
3208         if (scf_parse_svc_fmri(cfmri, &scope_name, &svc_name, &inst_name,
3209             &pg_name, NULL) != SCF_SUCCESS) {
3210                 free(cfmri);
3211                 return (0);
3212         }
3213 
3214         if (svc_name == NULL || pg_name != NULL) {
3215                 free(cfmri);
3216                 return (0);
3217         }
3218 
3219         /*
3220          * If the user has specified an instance, then also match dependencies
3221          * on the service itself.
3222          */
3223         *matchp = (strcmp(provider_scope, scope_name) == 0 &&
3224             strcmp(provider_svc, svc_name) == 0 &&
3225             (provider_inst == NULL ? (inst_name == NULL) :
3226             (inst_name == NULL || strcmp(provider_inst, inst_name) == 0)));
3227 
3228         free(cfmri);
3229 
3230         /* Stop on matches. */
3231         return (*matchp);
3232 }
3233 
3234 static int
3235 list_if_dependent(void *unused, scf_walkinfo_t *wip)
3236 {
3237         /* Only proceed if this instance depends on provider_*. */
3238         int match = 0;
3239 
3240         (void) walk_dependencies(wip, check_against_provider, &match);
3241 
3242         if (match)
3243                 return (list_instance(unused, wip));
3244 
3245         return (0);
3246 }
3247 
3248 /*ARGSUSED*/
3249 static int
3250 list_dependents(void *unused, scf_walkinfo_t *wip)
3251 {
3252         char *save;
3253         int ret;
3254 
3255         if (scf_scope_get_name(wip->scope, provider_scope,
3256             max_scf_fmri_length) <= 0 ||
3257             scf_service_get_name(wip->svc, provider_svc,
3258             max_scf_fmri_length) <= 0)
3259                 scfdie();
3260 
3261         save = provider_inst;
3262         if (wip->inst == NULL)
3263                 provider_inst = NULL;
3264         else if (scf_instance_get_name(wip->inst, provider_inst,
3265             max_scf_fmri_length) <= 0)
3266                 scfdie();
3267 
3268         ret = scf_walk_fmri(h, 0, NULL, 0, list_if_dependent, NULL, NULL,
3269             uu_warn);
3270 
3271         provider_inst = save;
3272 
3273         return (ret);
3274 }
3275 
3276 /*
3277  * main() & helpers
3278  */
3279 
3280 static void
3281 add_sort_column(const char *col, int reverse)
3282 {
3283         int i;
3284 
3285         ++opt_snum;
3286 
3287         opt_sort = realloc(opt_sort, opt_snum * sizeof (*opt_sort));
3288         if (opt_sort == NULL)
3289                 uu_die(gettext("Too many sort criteria: out of memory.\n"));
3290 
3291         for (i = 0; i < ncolumns; ++i) {
3292                 if (strcasecmp(col, columns[i].name) == 0)
3293                         break;
3294         }
3295 
3296         if (i < ncolumns)
3297                 opt_sort[opt_snum - 1] = (reverse ? i | 0x100 : i);
3298         else
3299                 uu_die(gettext("Unrecognized sort column \"%s\".\n"), col);
3300 
3301         sortkey_sz += columns[i].sortkey_width;
3302 }
3303 
3304 static void
3305 add_restarter(const char *fmri)
3306 {
3307         char *cfmri;
3308         const char *pg_name;
3309         struct pfmri_list *rest;
3310 
3311         cfmri = safe_strdup(fmri);
3312         rest = safe_malloc(sizeof (*rest));
3313 
3314         if (scf_parse_svc_fmri(cfmri, &rest->scope, &rest->service,
3315             &rest->instance, &pg_name, NULL) != SCF_SUCCESS)
3316                 uu_die(gettext("Restarter FMRI \"%s\" is invalid.\n"), fmri);
3317 
3318         if (rest->instance == NULL || pg_name != NULL)
3319                 uu_die(gettext("Restarter FMRI \"%s\" does not designate an "
3320                     "instance.\n"), fmri);
3321 
3322         rest->next = restarters;
3323         restarters = rest;
3324         return;
3325 
3326 err:
3327         free(cfmri);
3328         free(rest);
3329 }
3330 
3331 /* ARGSUSED */
3332 static int
3333 line_cmp(const void *l_arg, const void *r_arg, void *private)
3334 {
3335         const struct avl_string *l = l_arg;
3336         const struct avl_string *r = r_arg;
3337 
3338         return (memcmp(l->key, r->key, sortkey_sz));
3339 }
3340 
3341 /* ARGSUSED */
3342 static int
3343 print_line(void *e, void *private)
3344 {
3345         struct avl_string *lp = e;
3346 
3347         (void) puts(lp->str);
3348 
3349         return (UU_WALK_NEXT);
3350 }
3351 
3352 /* ARGSUSED */
3353 static void
3354 errignore(const char *str, ...)
3355 {}
3356 
3357 int
3358 main(int argc, char **argv)
3359 {
3360         char opt, opt_mode;
3361         int i, n;
3362         char *columns_str = NULL;
3363         char *cp;
3364         const char *progname;
3365         int err, missing = 1, ignored, *errarg;
3366         uint_t nzents = 0, zent = 0;
3367         zoneid_t *zids = NULL;
3368         char zonename[ZONENAME_MAX];
3369         void (*errfunc)(const char *, ...);
3370 
3371         int show_all = 0;
3372         int show_zones = 0;
3373 
3374         const char * const options = "aHpvno:R:s:S:dDlL?xZz:";
3375 
3376         (void) setlocale(LC_ALL, "");
3377 
3378         locale = setlocale(LC_MESSAGES, NULL);
3379         if (locale) {
3380                 locale = safe_strdup(locale);
3381                 _scf_sanitize_locale(locale);
3382         }
3383 
3384         (void) textdomain(TEXT_DOMAIN);
3385         progname = uu_setpname(argv[0]);
3386 
3387         exit_status = UU_EXIT_OK;
3388 
3389         max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
3390         max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
3391         max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
3392         max_scf_type_length = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH);
3393 
3394         if (max_scf_name_length == -1 || max_scf_value_length == -1 ||
3395             max_scf_fmri_length == -1 || max_scf_type_length == -1)
3396                 scfdie();
3397 
3398         now = time(NULL);
3399         assert(now != -1);
3400 
3401         /*
3402          * opt_mode is the mode of operation.  0 for plain, 'd' for
3403          * dependencies, 'D' for dependents, and 'l' for detailed (long).  We
3404          * need to know now so we know which options are valid.
3405          */
3406         opt_mode = 0;
3407         while ((opt = getopt(argc, argv, options)) != -1) {
3408                 switch (opt) {
3409                 case '?':
3410                         if (optopt == '?') {
3411                                 print_help(progname);
3412                                 return (UU_EXIT_OK);
3413                         } else {
3414                                 argserr(progname);
3415                                 /* NOTREACHED */
3416                         }
3417 
3418                 case 'd':
3419                 case 'D':
3420                 case 'l':
3421                 case 'L':
3422                         if (opt_mode != 0)
3423                                 argserr(progname);
3424 
3425                         opt_mode = opt;
3426                         break;
3427 
3428                 case 'n':
3429                         if (opt_mode != 0)
3430                                 argserr(progname);
3431 
3432                         opt_mode = opt;
3433                         break;
3434 
3435                 case 'x':
3436                         if (opt_mode != 0)
3437                                 argserr(progname);
3438 
3439                         opt_mode = opt;
3440                         break;
3441 
3442                 default:
3443                         break;
3444                 }
3445         }
3446 
3447         sortkey_sz = 0;
3448 
3449         optind = 1;     /* Reset getopt() */
3450         while ((opt = getopt(argc, argv, options)) != -1) {
3451                 switch (opt) {
3452                 case 'a':
3453                         if (opt_mode != 0)
3454                                 argserr(progname);
3455                         show_all = 1;
3456                         break;
3457 
3458                 case 'H':
3459                         if (opt_mode == 'l' || opt_mode == 'x')
3460                                 argserr(progname);
3461                         opt_scripted = 1;
3462                         break;
3463 
3464                 case 'p':
3465                         if (opt_mode == 'x')
3466                                 argserr(progname);
3467                         opt_processes = 1;
3468                         break;
3469 
3470                 case 'v':
3471                         opt_verbose = 1;
3472                         break;
3473 
3474                 case 'o':
3475                         if (opt_mode == 'l' || opt_mode == 'x')
3476                                 argserr(progname);
3477                         columns_str = optarg;
3478                         break;
3479 
3480                 case 'R':
3481                         if (opt_mode != 0 || opt_mode == 'x')
3482                                 argserr(progname);
3483 
3484                         add_restarter(optarg);
3485                         break;
3486 
3487                 case 's':
3488                 case 'S':
3489                         if (opt_mode != 0)
3490                                 argserr(progname);
3491 
3492                         add_sort_column(optarg, optopt == 'S');
3493                         break;
3494 
3495                 case 'd':
3496                 case 'D':
3497                 case 'l':
3498                 case 'L':
3499                 case 'n':
3500                 case 'x':
3501                         assert(opt_mode == optopt);
3502                         break;
3503 
3504                 case 'z':
3505                         if (getzoneid() != GLOBAL_ZONEID)
3506                                 uu_die(gettext("svcs -z may only be used from "
3507                                     "the global zone\n"));
3508                         if (show_zones)
3509                                 argserr(progname);
3510 
3511                         opt_zone = optarg;
3512                         break;
3513 
3514                 case 'Z':
3515                         if (getzoneid() != GLOBAL_ZONEID)
3516                                 uu_die(gettext("svcs -Z may only be used from "
3517                                     "the global zone\n"));
3518                         if (opt_zone != NULL)
3519                                 argserr(progname);
3520 
3521                         show_zones = 1;
3522                         break;
3523 
3524                 case '?':
3525                         argserr(progname);
3526                         /* NOTREACHED */
3527 
3528                 default:
3529                         assert(0);
3530                         abort();
3531                 }
3532         }
3533 
3534         /*
3535          * -a is only meaningful when given no arguments
3536          */
3537         if (show_all && optind != argc)
3538                 uu_warn(gettext("-a ignored when used with arguments.\n"));
3539 
3540         while (show_zones) {
3541                 uint_t found;
3542 
3543                 if (zone_list(NULL, &nzents) != 0)
3544                         uu_die(gettext("could not get number of zones"));
3545 
3546                 if ((zids = malloc(nzents * sizeof (zoneid_t))) == NULL) {
3547                         uu_die(gettext("could not allocate array for "
3548                             "%d zone IDs"), nzents);
3549                 }
3550 
3551                 found = nzents;
3552 
3553                 if (zone_list(zids, &found) != 0)
3554                         uu_die(gettext("could not get zone list"));
3555 
3556                 /*
3557                  * If the number of zones has not changed between our calls to
3558                  * zone_list(), we're done -- otherwise, we must free our array
3559                  * of zone IDs and take another lap.
3560                  */
3561                 if (found == nzents)
3562                         break;
3563 
3564                 free(zids);
3565         }
3566 
3567         argc -= optind;
3568         argv += optind;
3569 
3570 again:
3571         h = scf_handle_create(SCF_VERSION);
3572         if (h == NULL)
3573                 scfdie();
3574 
3575         if (opt_zone != NULL || zids != NULL) {
3576                 scf_value_t *zone;
3577 
3578                 assert(opt_zone == NULL || zids == NULL);
3579 
3580                 if (opt_zone == NULL) {
3581                         if (getzonenamebyid(zids[zent++],
3582                             zonename, sizeof (zonename)) < 0) {
3583                                 uu_warn(gettext("could not get name for "
3584                                     "zone %d; ignoring"), zids[zent - 1]);
3585                                 goto nextzone;
3586                         }
3587 
3588                         g_zonename = zonename;
3589                 } else {
3590                         g_zonename = opt_zone;
3591                 }
3592 
3593                 if ((zone = scf_value_create(h)) == NULL)
3594                         scfdie();
3595 
3596                 if (scf_value_set_astring(zone, g_zonename) != SCF_SUCCESS)
3597                         scfdie();
3598 
3599                 if (scf_handle_decorate(h, "zone", zone) != SCF_SUCCESS)
3600                         uu_die(gettext("invalid zone '%s'\n"), g_zonename);
3601 
3602                 scf_value_destroy(zone);
3603         }
3604 
3605         if (scf_handle_bind(h) == -1) {
3606                 if (g_zonename != NULL) {
3607                         uu_warn(gettext("Could not bind to repository "
3608                             "server for zone %s: %s\n"), g_zonename,
3609                             scf_strerror(scf_error()));
3610 
3611                         if (!show_zones)
3612                                 return (UU_EXIT_FATAL);
3613 
3614                         goto nextzone;
3615                 }
3616 
3617                 uu_die(gettext("Could not bind to repository server: %s.  "
3618                     "Exiting.\n"), scf_strerror(scf_error()));
3619         }
3620 
3621         if ((g_pg = scf_pg_create(h)) == NULL ||
3622             (g_prop = scf_property_create(h)) == NULL ||
3623             (g_val = scf_value_create(h)) == NULL)
3624                 scfdie();
3625 
3626         if (show_zones) {
3627                 /*
3628                  * It's hard to avoid editorializing here, but suffice it to
3629                  * say that scf_walk_fmri() takes an error handler, the
3630                  * interface to which has been regrettably misdesigned:  the
3631                  * handler itself takes exclusively a string -- even though
3632                  * scf_walk_fmri() has detailed, programmatic knowledge
3633                  * of the error condition at the time it calls its errfunc.
3634                  * That is, only the error message and not the error semantics
3635                  * are given to the handler.  This is poor interface at best,
3636                  * but it is particularly problematic when we are talking to
3637                  * multiple repository servers (as when we are iterating over
3638                  * all zones) as we do not want to treat failure to find a
3639                  * match in one zone as overall failure.  Ideally, we would
3640                  * simply ignore SCF_MSG_PATTERN_NOINSTANCE and correctly
3641                  * process the others, but alas, no such interface exists --
3642                  * and we must settle for instead ignoring all errfunc-called
3643                  * errors in the case that we are iterating over all zones...
3644                  */
3645                 errfunc = errignore;
3646                 errarg = missing ? &missing : &ignored;
3647                 missing = 0;
3648         } else {
3649                 errfunc = uu_warn;
3650                 errarg = &exit_status;
3651         }
3652 
3653         /*
3654          * If we're in long mode, take care of it now before we deal with the
3655          * sorting and the columns, since we won't use them anyway.
3656          */
3657         if (opt_mode == 'l') {
3658                 if (argc == 0)
3659                         argserr(progname);
3660 
3661                 if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE,
3662                     print_detailed, NULL, errarg, errfunc)) != 0) {
3663                         uu_warn(gettext("failed to iterate over "
3664                             "instances: %s\n"), scf_strerror(err));
3665                         exit_status = UU_EXIT_FATAL;
3666                 }
3667 
3668                 goto nextzone;
3669         }
3670 
3671         if (opt_mode == 'L') {
3672                 if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE,
3673                     print_log, NULL, &exit_status, uu_warn)) != 0) {
3674                         uu_warn(gettext("failed to iterate over "
3675                             "instances: %s\n"), scf_strerror(err));
3676                         exit_status = UU_EXIT_FATAL;
3677                 }
3678 
3679                 goto nextzone;
3680         }
3681 
3682         if (opt_mode == 'n') {
3683                 print_notify_special();
3684                 if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE,
3685                     print_notify, NULL, errarg, errfunc)) != 0) {
3686                         uu_warn(gettext("failed to iterate over "
3687                             "instances: %s\n"), scf_strerror(err));
3688                         exit_status = UU_EXIT_FATAL;
3689                 }
3690 
3691                 goto nextzone;
3692         }
3693 
3694         if (opt_mode == 'x') {
3695                 explain(opt_verbose, argc, argv);
3696                 goto nextzone;
3697         }
3698 
3699         if (columns_str == NULL) {
3700                 if (opt_snum == 0) {
3701                         if (show_zones)
3702                                 add_sort_column("zone", 0);
3703 
3704                         /* Default sort. */
3705                         add_sort_column("state", 0);
3706                         add_sort_column("stime", 0);
3707                         add_sort_column("fmri", 0);
3708                 }
3709 
3710                 if (!opt_verbose) {
3711                         columns_str = safe_strdup(show_zones ?
3712                             "zone,state,stime,fmri" : "state,stime,fmri");
3713                 } else {
3714                         columns_str = safe_strdup(show_zones ?
3715                             "zone,state,nstate,stime,ctid,fmri" :
3716                             "state,nstate,stime,ctid,fmri");
3717                 }
3718         }
3719 
3720         if (opt_columns == NULL) {
3721                 /* Decode columns_str into opt_columns. */
3722                 line_sz = 0;
3723 
3724                 opt_cnum = 1;
3725                 for (cp = columns_str; *cp != '\0'; ++cp)
3726                         if (*cp == ',')
3727                                 ++opt_cnum;
3728 
3729                 if (*columns_str == '\0')
3730                         uu_die(gettext("No columns specified.\n"));
3731 
3732                 opt_columns = malloc(opt_cnum * sizeof (*opt_columns));
3733                 if (opt_columns == NULL)
3734                         uu_die(gettext("Too many columns.\n"));
3735 
3736                 for (n = 0; *columns_str != '\0'; ++n) {
3737                         i = getcolumnopt(&columns_str);
3738                         if (i == -1)
3739                                 uu_die(gettext("Unknown column \"%s\".\n"),
3740                                     columns_str);
3741 
3742                         if (strcmp(columns[i].name, "N") == 0 ||
3743                             strcmp(columns[i].name, "SN") == 0 ||
3744                             strcmp(columns[i].name, "NSTA") == 0 ||
3745                             strcmp(columns[i].name, "NSTATE") == 0)
3746                                 opt_nstate_shown = 1;
3747 
3748                         opt_columns[n] = i;
3749                         line_sz += columns[i].width + 1;
3750                 }
3751 
3752                 if ((lines_pool = uu_avl_pool_create("lines_pool",
3753                     sizeof (struct avl_string), offsetof(struct avl_string,
3754                     node), line_cmp, UU_AVL_DEBUG)) == NULL ||
3755                     (lines = uu_avl_create(lines_pool, NULL, 0)) == NULL)
3756                         uu_die(gettext("Unexpected libuutil error: %s\n"),
3757                             uu_strerror(uu_error()));
3758         }
3759 
3760         switch (opt_mode) {
3761         case 0:
3762                 /*
3763                  * If we already have a hash table (e.g., because we are
3764                  * processing multiple zones), destroy it before creating
3765                  * a new one.
3766                  */
3767                 if (ht_buckets != NULL)
3768                         ht_free();
3769 
3770                 ht_init();
3771 
3772                 /* Always show all FMRIs when given arguments or restarters */
3773                 if (argc != 0 || restarters != NULL)
3774                         show_all =  1;
3775 
3776                 if ((err = scf_walk_fmri(h, argc, argv,
3777                     SCF_WALK_MULTIPLE | SCF_WALK_LEGACY,
3778                     show_all ? list_instance : list_if_enabled, NULL,
3779                     errarg, errfunc)) != 0) {
3780                         uu_warn(gettext("failed to iterate over "
3781                             "instances: %s\n"), scf_strerror(err));
3782                         exit_status = UU_EXIT_FATAL;
3783                 }
3784                 break;
3785 
3786         case 'd':
3787                 if (argc == 0)
3788                         argserr(progname);
3789 
3790                 if ((err = scf_walk_fmri(h, argc, argv,
3791                     SCF_WALK_MULTIPLE, list_dependencies, NULL,
3792                     errarg, errfunc)) != 0) {
3793                         uu_warn(gettext("failed to iterate over "
3794                             "instances: %s\n"), scf_strerror(err));
3795                         exit_status = UU_EXIT_FATAL;
3796                 }
3797                 break;
3798 
3799         case 'D':
3800                 if (argc == 0)
3801                         argserr(progname);
3802 
3803                 provider_scope = safe_malloc(max_scf_fmri_length);
3804                 provider_svc = safe_malloc(max_scf_fmri_length);
3805                 provider_inst = safe_malloc(max_scf_fmri_length);
3806 
3807                 if ((err = scf_walk_fmri(h, argc, argv,
3808                     SCF_WALK_MULTIPLE | SCF_WALK_SERVICE,
3809                     list_dependents, NULL, &exit_status, uu_warn)) != 0) {
3810                         uu_warn(gettext("failed to iterate over "
3811                             "instances: %s\n"), scf_strerror(err));
3812                         exit_status = UU_EXIT_FATAL;
3813                 }
3814 
3815                 free(provider_scope);
3816                 free(provider_svc);
3817                 free(provider_inst);
3818                 break;
3819 
3820         case 'n':
3821                 break;
3822 
3823         default:
3824                 assert(0);
3825                 abort();
3826         }
3827 
3828 nextzone:
3829         if (show_zones && zent < nzents && exit_status == 0) {
3830                 scf_handle_destroy(h);
3831                 goto again;
3832         }
3833 
3834         if (show_zones && exit_status == 0)
3835                 exit_status = missing;
3836 
3837         if (opt_columns == NULL)
3838                 return (exit_status);
3839 
3840         if (!opt_scripted)
3841                 print_header();
3842 
3843         (void) uu_avl_walk(lines, print_line, NULL, 0);
3844 
3845         return (exit_status);
3846 }