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