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