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