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