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