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