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