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