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