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