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