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