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