1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright 2019 Joyent, Inc.
29 */
30
31 /*
32 * svcprop - report service configuration properties
33 */
34
35 #include <locale.h>
36 #include <libintl.h>
37 #include <libscf.h>
38 #include <libscf_priv.h>
39 #include <libuutil.h>
40 #include <stddef.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <strings.h>
45 #include <assert.h>
46 #include <zone.h>
47
48 #ifndef TEXT_DOMAIN
49 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
50 #endif /* TEXT_DOMAIN */
51
52 /*
53 * Error functions. These can change if the quiet (-q) option is used.
54 */
55 static void (*warn)(const char *, ...) = uu_warn;
56 static __NORETURN void (*die)(const char *, ...) = uu_die;
57
58 /*
59 * Entity encapsulation. This allows me to treat services and instances
60 * similarly, and avoid duplicating process_ent().
61 */
62 typedef struct {
63 char type; /* !=0: service, 0: instance */
64 union {
65 scf_service_t *svc;
66 scf_instance_t *inst;
67 } u;
68 } scf_entityp_t;
69
70 #define ENT_INSTANCE 0
71
72 #define SCF_ENTITY_SET_TO_SERVICE(ent, s) { ent.type = 1; ent.u.svc = s; }
73
74 #define SCF_ENTITY_SET_TO_INSTANCE(ent, i) \
75 { ent.type = ENT_INSTANCE; ent.u.inst = i; }
76
77 #define scf_entity_get_pg(ent, name, pg) \
78 (ent.type ? scf_service_get_pg(ent.u.svc, name, pg) : \
79 scf_instance_get_pg(ent.u.inst, name, pg))
80
81 #define scf_entity_to_fmri(ent, buf, buf_sz) \
82 (ent.type ? scf_service_to_fmri(ent.u.svc, buf, buf_sz) : \
83 scf_instance_to_fmri(ent.u.inst, buf, buf_sz))
84
85 #define SCF_ENTITY_TYPE_NAME(ent) (ent.type ? "service" : "instance")
86
87 /*
88 * Data structure for -p arguments. Since they may be name or name/name, we
89 * just track the components.
90 */
91 typedef struct svcprop_prop_node {
92 uu_list_node_t spn_list_node;
93 const char *spn_comp1;
94 const char *spn_comp2;
95 } svcprop_prop_node_t;
96
97 static uu_list_pool_t *prop_pool;
98 static uu_list_t *prop_list;
99
100 static scf_handle_t *hndl;
101 static ssize_t max_scf_name_length;
102 static ssize_t max_scf_value_length;
103 static ssize_t max_scf_fmri_length;
104
105 /* Options */
106 static int quiet = 0; /* No output. Nothing found, exit(1) */
107 static int types = 0; /* Display types of properties. */
108 static int verbose = 0; /* Print not found errors to stderr. */
109 static int fmris = 0; /* Display full FMRIs for properties. */
110 static int wait = 0; /* Wait mode. */
111 static char *snapshot = "running"; /* Snapshot to use. */
112 static int Cflag = 0; /* C option supplied */
113 static int cflag = 0; /* c option supplied */
114 static int sflag = 0; /* s option supplied */
115 static int return_code; /* main's return code */
116
117 #define PRINT_NOPROP_ERRORS (!quiet || verbose)
118
119 /*
120 * For unexpected libscf errors. The ending newline is necessary to keep
121 * uu_die() from appending the errno error.
122 */
123 static void
scfdie(void)124 scfdie(void)
125 {
126 die(gettext("Unexpected libscf error: %s. Exiting.\n"),
127 scf_strerror(scf_error()));
128 }
129
130 static void *
safe_malloc(size_t sz)131 safe_malloc(size_t sz)
132 {
133 void *p;
134
135 p = malloc(sz);
136 if (p == NULL)
137 die(gettext("Could not allocate memory"));
138
139 return (p);
140 }
141
142 static void
usage(void)143 usage(void)
144 {
145 (void) fprintf(stderr, gettext("Usage: %1$s [-fqtv] "
146 "[-C | -c | -s snapshot] [-z zone] "
147 "[-p [name/]name]... \n"
148 " {FMRI | pattern}...\n"
149 " %1$s -w [-fqtv] [-z zone] [-p [name/]name] "
150 "{FMRI | pattern}\n"), uu_getpname());
151 exit(UU_EXIT_USAGE);
152 }
153
154 /*
155 * Return an allocated copy of str, with the Bourne shell's metacharacters
156 * escaped by '\'.
157 *
158 * What about unicode?
159 */
160 static char *
quote_for_shell(const char * str)161 quote_for_shell(const char *str)
162 {
163 const char *sp;
164 char *dst, *dp;
165 size_t dst_len;
166
167 const char * const metachars = ";&()|^<>\n \t\\\"\'`";
168
169 if (str[0] == '\0')
170 return (strdup("\"\""));
171
172 dst_len = 0;
173 for (sp = str; *sp != '\0'; ++sp) {
174 ++dst_len;
175
176 if (strchr(metachars, *sp) != NULL)
177 ++dst_len;
178 }
179
180 if (sp - str == dst_len)
181 return (strdup(str));
182
183 dst = safe_malloc(dst_len + 1);
184
185 for (dp = dst, sp = str; *sp != '\0'; ++dp, ++sp) {
186 if (strchr(metachars, *sp) != NULL)
187 *dp++ = '\\';
188
189 *dp = *sp;
190 }
191 *dp = '\0';
192
193 return (dst);
194 }
195
196 static void
print_value(scf_value_t * val)197 print_value(scf_value_t *val)
198 {
199 char *buf, *qbuf;
200 ssize_t bufsz, r;
201
202 bufsz = scf_value_get_as_string(val, NULL, 0) + 1;
203 if (bufsz - 1 < 0)
204 scfdie();
205
206 buf = safe_malloc(bufsz);
207
208 r = scf_value_get_as_string(val, buf, bufsz);
209 assert(r + 1 == bufsz);
210
211 qbuf = quote_for_shell(buf);
212 (void) fputs(qbuf, stdout);
213
214 free(qbuf);
215 free(buf);
216 }
217
218 /*
219 * Display a property's values on a line. If types is true, prepend
220 * identification (the FMRI if fmris is true, pg/prop otherwise) and the type
221 * of the property.
222 */
223 static void
display_prop(scf_propertygroup_t * pg,scf_property_t * prop)224 display_prop(scf_propertygroup_t *pg, scf_property_t *prop)
225 {
226 scf_value_t *val;
227 scf_iter_t *iter;
228 int ret, first, err;
229
230 const char * const permission_denied_emsg =
231 gettext("Permission denied.\n");
232
233 if (types) {
234 scf_type_t ty;
235 char *buf;
236 size_t buf_sz;
237
238 if (fmris) {
239 buf_sz = max_scf_fmri_length + 1;
240 buf = safe_malloc(buf_sz);
241
242 if (scf_property_to_fmri(prop, buf, buf_sz) == -1)
243 scfdie();
244 (void) fputs(buf, stdout);
245
246 free(buf);
247 } else {
248 buf_sz = max_scf_name_length + 1;
249 buf = safe_malloc(buf_sz);
250
251 if (scf_pg_get_name(pg, buf, buf_sz) < 0)
252 scfdie();
253 (void) fputs(buf, stdout);
254 (void) putchar('/');
255
256 if (scf_property_get_name(prop, buf, buf_sz) < 0)
257 scfdie();
258 (void) fputs(buf, stdout);
259
260 free(buf);
261 }
262
263 (void) putchar(' ');
264
265 if (scf_property_type(prop, &ty) == -1)
266 scfdie();
267 (void) fputs(scf_type_to_string(ty), stdout);
268 (void) putchar(' ');
269 }
270
271 if ((iter = scf_iter_create(hndl)) == NULL ||
272 (val = scf_value_create(hndl)) == NULL)
273 scfdie();
274
275 if (scf_iter_property_values(iter, prop) == -1)
276 scfdie();
277
278 first = 1;
279 while ((ret = scf_iter_next_value(iter, val)) == 1) {
280 if (first)
281 first = 0;
282 else
283 (void) putchar(' ');
284 print_value(val);
285 }
286 if (ret == -1) {
287 err = scf_error();
288 if (err == SCF_ERROR_PERMISSION_DENIED) {
289 if (uu_list_numnodes(prop_list) > 0)
290 die(permission_denied_emsg);
291 } else {
292 scfdie();
293 }
294 }
295
296 (void) putchar('\n');
297
298 scf_iter_destroy(iter);
299 (void) scf_value_destroy(val);
300 }
301
302 /*
303 * display_prop() all of the properties in the given property group. Force
304 * types to true so identification will be displayed.
305 */
306 static void
display_pg(scf_propertygroup_t * pg)307 display_pg(scf_propertygroup_t *pg)
308 {
309 scf_property_t *prop;
310 scf_iter_t *iter;
311 int ret;
312
313 types = 1; /* Always display types for whole propertygroups. */
314
315 if ((prop = scf_property_create(hndl)) == NULL ||
316 (iter = scf_iter_create(hndl)) == NULL)
317 scfdie();
318
319 if (scf_iter_pg_properties(iter, pg) == -1)
320 scfdie();
321
322 while ((ret = scf_iter_next_property(iter, prop)) == 1)
323 display_prop(pg, prop);
324 if (ret == -1)
325 scfdie();
326
327 scf_iter_destroy(iter);
328 scf_property_destroy(prop);
329 }
330
331 /*
332 * Common code to execute when a nonexistant property is encountered.
333 */
334 static void
noprop_common_action()335 noprop_common_action()
336 {
337 if (!PRINT_NOPROP_ERRORS)
338 /* We're not printing errors, so we can cut out early. */
339 exit(UU_EXIT_FATAL);
340
341 return_code = UU_EXIT_FATAL;
342 }
343
344 /*
345 * Iterate the properties of a service or an instance when no snapshot
346 * is specified.
347 */
348 static int
scf_iter_entity_pgs(scf_iter_t * iter,scf_entityp_t ent)349 scf_iter_entity_pgs(scf_iter_t *iter, scf_entityp_t ent)
350 {
351 int ret = 0;
352
353 if (ent.type) {
354 /*
355 * If we are displaying properties for a service,
356 * treat it as though it were a composed, current
357 * lookup. (implicit cflag) However, if a snapshot
358 * was specified, fail.
359 */
360 if (sflag)
361 die(gettext("Only instances have "
362 "snapshots.\n"));
363 ret = scf_iter_service_pgs(iter, ent.u.svc);
364 } else {
365 if (Cflag)
366 ret = scf_iter_instance_pgs(iter, ent.u.inst);
367 else
368 ret = scf_iter_instance_pgs_composed(iter, ent.u.inst,
369 NULL);
370 }
371 return (ret);
372 }
373
374 /*
375 * Return a snapshot for the supplied instance and snapshot name.
376 */
377 static scf_snapshot_t *
get_snapshot(const scf_instance_t * inst,const char * snapshot)378 get_snapshot(const scf_instance_t *inst, const char *snapshot)
379 {
380 scf_snapshot_t *snap = scf_snapshot_create(hndl);
381
382 if (snap == NULL)
383 scfdie();
384
385 if (scf_instance_get_snapshot(inst, snapshot, snap) == -1) {
386 switch (scf_error()) {
387 case SCF_ERROR_INVALID_ARGUMENT:
388 die(gettext("Invalid snapshot name.\n"));
389 /* NOTREACHED */
390
391 case SCF_ERROR_NOT_FOUND:
392 if (sflag == 0) {
393 scf_snapshot_destroy(snap);
394 snap = NULL;
395 } else
396 die(gettext("No such snapshot.\n"));
397 break;
398
399 default:
400 scfdie();
401 }
402 }
403
404 return (snap);
405 }
406
407 /*
408 * Entity (service or instance): If there are -p options,
409 * display_{pg,prop}() the named property groups and/or properties. Otherwise
410 * display_pg() all property groups.
411 */
412 static void
process_ent(scf_entityp_t ent)413 process_ent(scf_entityp_t ent)
414 {
415 scf_snapshot_t *snap = NULL;
416 scf_propertygroup_t *pg;
417 scf_property_t *prop;
418 scf_iter_t *iter;
419 svcprop_prop_node_t *spn;
420 int ret, err;
421
422 if (uu_list_numnodes(prop_list) == 0) {
423 if (quiet)
424 return;
425
426 if ((pg = scf_pg_create(hndl)) == NULL ||
427 (iter = scf_iter_create(hndl)) == NULL)
428 scfdie();
429
430 if (cflag || Cflag || ent.type != ENT_INSTANCE) {
431 if (scf_iter_entity_pgs(iter, ent) == -1)
432 scfdie();
433 } else {
434 if (snapshot != NULL)
435 snap = get_snapshot(ent.u.inst, snapshot);
436
437 if (scf_iter_instance_pgs_composed(iter, ent.u.inst,
438 snap) == -1)
439 scfdie();
440 if (snap)
441 scf_snapshot_destroy(snap);
442 }
443
444 while ((ret = scf_iter_next_pg(iter, pg)) == 1)
445 display_pg(pg);
446 if (ret == -1)
447 scfdie();
448
449 /*
450 * In normal usage, i.e. against the running snapshot,
451 * we must iterate over the current non-persistent
452 * pg's.
453 */
454 if (sflag == 0 && snap != NULL) {
455 scf_iter_reset(iter);
456 if (scf_iter_instance_pgs_composed(iter, ent.u.inst,
457 NULL) == -1)
458 scfdie();
459 while ((ret = scf_iter_next_pg(iter, pg)) == 1) {
460 uint32_t flags;
461
462 if (scf_pg_get_flags(pg, &flags) == -1)
463 scfdie();
464 if (flags & SCF_PG_FLAG_NONPERSISTENT)
465 display_pg(pg);
466 }
467 }
468 if (ret == -1)
469 scfdie();
470
471 scf_iter_destroy(iter);
472 scf_pg_destroy(pg);
473
474 return;
475 }
476
477 if ((pg = scf_pg_create(hndl)) == NULL ||
478 (prop = scf_property_create(hndl)) == NULL)
479 scfdie();
480
481 if (ent.type == ENT_INSTANCE && snapshot != NULL)
482 snap = get_snapshot(ent.u.inst, snapshot);
483
484 for (spn = uu_list_first(prop_list);
485 spn != NULL;
486 spn = uu_list_next(prop_list, spn)) {
487 if (ent.type == ENT_INSTANCE) {
488 if (Cflag)
489 ret = scf_instance_get_pg(ent.u.inst,
490 spn->spn_comp1, pg);
491 else
492 ret = scf_instance_get_pg_composed(ent.u.inst,
493 snap, spn->spn_comp1, pg);
494 err = scf_error();
495
496 /*
497 * If we didn't find it in the specified snapshot, use
498 * the current values if the pg is nonpersistent.
499 */
500 if (ret == -1 && !Cflag &&snap != NULL && err ==
501 SCF_ERROR_NOT_FOUND) {
502 ret = scf_instance_get_pg_composed(
503 ent.u.inst, NULL, spn->spn_comp1,
504 pg);
505
506 if (ret == 0) {
507 uint32_t flags;
508
509 if (scf_pg_get_flags(pg, &flags) == -1)
510 scfdie();
511 if ((flags & SCF_PG_FLAG_NONPERSISTENT)
512 == 0) {
513 ret = -1;
514 }
515 }
516 }
517 } else {
518 /*
519 * If we are displaying properties for a service,
520 * treat it as though it were a composed, current
521 * lookup. (implicit cflag) However, if a snapshot
522 * was specified, fail.
523 */
524 if (sflag)
525 die(gettext("Only instances have "
526 "snapshots.\n"));
527 ret = scf_entity_get_pg(ent, spn->spn_comp1, pg);
528 err = scf_error();
529 }
530 if (ret == -1) {
531 if (err != SCF_ERROR_NOT_FOUND)
532 scfdie();
533
534 if (PRINT_NOPROP_ERRORS) {
535 char *buf;
536
537 buf = safe_malloc(max_scf_fmri_length + 1);
538 if (scf_entity_to_fmri(ent, buf,
539 max_scf_fmri_length + 1) == -1)
540 scfdie();
541
542 uu_warn(gettext("Couldn't find property group "
543 "`%s' for %s `%s'.\n"), spn->spn_comp1,
544 SCF_ENTITY_TYPE_NAME(ent), buf);
545
546 free(buf);
547 }
548
549 noprop_common_action();
550
551 continue;
552 }
553
554 if (spn->spn_comp2 == NULL) {
555 if (!quiet)
556 display_pg(pg);
557 continue;
558 }
559
560 if (scf_pg_get_property(pg, spn->spn_comp2, prop) == -1) {
561 if (scf_error() != SCF_ERROR_NOT_FOUND)
562 scfdie();
563
564 if (PRINT_NOPROP_ERRORS) {
565 char *buf;
566
567 buf = safe_malloc(max_scf_fmri_length + 1);
568 if (scf_entity_to_fmri(ent, buf,
569 max_scf_fmri_length + 1) == -1)
570 scfdie();
571
572 /* FMRI syntax knowledge */
573 uu_warn(gettext("Couldn't find property "
574 "`%s/%s' for %s `%s'.\n"), spn->spn_comp1,
575 spn->spn_comp2, SCF_ENTITY_TYPE_NAME(ent),
576 buf);
577
578 free(buf);
579 }
580
581 noprop_common_action();
582
583 continue;
584 }
585
586 if (!quiet)
587 display_prop(pg, prop);
588 }
589
590 scf_property_destroy(prop);
591 scf_pg_destroy(pg);
592 if (snap)
593 scf_snapshot_destroy(snap);
594 }
595
596 /*
597 * Without -p options, just call display_pg(). Otherwise display_prop() the
598 * named properties of the property group.
599 */
600 static void
process_pg(scf_propertygroup_t * pg)601 process_pg(scf_propertygroup_t *pg)
602 {
603 scf_property_t *prop;
604 svcprop_prop_node_t *spn;
605
606 if (uu_list_first(prop_list) == NULL) {
607 if (quiet)
608 return;
609
610 display_pg(pg);
611 return;
612 }
613
614 prop = scf_property_create(hndl);
615 if (prop == NULL)
616 scfdie();
617
618 for (spn = uu_list_first(prop_list);
619 spn != NULL;
620 spn = uu_list_next(prop_list, spn)) {
621 if (spn->spn_comp2 != NULL) {
622 char *buf;
623
624 buf = safe_malloc(max_scf_fmri_length + 1);
625 if (scf_pg_to_fmri(pg, buf, max_scf_fmri_length + 1) ==
626 -1)
627 scfdie();
628
629 uu_xdie(UU_EXIT_USAGE, gettext("-p argument `%s/%s' "
630 "has too many components for property "
631 "group `%s'.\n"), spn->spn_comp1, spn->spn_comp2,
632 buf);
633 }
634
635 if (scf_pg_get_property(pg, spn->spn_comp1, prop) == 0) {
636 if (!quiet)
637 display_prop(pg, prop);
638 continue;
639 }
640
641 if (scf_error() != SCF_ERROR_NOT_FOUND)
642 scfdie();
643
644 if (PRINT_NOPROP_ERRORS) {
645 char *buf;
646
647 buf = safe_malloc(max_scf_fmri_length + 1);
648 if (scf_pg_to_fmri(pg, buf, max_scf_fmri_length + 1) ==
649 -1)
650 scfdie();
651
652 uu_warn(gettext("Couldn't find property `%s' in "
653 "property group `%s'.\n"), spn->spn_comp1, buf);
654
655 free(buf);
656 }
657
658 noprop_common_action();
659 }
660 }
661
662 /*
663 * If there are -p options, show the error. Otherwise just call
664 * display_prop().
665 */
666 static void
process_prop(scf_propertygroup_t * pg,scf_property_t * prop)667 process_prop(scf_propertygroup_t *pg, scf_property_t *prop)
668 {
669 if (uu_list_first(prop_list) != NULL) {
670 uu_warn(gettext("The -p option cannot be used with property "
671 "operands.\n"));
672 usage();
673 }
674
675 if (quiet)
676 return;
677
678 display_prop(pg, prop);
679 }
680
681 /* Decode an operand & dispatch. */
682 /* ARGSUSED */
683 static int
process_fmri(void * unused,scf_walkinfo_t * wip)684 process_fmri(void *unused, scf_walkinfo_t *wip)
685 {
686 scf_entityp_t ent;
687
688 /* Multiple matches imply multiple entities. */
689 if (wip->count > 1)
690 types = fmris = 1;
691
692 if (wip->prop != NULL) {
693 process_prop(wip->pg, wip->prop);
694 } else if (wip->pg != NULL) {
695 process_pg(wip->pg);
696 } else if (wip->inst != NULL) {
697 SCF_ENTITY_SET_TO_INSTANCE(ent, wip->inst);
698 process_ent(ent);
699 } else {
700 /* scf_walk_fmri() won't let this happen */
701 assert(wip->svc != NULL);
702 SCF_ENTITY_SET_TO_SERVICE(ent, wip->svc);
703 process_ent(ent);
704 }
705
706 return (0);
707 }
708
709 static void
add_prop(char * property)710 add_prop(char *property)
711 {
712 svcprop_prop_node_t *p, *last;
713 char *slash;
714
715 const char * const invalid_component_emsg =
716 gettext("Invalid component name `%s'.\n");
717
718 /* FMRI syntax knowledge. */
719 slash = strchr(property, '/');
720 if (slash != NULL) {
721 if (strchr(slash + 1, '/') != NULL) {
722 uu_warn(gettext("-p argument `%s' has too many "
723 "components.\n"), property);
724 usage();
725 }
726 }
727
728 if (slash != NULL)
729 *slash = '\0';
730
731 p = safe_malloc(sizeof (svcprop_prop_node_t));
732 uu_list_node_init(p, &p->spn_list_node, prop_pool);
733
734 p->spn_comp1 = property;
735 p->spn_comp2 = (slash == NULL) ? NULL : slash + 1;
736
737 if (uu_check_name(p->spn_comp1, UU_NAME_DOMAIN) == -1)
738 uu_xdie(UU_EXIT_USAGE, invalid_component_emsg, p->spn_comp1);
739 if (p->spn_comp2 != NULL &&
740 uu_check_name(p->spn_comp2, UU_NAME_DOMAIN) == -1)
741 uu_xdie(UU_EXIT_USAGE, invalid_component_emsg, p->spn_comp2);
742
743 last = uu_list_last(prop_list);
744 if (last != NULL) {
745 if ((last->spn_comp2 == NULL) ^ (p->spn_comp2 == NULL)) {
746 /*
747 * The -p options have mixed numbers of components.
748 * If they both turn out to be valid, then the
749 * single-component ones will specify property groups,
750 * so we need to turn on types to keep the output of
751 * display_prop() consistent with display_pg().
752 */
753 types = 1;
754 }
755 }
756
757 (void) uu_list_insert_after(prop_list, NULL, p);
758 }
759
760
761 /*
762 * Wait for a property group or property change.
763 *
764 * Extract a pg and optionally a property name from fmri & prop_list.
765 * _scf_pg_wait() for the pg, and display_pg(pg) or display_prop(pg, prop)
766 * when it returns.
767 */
768 /* ARGSUSED */
769 static int
do_wait(void * unused,scf_walkinfo_t * wip)770 do_wait(void *unused, scf_walkinfo_t *wip)
771 {
772 scf_property_t *prop;
773 scf_propertygroup_t *lpg, *pg;
774 const char *propname;
775 svcprop_prop_node_t *p;
776
777 const char *emsg_not_found = gettext("Not found.\n");
778
779 if ((lpg = scf_pg_create(hndl)) == NULL ||
780 (prop = scf_property_create(hndl)) == NULL)
781 scfdie();
782
783 if (wip->prop != NULL) {
784 if (uu_list_numnodes(prop_list) > 0)
785 uu_xdie(UU_EXIT_USAGE, gettext("-p cannot be used with "
786 "property FMRIs.\n"));
787 pg = wip->pg;
788
789 assert(strrchr(wip->fmri, '/') != NULL);
790 propname = strrchr(wip->fmri, '/') + 1;
791
792 } else if (wip->pg != NULL) {
793 p = uu_list_first(prop_list);
794
795 if (p != NULL) {
796 if (p->spn_comp2 != NULL)
797 uu_xdie(UU_EXIT_USAGE, gettext("-p argument "
798 "\"%s/%s\" has too many components for "
799 "property group %s.\n"),
800 p->spn_comp1, p->spn_comp2, wip->fmri);
801
802 propname = p->spn_comp1;
803
804 if (scf_pg_get_property(wip->pg, propname, prop) !=
805 SCF_SUCCESS) {
806 switch (scf_error()) {
807 case SCF_ERROR_INVALID_ARGUMENT:
808 uu_xdie(UU_EXIT_USAGE,
809 gettext("Invalid property name "
810 "\"%s\".\n"), propname);
811
812 /* NOTREACHED */
813
814 case SCF_ERROR_NOT_FOUND:
815 die(emsg_not_found);
816
817 default:
818 scfdie();
819 }
820 }
821 } else {
822 propname = NULL;
823 }
824
825 pg = wip->pg;
826
827 } else if (wip->inst != NULL) {
828
829 p = uu_list_first(prop_list);
830 if (p == NULL)
831 uu_xdie(UU_EXIT_USAGE,
832 gettext("Cannot wait for an instance.\n"));
833
834 if (scf_instance_get_pg(wip->inst, p->spn_comp1, lpg) !=
835 SCF_SUCCESS) {
836 switch (scf_error()) {
837 case SCF_ERROR_INVALID_ARGUMENT:
838 uu_xdie(UU_EXIT_USAGE, gettext("Invalid "
839 "property group name \"%s\".\n"),
840 p->spn_comp1);
841 /* NOTREACHED */
842
843 case SCF_ERROR_NOT_FOUND:
844 die(emsg_not_found);
845
846 default:
847 scfdie();
848 }
849 }
850
851 propname = p->spn_comp2;
852
853 if (propname != NULL) {
854 if (scf_pg_get_property(lpg, propname, prop) !=
855 SCF_SUCCESS) {
856 switch (scf_error()) {
857 case SCF_ERROR_INVALID_ARGUMENT:
858 uu_xdie(UU_EXIT_USAGE,
859 gettext("Invalid property name "
860 "\"%s\".\n"), propname);
861 /* NOTREACHED */
862
863 case SCF_ERROR_NOT_FOUND:
864 die(emsg_not_found);
865
866 default:
867 scfdie();
868 }
869 }
870 }
871
872 pg = lpg;
873
874 } else if (wip->svc != NULL) {
875
876 p = uu_list_first(prop_list);
877 if (p == NULL)
878 uu_xdie(UU_EXIT_USAGE,
879 gettext("Cannot wait for a service.\n"));
880
881 if (scf_service_get_pg(wip->svc, p->spn_comp1, lpg) !=
882 SCF_SUCCESS) {
883 switch (scf_error()) {
884 case SCF_ERROR_INVALID_ARGUMENT:
885 uu_xdie(UU_EXIT_USAGE, gettext("Invalid "
886 "property group name \"%s\".\n"),
887 p->spn_comp1);
888 /* NOTREACHED */
889
890 case SCF_ERROR_NOT_FOUND:
891 die(emsg_not_found);
892
893 default:
894 scfdie();
895 }
896 }
897
898 propname = p->spn_comp2;
899
900 if (propname != NULL) {
901 if (scf_pg_get_property(lpg, propname, prop) !=
902 SCF_SUCCESS) {
903 switch (scf_error()) {
904 case SCF_ERROR_INVALID_ARGUMENT:
905 uu_xdie(UU_EXIT_USAGE,
906 gettext("Invalid property name "
907 "\"%s\".\n"), propname);
908
909 /* NOTREACHED */
910
911 case SCF_ERROR_NOT_FOUND:
912 die(emsg_not_found);
913
914 default:
915 scfdie();
916 }
917 }
918 }
919
920 pg = lpg;
921
922 } else {
923 uu_xdie(UU_EXIT_USAGE, gettext("FMRI must specify an entity, "
924 "property group, or property.\n"));
925 }
926
927 for (;;) {
928 int ret;
929
930 ret = _scf_pg_wait(pg, -1);
931 if (ret != SCF_SUCCESS)
932 scfdie();
933
934 ret = scf_pg_update(pg);
935 if (ret < 0) {
936 if (scf_error() != SCF_ERROR_DELETED)
937 scfdie();
938
939 die(emsg_not_found);
940 }
941 if (ret == SCF_COMPLETE)
942 break;
943 }
944
945 if (propname != NULL) {
946 if (scf_pg_get_property(pg, propname, prop) == SCF_SUCCESS) {
947 if (!quiet)
948 display_prop(pg, prop);
949 } else {
950 if (scf_error() != SCF_ERROR_NOT_FOUND)
951 scfdie();
952
953 if (PRINT_NOPROP_ERRORS)
954 uu_warn(emsg_not_found);
955
956 return_code = UU_EXIT_FATAL;
957 }
958 } else {
959 if (!quiet)
960 display_pg(pg);
961 }
962
963 scf_property_destroy(prop);
964 scf_pg_destroy(lpg);
965
966 return (0);
967 }
968
969 /*
970 * These functions replace uu_warn() and uu_die() when the quiet (-q) option is
971 * used, and silently ignore any output.
972 */
973
974 /*ARGSUSED*/
975 static void
quiet_warn(const char * fmt,...)976 quiet_warn(const char *fmt, ...)
977 {
978 /* Do nothing */
979 }
980
981 /*ARGSUSED*/
982 static __NORETURN void
quiet_die(const char * fmt,...)983 quiet_die(const char *fmt, ...)
984 {
985 exit(UU_EXIT_FATAL);
986 }
987
988 int
main(int argc,char * argv[])989 main(int argc, char *argv[])
990 {
991 int c;
992 scf_walk_callback callback;
993 int flags;
994 int err;
995
996 (void) setlocale(LC_ALL, "");
997 (void) textdomain(TEXT_DOMAIN);
998
999 return_code = UU_EXIT_OK;
1000
1001 (void) uu_setpname(argv[0]);
1002
1003 prop_pool = uu_list_pool_create("properties",
1004 sizeof (svcprop_prop_node_t),
1005 offsetof(svcprop_prop_node_t, spn_list_node), NULL, 0);
1006 if (prop_pool == NULL)
1007 uu_die("%s\n", uu_strerror(uu_error()));
1008
1009 prop_list = uu_list_create(prop_pool, NULL, 0);
1010
1011 hndl = scf_handle_create(SCF_VERSION);
1012 if (hndl == NULL)
1013 scfdie();
1014
1015 while ((c = getopt(argc, argv, "Ccfp:qs:tvwz:")) != -1) {
1016 switch (c) {
1017 case 'C':
1018 if (cflag || sflag || wait)
1019 usage(); /* Not with -c, -s or -w */
1020 Cflag++;
1021 snapshot = NULL;
1022 break;
1023
1024 case 'c':
1025 if (Cflag || sflag || wait)
1026 usage(); /* Not with -C, -s or -w */
1027 cflag++;
1028 snapshot = NULL;
1029 break;
1030
1031 case 'f':
1032 types = 1;
1033 fmris = 1;
1034 break;
1035
1036 case 'p':
1037 add_prop(optarg);
1038 break;
1039
1040 case 'q':
1041 quiet = 1;
1042 warn = quiet_warn;
1043 die = quiet_die;
1044 break;
1045
1046 case 's':
1047 if (Cflag || cflag || wait)
1048 usage(); /* Not with -C, -c or -w */
1049 snapshot = optarg;
1050 sflag++;
1051 break;
1052
1053 case 't':
1054 types = 1;
1055 break;
1056
1057 case 'v':
1058 verbose = 1;
1059 break;
1060
1061 case 'w':
1062 if (Cflag || cflag || sflag)
1063 usage(); /* Not with -C, -c or -s */
1064 wait = 1;
1065 break;
1066
1067 case 'z': {
1068 scf_value_t *zone;
1069 scf_handle_t *h = hndl;
1070
1071 if (getzoneid() != GLOBAL_ZONEID)
1072 uu_die(gettext("svcprop -z may only be used "
1073 "from the global zone\n"));
1074
1075 if ((zone = scf_value_create(h)) == NULL)
1076 scfdie();
1077
1078 if (scf_value_set_astring(zone, optarg) != SCF_SUCCESS)
1079 scfdie();
1080
1081 if (scf_handle_decorate(h, "zone", zone) !=
1082 SCF_SUCCESS) {
1083 uu_die(gettext("zone '%s': %s\n"),
1084 optarg, scf_strerror(scf_error()));
1085 }
1086
1087 scf_value_destroy(zone);
1088 break;
1089 }
1090
1091 case '?':
1092 switch (optopt) {
1093 case 'p':
1094 usage();
1095
1096 default:
1097 break;
1098 }
1099
1100 /* FALLTHROUGH */
1101
1102 default:
1103 usage();
1104 }
1105 }
1106
1107 if (optind == argc)
1108 usage();
1109
1110 max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
1111 max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1112 max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
1113 if (max_scf_name_length == -1 || max_scf_value_length == -1 ||
1114 max_scf_fmri_length == -1)
1115 scfdie();
1116
1117 if (scf_handle_bind(hndl) == -1)
1118 die(gettext("Could not connect to configuration repository: "
1119 "%s.\n"), scf_strerror(scf_error()));
1120
1121 flags = SCF_WALK_PROPERTY | SCF_WALK_SERVICE | SCF_WALK_EXPLICIT;
1122
1123 if (wait) {
1124 if (uu_list_numnodes(prop_list) > 1)
1125 usage();
1126
1127 if (argc - optind > 1)
1128 usage();
1129
1130 callback = do_wait;
1131
1132 } else {
1133 callback = process_fmri;
1134
1135 flags |= SCF_WALK_MULTIPLE;
1136 }
1137
1138 if ((err = scf_walk_fmri(hndl, argc - optind, argv + optind, flags,
1139 callback, NULL, &return_code, warn)) != 0) {
1140 warn(gettext("failed to iterate over instances: %s\n"),
1141 scf_strerror(err));
1142 return_code = UU_EXIT_FATAL;
1143 }
1144
1145 scf_handle_destroy(hndl);
1146
1147 return (return_code);
1148 }
1149