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