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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * Copyright 2020, Joyent, Inc. All rights reserved.
28 */
29
30 /*
31 * svcadm - request adminstrative actions for service instances
32 */
33
34 #include <locale.h>
35 #include <libintl.h>
36 #include <libscf.h>
37 #include <libscf_priv.h>
38 #include <libcontract.h>
39 #include <libcontract_priv.h>
40 #include <sys/contract/process.h>
41 #include <libuutil.h>
42 #include <stddef.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <fcntl.h>
48 #include <procfs.h>
49 #include <assert.h>
50 #include <errno.h>
51 #include <zone.h>
52
53 #ifndef TEXT_DOMAIN
54 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
55 #endif /* TEXT_DOMAIN */
56
57 /* Must be a power of two */
58 #define HT_BUCKETS 64
59
60 /*
61 * Exit codes for enable and disable -s.
62 */
63 #define EXIT_SVC_FAILURE 3
64 #define EXIT_DEP_FAILURE 4
65
66 #define WALK_FLAGS (SCF_WALK_UNIPARTIAL | SCF_WALK_MULTIPLE)
67
68 /*
69 * How long we will wait (in seconds) for a service to change state
70 * before re-checking its dependencies.
71 */
72 #define WAIT_INTERVAL 3
73
74 #define bad_error(func, err) \
75 uu_panic("%s:%d: %s() failed with unexpected error %d.\n", \
76 __FILE__, __LINE__, (func), (err));
77
78 struct ht_elt {
79 struct ht_elt *next;
80 boolean_t active;
81 char str[1];
82 };
83
84
85 /*
86 * Callback data for enable/disable.
87 */
88 #define SET_ENABLED 0x1
89 #define SET_TEMPORARY 0x2
90 #define SET_RECURSIVE 0x4
91
92 typedef struct {
93 char ed_comment[SCF_COMMENT_MAX_LENGTH];
94 int ed_flags;
95 } enable_data_t;
96
97
98 scf_handle_t *h;
99 ssize_t max_scf_fmri_sz;
100 static const char *emsg_permission_denied;
101 static const char *emsg_nomem;
102 static const char *emsg_create_pg_perm_denied;
103 static const char *emsg_pg_perm_denied;
104 static const char *emsg_prop_perm_denied;
105 static const char *emsg_no_service;
106
107 static int exit_status = 0;
108 static int verbose = 0;
109 static char *scratch_fmri;
110 static char *g_zonename = NULL;
111 static char svcstate[80];
112 static boolean_t svcsearch = B_FALSE;
113
114 static struct ht_elt **visited;
115
116 void do_scfdie(int lineno) __NORETURN;
117 static void usage_milestone(void) __NORETURN;
118 static void set_astring_prop(const char *, const char *, const char *,
119 uint32_t, const char *, const char *);
120 static void pr_warn(const char *format, ...);
121
122 /*
123 * Visitors from synch.c, needed for enable -s and disable -s.
124 */
125 extern int is_enabled(scf_instance_t *);
126 extern int has_potential(scf_instance_t *, int);
127
128 void
129 do_scfdie(int lineno)
130 {
131 scf_error_t err;
132
133 switch (err = scf_error()) {
134 case SCF_ERROR_CONNECTION_BROKEN:
135 uu_die(gettext("Connection to repository server broken. "
136 "Exiting.\n"));
137 /* NOTREACHED */
138
139 case SCF_ERROR_BACKEND_READONLY:
140 uu_die(gettext("Repository is read-only. Exiting.\n"));
141 /* NOTREACHED */
142
143 default:
144 #ifdef NDEBUG
145 uu_die(gettext("Unexpected libscf error: %s. Exiting.\n"),
146 scf_strerror(err));
147 #else
148 uu_die("Unexpected libscf error on line %d: %s.\n", lineno,
149 scf_strerror(err));
150 #endif
151 }
152 }
153
154 #define scfdie() do_scfdie(__LINE__)
155
156 static void
157 usage()
158 {
159 (void) fprintf(stderr, gettext(
160 "Usage: %1$s [-S <state>] [-v] [-Z | -z zone] [cmd [args ... ]]\n\n"
161 "\t%1$s enable [-rst] [<service> ...]\t- enable and online service(s)\n"
162 "\t%1$s disable [-c comment] [-st] [<service> ...] - disable "
163 "service(s)\n"
164 "\t%1$s restart [-d] [<service> ...]\t- restart specified service(s)\n"
165 "\t%1$s refresh [<service> ...]\t\t- re-read service configuration\n"
166 "\t%1$s mark [-It] <state> [<service> ...] - set maintenance state\n"
167 "\t%1$s clear [<service> ...]\t\t- clear maintenance state\n"
168 "\t%1$s milestone [-d] <milestone>\t- advance to a service milestone\n"
169 "\n\t"
170 "Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n"
171 "\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n"
172 "\n"
173 "\t%1$s <cmd> svc:/network/smtp:sendmail\n"
174 "\t%1$s <cmd> network/smtp:sendmail\n"
175 "\t%1$s <cmd> network/*mail\n"
176 "\t%1$s <cmd> network/smtp\n"
177 "\t%1$s <cmd> smtp:sendmail\n"
178 "\t%1$s <cmd> smtp\n"
179 "\t%1$s <cmd> sendmail\n"), uu_getpname());
180
181 exit(UU_EXIT_USAGE);
182 }
183
184
185 /*
186 * FMRI hash table for recursive enable.
187 */
188
189 static uint32_t
190 hash_fmri(const char *str)
191 {
192 uint32_t h = 0, g;
193 const char *p;
194
195 /* Generic hash function from uts/common/os/modhash.c . */
196 for (p = str; *p != '\0'; ++p) {
197 h = (h << 4) + *p;
198 if ((g = (h & 0xf0000000)) != 0) {
199 h ^= (g >> 24);
200 h ^= g;
201 }
202 }
203
204 return (h);
205 }
206
207 /*
208 * Return 1 if str has been visited, 0 if it has not, and -1 if memory could not
209 * be allocated.
210 */
211 static int
212 visited_find_or_add(const char *str, struct ht_elt **hep)
213 {
214 uint32_t h;
215 uint_t i;
216 struct ht_elt *he;
217
218 h = hash_fmri(str);
219 i = h & (HT_BUCKETS - 1);
220
221 for (he = visited[i]; he != NULL; he = he->next) {
222 if (strcmp(he->str, str) == 0) {
223 if (hep)
224 *hep = he;
225 return (1);
226 }
227 }
228
229 he = malloc(offsetof(struct ht_elt, str) + strlen(str) + 1);
230 if (he == NULL)
231 return (-1);
232
233 (void) strcpy(he->str, str);
234
235 he->next = visited[i];
236 visited[i] = he;
237
238 if (hep)
239 *hep = he;
240 return (0);
241 }
242
243
244 /*
245 * Returns 0, ECANCELED if pg is deleted, ENOENT if propname doesn't exist,
246 * EINVAL if the property is not of boolean type or has no values, and E2BIG
247 * if it has more than one value. *bp is set if 0 or E2BIG is returned.
248 */
249 int
250 get_bool_prop(scf_propertygroup_t *pg, const char *propname, uint8_t *bp)
251 {
252 scf_property_t *prop;
253 scf_value_t *val;
254 int ret;
255
256 if ((prop = scf_property_create(h)) == NULL ||
257 (val = scf_value_create(h)) == NULL)
258 scfdie();
259
260 if (scf_pg_get_property(pg, propname, prop) != 0) {
261 switch (scf_error()) {
262 case SCF_ERROR_DELETED:
263 ret = ECANCELED;
264 goto out;
265
266 case SCF_ERROR_NOT_FOUND:
267 ret = ENOENT;
268 goto out;
269
270 case SCF_ERROR_NOT_SET:
271 assert(0);
272 abort();
273 /* NOTREACHED */
274
275 default:
276 scfdie();
277 }
278 }
279
280 if (scf_property_get_value(prop, val) == 0) {
281 ret = 0;
282 } else {
283 switch (scf_error()) {
284 case SCF_ERROR_DELETED:
285 ret = ENOENT;
286 goto out;
287
288 case SCF_ERROR_NOT_FOUND:
289 ret = EINVAL;
290 goto out;
291
292 case SCF_ERROR_CONSTRAINT_VIOLATED:
293 ret = E2BIG;
294 break;
295
296 case SCF_ERROR_NOT_SET:
297 assert(0);
298 abort();
299 /* NOTREACHED */
300
301 default:
302 scfdie();
303 }
304 }
305
306 if (scf_value_get_boolean(val, bp) != 0) {
307 if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
308 scfdie();
309
310 ret = EINVAL;
311 goto out;
312 }
313
314 out:
315 scf_value_destroy(val);
316 scf_property_destroy(prop);
317 return (ret);
318 }
319
320 /*
321 * Returns 0, EPERM, or EROFS.
322 */
323 static int
324 set_bool_prop(scf_propertygroup_t *pg, const char *propname, boolean_t b)
325 {
326 scf_value_t *v;
327 scf_transaction_t *tx;
328 scf_transaction_entry_t *ent;
329 int ret = 0, r;
330
331 if ((tx = scf_transaction_create(h)) == NULL ||
332 (ent = scf_entry_create(h)) == NULL ||
333 (v = scf_value_create(h)) == NULL)
334 scfdie();
335
336 scf_value_set_boolean(v, b);
337
338 for (;;) {
339 if (scf_transaction_start(tx, pg) == -1) {
340 switch (scf_error()) {
341 case SCF_ERROR_PERMISSION_DENIED:
342 ret = EPERM;
343 goto out;
344
345 case SCF_ERROR_BACKEND_READONLY:
346 ret = EROFS;
347 goto out;
348
349 default:
350 scfdie();
351 }
352 }
353
354 if (scf_transaction_property_change_type(tx, ent, propname,
355 SCF_TYPE_BOOLEAN) != 0) {
356 if (scf_error() != SCF_ERROR_NOT_FOUND)
357 scfdie();
358
359 if (scf_transaction_property_new(tx, ent, propname,
360 SCF_TYPE_BOOLEAN) != 0)
361 scfdie();
362 }
363
364 r = scf_entry_add_value(ent, v);
365 assert(r == 0);
366
367 r = scf_transaction_commit(tx);
368 if (r == 1)
369 break;
370
371 scf_transaction_reset(tx);
372
373 if (r != 0) {
374 switch (scf_error()) {
375 case SCF_ERROR_PERMISSION_DENIED:
376 ret = EPERM;
377 goto out;
378
379 case SCF_ERROR_BACKEND_READONLY:
380 ret = EROFS;
381 goto out;
382
383 default:
384 scfdie();
385 }
386 }
387
388 if (scf_pg_update(pg) == -1)
389 scfdie();
390 }
391
392 out:
393 scf_transaction_destroy(tx);
394 scf_entry_destroy(ent);
395 scf_value_destroy(v);
396 return (ret);
397 }
398
399 /*
400 * Gets the single astring value of the propname property of pg. prop & v are
401 * scratch space. Returns the length of the string on success or
402 * -ENOENT - pg has no property named propname
403 * -E2BIG - property has no values or multiple values
404 * -EINVAL - property type is not compatible with astring
405 */
406 ssize_t
407 get_astring_prop(const scf_propertygroup_t *pg, const char *propname,
408 scf_property_t *prop, scf_value_t *v, char *buf, size_t bufsz)
409 {
410 ssize_t sz;
411
412 if (scf_pg_get_property(pg, propname, prop) != 0) {
413 if (scf_error() != SCF_ERROR_NOT_FOUND)
414 scfdie();
415
416 return (-ENOENT);
417 }
418
419 if (scf_property_get_value(prop, v) != 0) {
420 switch (scf_error()) {
421 case SCF_ERROR_NOT_FOUND:
422 case SCF_ERROR_CONSTRAINT_VIOLATED:
423 return (-E2BIG);
424
425 default:
426 scfdie();
427 }
428 }
429
430 sz = scf_value_get_astring(v, buf, bufsz);
431 if (sz < 0) {
432 if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
433 scfdie();
434
435 return (-EINVAL);
436 }
437
438 return (sz);
439 }
440
441 /*
442 * Returns 0 or EPERM.
443 */
444 static int
445 pg_get_or_add(const scf_instance_t *inst, const char *pgname,
446 const char *pgtype, uint32_t pgflags, scf_propertygroup_t *pg)
447 {
448 again:
449 if (scf_instance_get_pg(inst, pgname, pg) == 0)
450 return (0);
451
452 if (scf_error() != SCF_ERROR_NOT_FOUND)
453 scfdie();
454
455 if (scf_instance_add_pg(inst, pgname, pgtype, pgflags, pg) == 0)
456 return (0);
457
458 switch (scf_error()) {
459 case SCF_ERROR_EXISTS:
460 goto again;
461
462 case SCF_ERROR_PERMISSION_DENIED:
463 return (EPERM);
464
465 default:
466 scfdie();
467 /* NOTREACHED */
468 }
469 }
470
471 static int
472 my_ct_name(char *out, size_t len)
473 {
474 ct_stathdl_t st;
475 char *ct_fmri;
476 ctid_t ct;
477 int fd, errno, ret;
478
479 if ((ct = getctid()) == -1)
480 uu_die(gettext("Could not get contract id for process"));
481
482 fd = contract_open(ct, "process", "status", O_RDONLY);
483
484 if ((errno = ct_status_read(fd, CTD_ALL, &st)) != 0)
485 uu_warn(gettext("Could not read status of contract "
486 "%ld: %s.\n"), ct, strerror(errno));
487
488 if ((errno = ct_pr_status_get_svc_fmri(st, &ct_fmri)) != 0)
489 uu_warn(gettext("Could not get svc_fmri for contract "
490 "%ld: %s.\n"), ct, strerror(errno));
491
492 ret = strlcpy(out, ct_fmri, len);
493
494 ct_status_free(st);
495 (void) close(fd);
496
497 return (ret);
498 }
499
500 /*
501 * Set auxiliary_tty and auxiliary_fmri properties in restarter_actions pg to
502 * communicate whether the action is requested from a tty and the fmri of the
503 * responsible process.
504 *
505 * Returns 0, EPERM, or EROFS
506 */
507 static int
508 restarter_setup(const char *fmri, const scf_instance_t *inst)
509 {
510 boolean_t b = B_FALSE;
511 scf_propertygroup_t *pg = NULL;
512 int ret = 0;
513
514 if ((pg = scf_pg_create(h)) == NULL)
515 scfdie();
516
517 if (pg_get_or_add(inst, SCF_PG_RESTARTER_ACTIONS,
518 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
519 pg) == EPERM) {
520 if (!verbose)
521 uu_warn(emsg_permission_denied, fmri);
522 else
523 uu_warn(emsg_create_pg_perm_denied, fmri,
524 SCF_PG_RESTARTER_ACTIONS);
525
526 ret = EPERM;
527 goto out;
528 }
529
530 /* Set auxiliary_tty property */
531 if (isatty(STDIN_FILENO))
532 b = B_TRUE;
533
534 /* Create and set state to disabled */
535 switch (set_bool_prop(pg, SCF_PROPERTY_AUX_TTY, b)) {
536 case 0:
537 break;
538
539 case EPERM:
540 if (!verbose)
541 uu_warn(emsg_permission_denied, fmri);
542 else
543 uu_warn(emsg_prop_perm_denied, fmri,
544 SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_TTY);
545
546 ret = EPERM;
547 goto out;
548 /* NOTREACHED */
549
550 case EROFS:
551 /* Shouldn't happen, but it can. */
552 if (!verbose)
553 uu_warn(gettext("%s: Repository read-only.\n"), fmri);
554 else
555 uu_warn(gettext("%s: Could not set %s/%s "
556 "(repository read-only).\n"), fmri,
557 SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_TTY);
558
559 ret = EROFS;
560 goto out;
561 /* NOTREACHED */
562
563 default:
564 scfdie();
565 }
566
567 if (my_ct_name(scratch_fmri, max_scf_fmri_sz) > 0) {
568 set_astring_prop(fmri, SCF_PG_RESTARTER_ACTIONS,
569 SCF_PG_RESTARTER_ACTIONS_TYPE,
570 SCF_PG_RESTARTER_ACTIONS_FLAGS,
571 SCF_PROPERTY_AUX_FMRI, scratch_fmri);
572 } else {
573 uu_warn(gettext("%s: Could not set %s/%s: "
574 "my_ct_name failed.\n"), fmri,
575 SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_FMRI);
576 }
577
578 out:
579 scf_pg_destroy(pg);
580 return (ret);
581 }
582
583 static int
584 delete_prop(const char *fmri, scf_instance_t *inst, const char *pgname,
585 const char *propname)
586 {
587 int r = scf_instance_delete_prop(inst, pgname, propname);
588
589 switch (r) {
590 case 0:
591 break;
592
593 case ECANCELED:
594 uu_warn(emsg_no_service, fmri);
595 break;
596
597 case EACCES:
598 uu_warn(gettext("Could not delete %s/%s "
599 "property of %s: backend access denied.\n"),
600 pgname, propname, fmri);
601 break;
602
603 case EROFS:
604 uu_warn(gettext("Could not delete %s/%s "
605 "property of %s: backend is read-only.\n"),
606 pgname, propname, fmri);
607 break;
608
609 default:
610 bad_error("scf_instance_delete_prop", r);
611 }
612
613 return (r);
614 }
615
616 /*
617 * Returns 0, EPERM, or EROFS.
618 */
619 static int
620 set_enabled_props(scf_propertygroup_t *pg, enable_data_t *ed)
621 {
622 scf_transaction_entry_t *ent1;
623 scf_transaction_entry_t *ent2;
624 scf_transaction_t *tx;
625 scf_value_t *v2;
626 scf_value_t *v1;
627 int ret = 0, r;
628
629 if ((tx = scf_transaction_create(h)) == NULL ||
630 (ent1 = scf_entry_create(h)) == NULL ||
631 (ent2 = scf_entry_create(h)) == NULL ||
632 (v1 = scf_value_create(h)) == NULL ||
633 (v2 = scf_value_create(h)) == NULL)
634 scfdie();
635
636 for (;;) {
637 if (scf_transaction_start(tx, pg) == -1) {
638 switch (scf_error()) {
639 case SCF_ERROR_PERMISSION_DENIED:
640 ret = EPERM;
641 goto out;
642
643 case SCF_ERROR_BACKEND_READONLY:
644 ret = EROFS;
645 goto out;
646
647 default:
648 scfdie();
649 }
650 }
651
652 if (scf_transaction_property_change_type(tx, ent1,
653 SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN) != 0) {
654 if (scf_error() != SCF_ERROR_NOT_FOUND)
655 scfdie();
656
657 if (scf_transaction_property_new(tx, ent1,
658 SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN) != 0)
659 scfdie();
660 }
661
662 scf_value_set_boolean(v1, !!(ed->ed_flags & SET_ENABLED));
663
664 r = scf_entry_add_value(ent1, v1);
665 assert(r == 0);
666
667 if (scf_transaction_property_change_type(tx, ent2,
668 SCF_PROPERTY_COMMENT, SCF_TYPE_ASTRING) != 0) {
669 if (scf_error() != SCF_ERROR_NOT_FOUND)
670 scfdie();
671
672 if (scf_transaction_property_new(tx, ent2,
673 SCF_PROPERTY_COMMENT, SCF_TYPE_ASTRING) != 0)
674 scfdie();
675 }
676
677 if (scf_value_set_astring(v2, ed->ed_comment) != SCF_SUCCESS)
678 scfdie();
679
680 if (scf_entry_add_value(ent2, v2) != SCF_SUCCESS)
681 scfdie();
682
683 r = scf_transaction_commit(tx);
684 if (r == 1)
685 break;
686
687 scf_transaction_reset(tx);
688
689 if (r != 0) {
690 switch (scf_error()) {
691 case SCF_ERROR_PERMISSION_DENIED:
692 ret = EPERM;
693 goto out;
694
695 case SCF_ERROR_BACKEND_READONLY:
696 ret = EROFS;
697 goto out;
698
699 default:
700 scfdie();
701 }
702 }
703
704 if (scf_pg_update(pg) == -1)
705 scfdie();
706 }
707
708 out:
709 scf_transaction_destroy(tx);
710 scf_entry_destroy(ent1);
711 scf_entry_destroy(ent2);
712 scf_value_destroy(v1);
713 scf_value_destroy(v2);
714 return (ret);
715 }
716
717 /*
718 * Enable or disable an instance. SET_TEMPORARY modifications apply to
719 * general_ovr/ property group.
720 */
721 static void
722 set_inst_enabled(const char *fmri, scf_instance_t *inst, enable_data_t *ed)
723 {
724 scf_propertygroup_t *pg;
725 uint8_t b;
726 const char *pgname = NULL; /* For emsg_pg_perm_denied */
727
728 pg = scf_pg_create(h);
729 if (pg == NULL)
730 scfdie();
731
732 if (restarter_setup(fmri, inst))
733 goto out;
734
735 /*
736 * An instance's configuration is incomplete if general/enabled
737 * doesn't exist. Create both the property group and property
738 * here if they don't exist.
739 */
740 pgname = SCF_PG_GENERAL;
741 if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE,
742 SCF_PG_GENERAL_FLAGS, pg) != 0)
743 goto eperm;
744
745 if (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b) != 0) {
746 /* Create and set state to disabled */
747 switch (set_bool_prop(pg, SCF_PROPERTY_ENABLED, B_FALSE)) {
748 case 0:
749 break;
750
751 case EPERM:
752 goto eperm;
753
754 case EROFS:
755 /* Shouldn't happen, but it can. */
756 if (!verbose)
757 uu_warn(gettext("%s: Repository read-only.\n"),
758 fmri);
759 else
760 uu_warn(gettext("%s: Could not set %s/%s "
761 "(repository read-only).\n"), fmri,
762 SCF_PG_GENERAL, SCF_PROPERTY_ENABLED);
763 goto out;
764
765 default:
766 assert(0);
767 abort();
768 }
769 }
770
771 if (ed->ed_flags & SET_TEMPORARY) {
772 pgname = SCF_PG_GENERAL_OVR;
773 if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_OVR_TYPE,
774 SCF_PG_GENERAL_OVR_FLAGS, pg) != 0)
775 goto eperm;
776
777 switch (set_enabled_props(pg, ed)) {
778 case 0:
779 break;
780
781 case EPERM:
782 goto eperm;
783
784 case EROFS:
785 /* Shouldn't happen, but it can. */
786 if (!verbose)
787 uu_warn(gettext("%s: Repository read-only.\n"),
788 fmri);
789 else
790 uu_warn(gettext("%s: Could not set %s/%s "
791 "(repository read-only).\n"), fmri,
792 SCF_PG_GENERAL_OVR, SCF_PROPERTY_ENABLED);
793 goto out;
794
795 default:
796 assert(0);
797 abort();
798 }
799
800 if (verbose)
801 (void) printf((ed->ed_flags & SET_ENABLED) ?
802 gettext("%s temporarily enabled.\n") :
803 gettext("%s temporarily disabled.\n"), fmri);
804 } else {
805 again:
806 /*
807 * Both pg and property should exist since we created
808 * them earlier. However, there's still a chance that
809 * someone may have deleted the property out from under
810 * us.
811 */
812 if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE,
813 SCF_PG_GENERAL_FLAGS, pg) != 0)
814 goto eperm;
815
816 switch (set_enabled_props(pg, ed)) {
817 case 0:
818 break;
819
820 case EPERM:
821 goto eperm;
822
823 case EROFS:
824 /*
825 * If general/enabled is already set the way we want,
826 * proceed.
827 */
828 switch (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b)) {
829 case 0:
830 if (!(b) == !(ed->ed_flags & SET_ENABLED))
831 break;
832 /* FALLTHROUGH */
833
834 case ENOENT:
835 case EINVAL:
836 case E2BIG:
837 if (!verbose)
838 uu_warn(gettext("%s: Repository "
839 "read-only.\n"), fmri);
840 else
841 uu_warn(gettext("%s: Could not set "
842 "%s/%s (repository read-only).\n"),
843 fmri, SCF_PG_GENERAL,
844 SCF_PROPERTY_ENABLED);
845 goto out;
846
847 case ECANCELED:
848 goto again;
849
850 default:
851 assert(0);
852 abort();
853 }
854 break;
855
856 default:
857 assert(0);
858 abort();
859 }
860
861 switch (delete_prop(fmri, inst, SCF_PG_GENERAL_OVR,
862 SCF_PROPERTY_ENABLED)) {
863 case 0:
864 break;
865
866 case EPERM:
867 goto eperm;
868
869 default:
870 goto out;
871 }
872
873 switch (delete_prop(fmri, inst, SCF_PG_GENERAL_OVR,
874 SCF_PROPERTY_COMMENT)) {
875 case 0:
876 break;
877
878 case EPERM:
879 goto eperm;
880
881 default:
882 goto out;
883 }
884
885 if (verbose) {
886 (void) printf((ed->ed_flags & SET_ENABLED) ?
887 gettext("%s enabled.\n") :
888 gettext("%s disabled.\n"), fmri);
889 }
890 }
891
892 scf_pg_destroy(pg);
893 return;
894
895 eperm:
896 assert(pgname != NULL);
897 if (!verbose)
898 uu_warn(emsg_permission_denied, fmri);
899 else
900 uu_warn(emsg_pg_perm_denied, fmri, pgname);
901
902 out:
903 scf_pg_destroy(pg);
904 exit_status = 1;
905 }
906
907 /*
908 * Set inst to the instance which corresponds to fmri. If fmri identifies
909 * a service with a single instance, get that instance.
910 *
911 * Fails with
912 * ENOTSUP - fmri has an unsupported scheme
913 * EINVAL - fmri is invalid
914 * ENOTDIR - fmri does not identify a service or instance
915 * ENOENT - could not locate instance
916 * E2BIG - fmri is a service with multiple instances (warning not printed)
917 */
918 static int
919 get_inst_mult(const char *fmri, scf_instance_t *inst)
920 {
921 char *cfmri;
922 const char *svc_name, *inst_name, *pg_name;
923 scf_service_t *svc;
924 scf_instance_t *inst2;
925 scf_iter_t *iter;
926 int ret;
927
928 if (strncmp(fmri, "lrc:", sizeof ("lrc:") - 1) == 0) {
929 uu_warn(gettext("FMRI \"%s\" is a legacy service.\n"), fmri);
930 exit_status = 1;
931 return (ENOTSUP);
932 }
933
934 cfmri = strdup(fmri);
935 if (cfmri == NULL)
936 uu_die(emsg_nomem);
937
938 if (scf_parse_svc_fmri(cfmri, NULL, &svc_name, &inst_name, &pg_name,
939 NULL) != SCF_SUCCESS) {
940 free(cfmri);
941 uu_warn(gettext("FMRI \"%s\" is invalid.\n"), fmri);
942 exit_status = 1;
943 return (EINVAL);
944 }
945
946 free(cfmri);
947
948 if (svc_name == NULL || pg_name != NULL) {
949 uu_warn(gettext(
950 "FMRI \"%s\" does not designate a service or instance.\n"),
951 fmri);
952 exit_status = 1;
953 return (ENOTDIR);
954 }
955
956 if (inst_name != NULL) {
957 if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
958 NULL, SCF_DECODE_FMRI_EXACT) == 0)
959 return (0);
960
961 if (scf_error() != SCF_ERROR_NOT_FOUND)
962 scfdie();
963
964 uu_warn(gettext("No such instance \"%s\".\n"), fmri);
965 exit_status = 1;
966
967 return (ENOENT);
968 }
969
970 if ((svc = scf_service_create(h)) == NULL ||
971 (inst2 = scf_instance_create(h)) == NULL ||
972 (iter = scf_iter_create(h)) == NULL)
973 scfdie();
974
975 if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL,
976 SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
977 if (scf_error() != SCF_ERROR_NOT_FOUND)
978 scfdie();
979
980 uu_warn(emsg_no_service, fmri);
981 exit_status = 1;
982
983 ret = ENOENT;
984 goto out;
985 }
986
987 /* If the service has only one child, use it. */
988 if (scf_iter_service_instances(iter, svc) != SCF_SUCCESS)
989 scfdie();
990
991 ret = scf_iter_next_instance(iter, inst);
992 if (ret < 0)
993 scfdie();
994 if (ret != 1) {
995 uu_warn(gettext("Service \"%s\" has no instances.\n"),
996 fmri);
997 exit_status = 1;
998 ret = ENOENT;
999 goto out;
1000 }
1001
1002 ret = scf_iter_next_instance(iter, inst2);
1003 if (ret < 0)
1004 scfdie();
1005
1006 if (ret != 0) {
1007 ret = E2BIG;
1008 goto out;
1009 }
1010
1011 ret = 0;
1012
1013 out:
1014 scf_iter_destroy(iter);
1015 scf_instance_destroy(inst2);
1016 scf_service_destroy(svc);
1017 return (ret);
1018 }
1019
1020 /*
1021 * Same as get_inst_mult(), but on E2BIG prints a warning and returns ENOENT.
1022 */
1023 static int
1024 get_inst(const char *fmri, scf_instance_t *inst)
1025 {
1026 int r;
1027
1028 r = get_inst_mult(fmri, inst);
1029 if (r != E2BIG)
1030 return (r);
1031
1032 uu_warn(gettext("operation on service %s is ambiguous; "
1033 "instance specification needed.\n"), fmri);
1034 return (ENOENT);
1035 }
1036
1037 static char *
1038 inst_get_fmri(const scf_instance_t *inst)
1039 {
1040 ssize_t sz;
1041
1042 sz = scf_instance_to_fmri(inst, scratch_fmri, max_scf_fmri_sz);
1043 if (sz < 0)
1044 scfdie();
1045 if (sz >= max_scf_fmri_sz)
1046 uu_die(gettext("scf_instance_to_fmri() returned unexpectedly "
1047 "long value.\n"));
1048
1049 return (scratch_fmri);
1050 }
1051
1052 static ssize_t
1053 dep_get_astring(const char *fmri, const char *pgname,
1054 const scf_propertygroup_t *pg, const char *propname, scf_property_t *prop,
1055 scf_value_t *v, char *buf, size_t bufsz)
1056 {
1057 ssize_t sz;
1058
1059 sz = get_astring_prop(pg, propname, prop, v, buf, bufsz);
1060 if (sz >= 0)
1061 return (sz);
1062
1063 switch (-sz) {
1064 case ENOENT:
1065 uu_warn(gettext("\"%s\" is misconfigured (\"%s\" dependency "
1066 "lacks \"%s\" property.)\n"), fmri, pgname, propname);
1067 return (-1);
1068
1069 case E2BIG:
1070 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" property "
1071 "is not single-valued.)\n"), fmri, pgname, propname);
1072 return (-1);
1073
1074 case EINVAL:
1075 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" property "
1076 "is not of astring type.)\n"), fmri, pgname, propname);
1077 return (-1);
1078
1079 default:
1080 assert(0);
1081 abort();
1082 /* NOTREACHED */
1083 }
1084 }
1085
1086 static boolean_t
1087 multiple_instances(scf_iter_t *iter, scf_value_t *v, char *buf)
1088 {
1089 int count = 0, r;
1090 boolean_t ret;
1091 scf_instance_t *inst;
1092
1093 inst = scf_instance_create(h);
1094 if (inst == NULL)
1095 scfdie();
1096
1097 for (;;) {
1098 r = scf_iter_next_value(iter, v);
1099 if (r == 0) {
1100 ret = B_FALSE;
1101 goto out;
1102 }
1103 if (r != 1)
1104 scfdie();
1105
1106 if (scf_value_get_astring(v, buf, max_scf_fmri_sz) < 0)
1107 scfdie();
1108
1109 switch (get_inst_mult(buf, inst)) {
1110 case 0:
1111 ++count;
1112 if (count > 1) {
1113 ret = B_TRUE;
1114 goto out;
1115 }
1116 break;
1117
1118 case ENOTSUP:
1119 case EINVAL:
1120 case ENOTDIR:
1121 case ENOENT:
1122 continue;
1123
1124 case E2BIG:
1125 ret = B_TRUE;
1126 goto out;
1127
1128 default:
1129 assert(0);
1130 abort();
1131 }
1132 }
1133
1134 out:
1135 scf_instance_destroy(inst);
1136 return (ret);
1137 }
1138
1139 /*
1140 * Enable the service or instance identified by fmri and its dependencies,
1141 * recursively. Specifically, call get_inst(fmri), enable the result, and
1142 * recurse on its restarter and the dependencies. To avoid duplication of
1143 * effort or looping around a dependency cycle, each FMRI is entered into the
1144 * "visited" hash table. While recursing, the hash table entry is marked
1145 * "active", so that if we come upon it again, we know we've hit a cycle.
1146 * exclude_all and optional_all dependencies are ignored. require_any
1147 * dependencies are followed only if they comprise a single service; otherwise
1148 * the user is warned.
1149 *
1150 * fmri must point to a writable max_scf_fmri_sz buffer. Returns EINVAL if fmri
1151 * is invalid, E2BIG if fmri identifies a service with multiple instances, ELOOP
1152 * on cycle detection, or 0 on success.
1153 */
1154 static int
1155 enable_fmri_rec(char *fmri, enable_data_t *ed)
1156 {
1157 scf_instance_t *inst;
1158 scf_snapshot_t *snap;
1159 scf_propertygroup_t *pg;
1160 scf_property_t *prop;
1161 scf_value_t *v;
1162 scf_iter_t *pg_iter, *val_iter;
1163 scf_type_t ty;
1164 char *buf, *pgname;
1165 ssize_t name_sz, len, sz;
1166 int ret;
1167 struct ht_elt *he;
1168
1169 len = scf_canonify_fmri(fmri, fmri, max_scf_fmri_sz);
1170 if (len < 0) {
1171 assert(scf_error() == SCF_ERROR_INVALID_ARGUMENT);
1172 return (EINVAL);
1173 }
1174 assert(len < max_scf_fmri_sz);
1175
1176 switch (visited_find_or_add(fmri, &he)) {
1177 case 0:
1178 he->active = B_TRUE;
1179 break;
1180
1181 case 1:
1182 return (he->active ? ELOOP : 0);
1183
1184 case -1:
1185 uu_die(emsg_nomem);
1186
1187 default:
1188 assert(0);
1189 abort();
1190 }
1191
1192 inst = scf_instance_create(h);
1193 if (inst == NULL)
1194 scfdie();
1195
1196 switch (get_inst_mult(fmri, inst)) {
1197 case 0:
1198 break;
1199
1200 case E2BIG:
1201 he->active = B_FALSE;
1202 return (E2BIG);
1203
1204 default:
1205 he->active = B_FALSE;
1206 return (0);
1207 }
1208
1209 set_inst_enabled(fmri, inst, ed);
1210
1211 if ((snap = scf_snapshot_create(h)) == NULL ||
1212 (pg = scf_pg_create(h)) == NULL ||
1213 (prop = scf_property_create(h)) == NULL ||
1214 (v = scf_value_create(h)) == NULL ||
1215 (pg_iter = scf_iter_create(h)) == NULL ||
1216 (val_iter = scf_iter_create(h)) == NULL)
1217 scfdie();
1218
1219 buf = malloc(max_scf_fmri_sz);
1220 if (buf == NULL)
1221 uu_die(emsg_nomem);
1222
1223 name_sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
1224 if (name_sz < 0)
1225 scfdie();
1226 ++name_sz;
1227 pgname = malloc(name_sz);
1228 if (pgname == NULL)
1229 uu_die(emsg_nomem);
1230
1231 if (scf_instance_get_snapshot(inst, "running", snap) != 0) {
1232 if (scf_error() != SCF_ERROR_NOT_FOUND)
1233 scfdie();
1234
1235 scf_snapshot_destroy(snap);
1236 snap = NULL;
1237 }
1238
1239 /* Enable restarter */
1240 if (scf_instance_get_pg_composed(inst, snap, SCF_PG_GENERAL, pg) != 0) {
1241 if (scf_error() != SCF_ERROR_NOT_FOUND)
1242 scfdie();
1243
1244 uu_warn(gettext("\"%s\" is misconfigured (lacks \"%s\" "
1245 "property group).\n"), fmri, SCF_PG_GENERAL);
1246 ret = 0;
1247 goto out;
1248 }
1249
1250 sz = get_astring_prop(pg, SCF_PROPERTY_RESTARTER, prop, v, buf,
1251 max_scf_fmri_sz);
1252 if (sz > max_scf_fmri_sz) {
1253 uu_warn(gettext("\"%s\" is misconfigured (the value of "
1254 "\"%s/%s\" is too long).\n"), fmri, SCF_PG_GENERAL,
1255 SCF_PROPERTY_RESTARTER);
1256 ret = 0;
1257 goto out;
1258 } else if (sz >= 0) {
1259 switch (enable_fmri_rec(buf, ed)) {
1260 case 0:
1261 break;
1262
1263 case EINVAL:
1264 uu_warn(gettext("Restarter FMRI for \"%s\" is "
1265 "invalid.\n"), fmri);
1266 break;
1267
1268 case E2BIG:
1269 uu_warn(gettext("Restarter FMRI for \"%s\" identifies "
1270 "a service with multiple instances.\n"), fmri);
1271 break;
1272
1273 case ELOOP:
1274 ret = ELOOP;
1275 goto out;
1276
1277 default:
1278 assert(0);
1279 abort();
1280 }
1281 } else if (sz < 0) {
1282 switch (-sz) {
1283 case ENOENT:
1284 break;
1285
1286 case E2BIG:
1287 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" "
1288 "property is not single-valued).\n"), fmri,
1289 SCF_PG_GENERAL, SCF_PROPERTY_RESTARTER);
1290 ret = 0;
1291 goto out;
1292
1293 case EINVAL:
1294 uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" "
1295 "property is not of astring type).\n"), fmri,
1296 SCF_PG_GENERAL, SCF_PROPERTY_RESTARTER);
1297 ret = 0;
1298 goto out;
1299
1300 default:
1301 assert(0);
1302 abort();
1303 }
1304 }
1305
1306 if (scf_iter_instance_pgs_typed_composed(pg_iter, inst, snap,
1307 SCF_GROUP_DEPENDENCY) == -1)
1308 scfdie();
1309
1310 while (scf_iter_next_pg(pg_iter, pg) > 0) {
1311 len = scf_pg_get_name(pg, pgname, name_sz);
1312 if (len < 0)
1313 scfdie();
1314 assert(len < name_sz);
1315
1316 if (dep_get_astring(fmri, pgname, pg, SCF_PROPERTY_TYPE, prop,
1317 v, buf, max_scf_fmri_sz) < 0)
1318 continue;
1319
1320 if (strcmp(buf, "service") != 0)
1321 continue;
1322
1323 if (dep_get_astring(fmri, pgname, pg, SCF_PROPERTY_GROUPING,
1324 prop, v, buf, max_scf_fmri_sz) < 0)
1325 continue;
1326
1327 if (strcmp(buf, SCF_DEP_EXCLUDE_ALL) == 0 ||
1328 strcmp(buf, SCF_DEP_OPTIONAL_ALL) == 0)
1329 continue;
1330
1331 if (strcmp(buf, SCF_DEP_REQUIRE_ALL) != 0 &&
1332 strcmp(buf, SCF_DEP_REQUIRE_ANY) != 0) {
1333 uu_warn(gettext("Dependency \"%s\" of \"%s\" has "
1334 "unknown type \"%s\".\n"), pgname, fmri, buf);
1335 continue;
1336 }
1337
1338 if (scf_pg_get_property(pg, SCF_PROPERTY_ENTITIES, prop) ==
1339 -1) {
1340 if (scf_error() != SCF_ERROR_NOT_FOUND)
1341 scfdie();
1342
1343 uu_warn(gettext("\"%s\" is misconfigured (\"%s\" "
1344 "dependency lacks \"%s\" property.)\n"), fmri,
1345 pgname, SCF_PROPERTY_ENTITIES);
1346 continue;
1347 }
1348
1349 if (scf_property_type(prop, &ty) != SCF_SUCCESS)
1350 scfdie();
1351
1352 if (ty != SCF_TYPE_FMRI) {
1353 uu_warn(gettext("\"%s\" is misconfigured (property "
1354 "\"%s/%s\" is not of fmri type).\n"), fmri, pgname,
1355 SCF_PROPERTY_ENTITIES);
1356 continue;
1357 }
1358
1359 if (scf_iter_property_values(val_iter, prop) == -1)
1360 scfdie();
1361
1362 if (strcmp(buf, SCF_DEP_REQUIRE_ANY) == 0) {
1363 if (multiple_instances(val_iter, v, buf)) {
1364 (void) printf(gettext("%s requires one of:\n"),
1365 fmri);
1366
1367 if (scf_iter_property_values(val_iter, prop) !=
1368 0)
1369 scfdie();
1370
1371 for (;;) {
1372 int r;
1373
1374 r = scf_iter_next_value(val_iter, v);
1375 if (r == 0)
1376 break;
1377 if (r != 1)
1378 scfdie();
1379
1380 if (scf_value_get_astring(v, buf,
1381 max_scf_fmri_sz) < 0)
1382 scfdie();
1383
1384 (void) fputs(" ", stdout);
1385 (void) puts(buf);
1386 }
1387
1388 continue;
1389 }
1390
1391 /*
1392 * Since there's only one instance, we can enable it.
1393 * Reset val_iter and continue.
1394 */
1395 if (scf_iter_property_values(val_iter, prop) != 0)
1396 scfdie();
1397 }
1398
1399 for (;;) {
1400 ret = scf_iter_next_value(val_iter, v);
1401 if (ret == 0)
1402 break;
1403 if (ret != 1)
1404 scfdie();
1405
1406 if (scf_value_get_astring(v, buf, max_scf_fmri_sz) ==
1407 -1)
1408 scfdie();
1409
1410 switch (enable_fmri_rec(buf, ed)) {
1411 case 0:
1412 break;
1413
1414 case EINVAL:
1415 uu_warn(gettext("\"%s\" dependency of \"%s\" "
1416 "has invalid FMRI \"%s\".\n"), pgname,
1417 fmri, buf);
1418 break;
1419
1420 case E2BIG:
1421 uu_warn(gettext("%s depends on %s, which has "
1422 "multiple instances.\n"), fmri, buf);
1423 break;
1424
1425 case ELOOP:
1426 ret = ELOOP;
1427 goto out;
1428
1429 default:
1430 assert(0);
1431 abort();
1432 }
1433 }
1434 }
1435
1436 ret = 0;
1437
1438 out:
1439 he->active = B_FALSE;
1440
1441 free(buf);
1442 free(pgname);
1443
1444 (void) scf_value_destroy(v);
1445 scf_property_destroy(prop);
1446 scf_pg_destroy(pg);
1447 scf_snapshot_destroy(snap);
1448 scf_iter_destroy(pg_iter);
1449 scf_iter_destroy(val_iter);
1450
1451 return (ret);
1452 }
1453
1454 /*
1455 * fmri here is only used for verbose messages.
1456 */
1457 static void
1458 set_inst_action(const char *fmri, const scf_instance_t *inst,
1459 const char *action)
1460 {
1461 scf_transaction_t *tx;
1462 scf_transaction_entry_t *ent;
1463 scf_propertygroup_t *pg;
1464 scf_property_t *prop;
1465 scf_value_t *v;
1466 int ret;
1467 int64_t t;
1468 hrtime_t timestamp;
1469
1470 const char * const scf_pg_restarter_actions = SCF_PG_RESTARTER_ACTIONS;
1471
1472 if ((pg = scf_pg_create(h)) == NULL ||
1473 (prop = scf_property_create(h)) == NULL ||
1474 (v = scf_value_create(h)) == NULL ||
1475 (tx = scf_transaction_create(h)) == NULL ||
1476 (ent = scf_entry_create(h)) == NULL)
1477 scfdie();
1478
1479 if (restarter_setup(fmri, inst)) {
1480 exit_status = 1;
1481 goto out;
1482 }
1483
1484 if (scf_instance_get_pg(inst, scf_pg_restarter_actions, pg) == -1) {
1485 if (scf_error() != SCF_ERROR_NOT_FOUND)
1486 scfdie();
1487
1488 /* Try creating the restarter_actions property group. */
1489 if (scf_instance_add_pg(inst, scf_pg_restarter_actions,
1490 SCF_PG_RESTARTER_ACTIONS_TYPE,
1491 SCF_PG_RESTARTER_ACTIONS_FLAGS, pg) == -1) {
1492 switch (scf_error()) {
1493 case SCF_ERROR_EXISTS:
1494 /* Someone must have added it. */
1495 break;
1496
1497 case SCF_ERROR_PERMISSION_DENIED:
1498 if (!verbose)
1499 uu_warn(emsg_permission_denied, fmri);
1500 else
1501 uu_warn(emsg_create_pg_perm_denied,
1502 fmri, scf_pg_restarter_actions);
1503 goto out;
1504
1505 default:
1506 scfdie();
1507 }
1508 }
1509 }
1510
1511 /*
1512 * If we lose the transaction race and need to retry, there are 2
1513 * potential other winners:
1514 * - another process setting actions
1515 * - the restarter marking the action complete
1516 * Therefore, re-read the property every time through the loop before
1517 * making any decisions based on their values.
1518 */
1519 do {
1520 timestamp = gethrtime();
1521
1522 if (scf_transaction_start(tx, pg) == -1) {
1523 if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1524 scfdie();
1525
1526 if (!verbose)
1527 uu_warn(emsg_permission_denied, fmri);
1528 else
1529 uu_warn(emsg_pg_perm_denied, fmri,
1530 scf_pg_restarter_actions);
1531 goto out;
1532 }
1533
1534 if (scf_pg_get_property(pg, action, prop) == -1) {
1535 if (scf_error() != SCF_ERROR_NOT_FOUND)
1536 scfdie();
1537 if (scf_transaction_property_new(tx, ent,
1538 action, SCF_TYPE_INTEGER) == -1)
1539 scfdie();
1540 goto action_set;
1541 } else {
1542 if (scf_transaction_property_change_type(tx, ent,
1543 action, SCF_TYPE_INTEGER) == -1)
1544 scfdie();
1545 }
1546
1547 if (scf_property_get_value(prop, v) == -1) {
1548 switch (scf_error()) {
1549 case SCF_ERROR_CONSTRAINT_VIOLATED:
1550 case SCF_ERROR_NOT_FOUND:
1551 /* Misconfigured, so set anyway. */
1552 goto action_set;
1553
1554 default:
1555 scfdie();
1556 }
1557 } else {
1558 if (scf_value_get_integer(v, &t) == -1) {
1559 assert(scf_error() == SCF_ERROR_TYPE_MISMATCH);
1560 goto action_set;
1561 }
1562 if (t > timestamp)
1563 break;
1564 }
1565
1566 action_set:
1567 scf_value_set_integer(v, timestamp);
1568 if (scf_entry_add_value(ent, v) == -1)
1569 scfdie();
1570
1571 ret = scf_transaction_commit(tx);
1572 if (ret == -1) {
1573 if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1574 scfdie();
1575
1576 if (!verbose)
1577 uu_warn(emsg_permission_denied, fmri);
1578 else
1579 uu_warn(emsg_prop_perm_denied, fmri,
1580 scf_pg_restarter_actions, action);
1581 scf_transaction_reset(tx);
1582 goto out;
1583 }
1584
1585 scf_transaction_reset(tx);
1586
1587 if (ret == 0) {
1588 if (scf_pg_update(pg) == -1)
1589 scfdie();
1590 }
1591 } while (ret == 0);
1592
1593 if (verbose)
1594 (void) printf(gettext("Action %s set for %s.\n"), action, fmri);
1595
1596 out:
1597 scf_value_destroy(v);
1598 scf_entry_destroy(ent);
1599 scf_transaction_destroy(tx);
1600 scf_property_destroy(prop);
1601 scf_pg_destroy(pg);
1602 }
1603
1604 /*
1605 * Get the state of inst. state should point to a buffer of
1606 * MAX_SCF_STATE_STRING_SZ bytes. Returns 0 on success or -1 if
1607 * no restarter property group
1608 * no state property
1609 * state property is misconfigured (wrong type, not single-valued)
1610 * state value is too long
1611 * In these cases, fmri is used to print a warning.
1612 *
1613 * If pgp is non-NULL, a successful call to inst_get_state will store
1614 * the SCF_PG_RESTARTER property group in *pgp, and the caller will be
1615 * responsible for calling scf_pg_destroy on the property group.
1616 */
1617 int
1618 inst_get_state(scf_instance_t *inst, char *state, const char *fmri,
1619 scf_propertygroup_t **pgp)
1620 {
1621 scf_propertygroup_t *pg;
1622 scf_property_t *prop;
1623 scf_value_t *val;
1624 int ret = -1;
1625 ssize_t szret;
1626
1627 if ((pg = scf_pg_create(h)) == NULL ||
1628 (prop = scf_property_create(h)) == NULL ||
1629 (val = scf_value_create(h)) == NULL)
1630 scfdie();
1631
1632 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) != SCF_SUCCESS) {
1633 if (scf_error() != SCF_ERROR_NOT_FOUND)
1634 scfdie();
1635
1636 uu_warn(gettext("%s is misconfigured (lacks \"%s\" property "
1637 "group).\n"), fmri ? fmri : inst_get_fmri(inst),
1638 SCF_PG_RESTARTER);
1639 goto out;
1640 }
1641
1642 szret = get_astring_prop(pg, SCF_PROPERTY_STATE, prop, val, state,
1643 MAX_SCF_STATE_STRING_SZ);
1644 if (szret < 0) {
1645 switch (-szret) {
1646 case ENOENT:
1647 uu_warn(gettext("%s is misconfigured (\"%s\" property "
1648 "group lacks \"%s\" property).\n"),
1649 fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER,
1650 SCF_PROPERTY_STATE);
1651 goto out;
1652
1653 case E2BIG:
1654 uu_warn(gettext("%s is misconfigured (\"%s/%s\" "
1655 "property is not single-valued).\n"),
1656 fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER,
1657 SCF_PROPERTY_STATE);
1658 goto out;
1659
1660 case EINVAL:
1661 uu_warn(gettext("%s is misconfigured (\"%s/%s\" "
1662 "property is not of type astring).\n"),
1663 fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER,
1664 SCF_PROPERTY_STATE);
1665 goto out;
1666
1667 default:
1668 assert(0);
1669 abort();
1670 }
1671 }
1672 if (szret >= MAX_SCF_STATE_STRING_SZ) {
1673 uu_warn(gettext("%s is misconfigured (\"%s/%s\" property value "
1674 "is too long).\n"), fmri ? fmri : inst_get_fmri(inst),
1675 SCF_PG_RESTARTER, SCF_PROPERTY_STATE);
1676 goto out;
1677 }
1678
1679 ret = 0;
1680 if (pgp)
1681 *pgp = pg;
1682
1683 out:
1684 (void) scf_value_destroy(val);
1685 scf_property_destroy(prop);
1686 if (ret || pgp == NULL)
1687 scf_pg_destroy(pg);
1688 return (ret);
1689 }
1690
1691 static void
1692 set_astring_prop(const char *fmri, const char *pgname, const char *pgtype,
1693 uint32_t pgflags, const char *propname, const char *str)
1694 {
1695 scf_instance_t *inst;
1696 scf_propertygroup_t *pg;
1697 scf_property_t *prop;
1698 scf_value_t *val;
1699 scf_transaction_t *tx;
1700 scf_transaction_entry_t *txent;
1701 int ret;
1702
1703 inst = scf_instance_create(h);
1704 if (inst == NULL)
1705 scfdie();
1706
1707 if (get_inst(fmri, inst) != 0)
1708 return;
1709
1710 if ((pg = scf_pg_create(h)) == NULL ||
1711 (prop = scf_property_create(h)) == NULL ||
1712 (val = scf_value_create(h)) == NULL ||
1713 (tx = scf_transaction_create(h)) == NULL ||
1714 (txent = scf_entry_create(h)) == NULL)
1715 scfdie();
1716
1717 if (scf_instance_get_pg(inst, pgname, pg) != SCF_SUCCESS) {
1718 if (scf_error() != SCF_ERROR_NOT_FOUND)
1719 scfdie();
1720
1721 if (scf_instance_add_pg(inst, pgname, pgtype, pgflags, pg) !=
1722 SCF_SUCCESS) {
1723 switch (scf_error()) {
1724 case SCF_ERROR_EXISTS:
1725 if (scf_instance_get_pg(inst, pgname, pg) !=
1726 SCF_SUCCESS) {
1727 if (scf_error() != SCF_ERROR_NOT_FOUND)
1728 scfdie();
1729
1730 uu_warn(gettext("Repository write "
1731 "contention.\n"));
1732 goto out;
1733 }
1734 break;
1735
1736 case SCF_ERROR_PERMISSION_DENIED:
1737 if (!verbose)
1738 uu_warn(emsg_permission_denied, fmri);
1739 else
1740 uu_warn(emsg_create_pg_perm_denied,
1741 fmri, pgname);
1742 goto out;
1743
1744 default:
1745 scfdie();
1746 }
1747 }
1748 }
1749
1750 do {
1751 if (scf_transaction_start(tx, pg) != SCF_SUCCESS) {
1752 if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1753 scfdie();
1754
1755 if (!verbose)
1756 uu_warn(emsg_permission_denied, fmri);
1757 else
1758 uu_warn(emsg_pg_perm_denied, fmri, pgname);
1759 goto out;
1760 }
1761
1762 if (scf_transaction_property_change_type(tx, txent, propname,
1763 SCF_TYPE_ASTRING) != 0) {
1764 if (scf_error() != SCF_ERROR_NOT_FOUND)
1765 scfdie();
1766
1767 if (scf_transaction_property_new(tx, txent, propname,
1768 SCF_TYPE_ASTRING) != 0)
1769 scfdie();
1770 }
1771
1772 if (scf_value_set_astring(val, str) != SCF_SUCCESS)
1773 scfdie();
1774
1775 if (scf_entry_add_value(txent, val) != SCF_SUCCESS)
1776 scfdie();
1777
1778 ret = scf_transaction_commit(tx);
1779 if (ret == -1) {
1780 if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
1781 scfdie();
1782
1783 if (!verbose)
1784 uu_warn(emsg_permission_denied, fmri);
1785 else
1786 uu_warn(emsg_prop_perm_denied, fmri, pgname,
1787 propname);
1788 goto out;
1789 }
1790
1791 if (ret == 0) {
1792 scf_transaction_reset(tx);
1793
1794 if (scf_pg_update(pg) == -1)
1795 scfdie();
1796 }
1797 } while (ret == 0);
1798
1799 out:
1800 scf_transaction_destroy(tx);
1801 scf_entry_destroy(txent);
1802 scf_value_destroy(val);
1803 scf_property_destroy(prop);
1804 scf_pg_destroy(pg);
1805 scf_instance_destroy(inst);
1806 }
1807
1808
1809 static int
1810 set_fmri_enabled(void *data, scf_walkinfo_t *wip)
1811 {
1812 enable_data_t *ed = data;
1813
1814 assert(wip->inst != NULL);
1815 assert(wip->pg == NULL);
1816
1817 if (svcsearch) {
1818 char state[MAX_SCF_STATE_STRING_SZ];
1819
1820 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
1821 return (0);
1822 if (strcmp(state, svcstate) != 0)
1823 return (0);
1824 }
1825
1826 if (ed->ed_flags & SET_RECURSIVE) {
1827 char *fmri_buf = malloc(max_scf_fmri_sz);
1828 if (fmri_buf == NULL)
1829 uu_die(emsg_nomem);
1830
1831 visited = calloc(HT_BUCKETS, sizeof (*visited));
1832 if (visited == NULL)
1833 uu_die(emsg_nomem);
1834
1835 /* scf_walk_fmri() guarantees that fmri isn't too long */
1836 assert(strlen(wip->fmri) <= max_scf_fmri_sz);
1837 (void) strlcpy(fmri_buf, wip->fmri, max_scf_fmri_sz);
1838
1839 switch (enable_fmri_rec(fmri_buf, ed)) {
1840 case E2BIG:
1841 uu_warn(gettext("operation on service %s is ambiguous; "
1842 "instance specification needed.\n"), fmri_buf);
1843 break;
1844
1845 case ELOOP:
1846 uu_warn(gettext("%s: Dependency cycle detected.\n"),
1847 fmri_buf);
1848 }
1849
1850 free(visited);
1851 free(fmri_buf);
1852
1853 } else {
1854 set_inst_enabled(wip->fmri, wip->inst, ed);
1855 }
1856
1857 return (0);
1858 }
1859
1860 /* ARGSUSED */
1861 static int
1862 wait_fmri_enabled(void *data, scf_walkinfo_t *wip)
1863 {
1864 scf_propertygroup_t *pg = NULL;
1865 char state[MAX_SCF_STATE_STRING_SZ];
1866
1867 assert(wip->inst != NULL);
1868 assert(wip->pg == NULL);
1869
1870 do {
1871 if (pg)
1872 scf_pg_destroy(pg);
1873 if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) {
1874 exit_status = EXIT_SVC_FAILURE;
1875 return (0);
1876 }
1877
1878 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0 ||
1879 strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) {
1880 /*
1881 * We're done.
1882 */
1883 goto out;
1884 }
1885
1886 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) {
1887 /*
1888 * The service is ill.
1889 */
1890 uu_warn(gettext("Instance \"%s\" is in maintenance"
1891 " state.\n"), wip->fmri);
1892 exit_status = EXIT_SVC_FAILURE;
1893 goto out;
1894 }
1895
1896 if (!is_enabled(wip->inst)) {
1897 /*
1898 * Someone stepped in and disabled the service.
1899 */
1900 uu_warn(gettext("Instance \"%s\" has been disabled"
1901 " by another entity.\n"), wip->fmri);
1902 exit_status = EXIT_SVC_FAILURE;
1903 goto out;
1904 }
1905
1906 if (!has_potential(wip->inst, B_FALSE)) {
1907 /*
1908 * Our dependencies aren't met. We'll never
1909 * amount to anything.
1910 */
1911 uu_warn(gettext("Instance \"%s\" has unsatisfied"
1912 " dependencies.\n"), wip->fmri);
1913 /*
1914 * EXIT_SVC_FAILURE takes precedence over
1915 * EXIT_DEP_FAILURE
1916 */
1917 if (exit_status == 0)
1918 exit_status = EXIT_DEP_FAILURE;
1919 goto out;
1920 }
1921 } while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0);
1922 scfdie();
1923 /* NOTREACHED */
1924
1925 out:
1926 scf_pg_destroy(pg);
1927 return (0);
1928 }
1929
1930 /* ARGSUSED */
1931 static int
1932 wait_fmri_disabled(void *data, scf_walkinfo_t *wip)
1933 {
1934 scf_propertygroup_t *pg = NULL;
1935 char state[MAX_SCF_STATE_STRING_SZ];
1936
1937 assert(wip->inst != NULL);
1938 assert(wip->pg == NULL);
1939
1940 do {
1941 if (pg)
1942 scf_pg_destroy(pg);
1943 if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) {
1944 exit_status = EXIT_SVC_FAILURE;
1945 return (0);
1946 }
1947
1948 if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0) {
1949 /*
1950 * We're done.
1951 */
1952 goto out;
1953 }
1954
1955 if (is_enabled(wip->inst)) {
1956 /*
1957 * Someone stepped in and enabled the service.
1958 */
1959 uu_warn(gettext("Instance \"%s\" has been enabled"
1960 " by another entity.\n"), wip->fmri);
1961 exit_status = EXIT_SVC_FAILURE;
1962 goto out;
1963 }
1964
1965 if (!has_potential(wip->inst, B_TRUE)) {
1966 /*
1967 * Our restarter is hopeless.
1968 */
1969 uu_warn(gettext("Restarter for instance \"%s\" is"
1970 " unavailable.\n"), wip->fmri);
1971 /*
1972 * EXIT_SVC_FAILURE takes precedence over
1973 * EXIT_DEP_FAILURE
1974 */
1975 if (exit_status == 0)
1976 exit_status = EXIT_DEP_FAILURE;
1977 goto out;
1978 }
1979
1980 } while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0);
1981 scfdie();
1982 /* NOTREACHED */
1983
1984 out:
1985 scf_pg_destroy(pg);
1986 return (0);
1987 }
1988
1989 /* ARGSUSED */
1990 static int
1991 clear_instance(void *data, scf_walkinfo_t *wip)
1992 {
1993 char state[MAX_SCF_STATE_STRING_SZ];
1994
1995 assert(wip->inst != NULL);
1996 assert(wip->pg == NULL);
1997
1998 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
1999 return (0);
2000
2001 if (svcsearch && strcmp(state, svcstate) != 0)
2002 return (0);
2003
2004 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) {
2005 set_inst_action(wip->fmri, wip->inst, SCF_PROPERTY_MAINT_OFF);
2006 } else if (strcmp(state, SCF_STATE_STRING_DEGRADED) ==
2007 0) {
2008 set_inst_action(wip->fmri, wip->inst, SCF_PROPERTY_RESTORE);
2009 } else {
2010 uu_warn(gettext("Instance \"%s\" is not in a "
2011 "maintenance or degraded state.\n"), wip->fmri);
2012
2013 exit_status = 1;
2014 }
2015
2016 return (0);
2017 }
2018
2019 static int
2020 set_fmri_action(void *action, scf_walkinfo_t *wip)
2021 {
2022 assert(wip->inst != NULL && wip->pg == NULL);
2023
2024 if (svcsearch) {
2025 char state[MAX_SCF_STATE_STRING_SZ];
2026
2027 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
2028 return (0);
2029 if (strcmp(state, svcstate) != 0)
2030 return (0);
2031 }
2032
2033 set_inst_action(wip->fmri, wip->inst, action);
2034
2035 return (0);
2036 }
2037
2038 /*
2039 * Flags to control 'mark' action.
2040 */
2041 #define MARK_IMMEDIATE 0x1
2042 #define MARK_TEMPORARY 0x2
2043
2044 static int
2045 force_degraded(void *data, scf_walkinfo_t *wip)
2046 {
2047 int flags = (int)data;
2048 char state[MAX_SCF_STATE_STRING_SZ];
2049
2050 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0) {
2051 exit_status = 1;
2052 return (0);
2053 }
2054
2055 if (svcsearch && strcmp(state, svcstate) != 0)
2056 return (0);
2057
2058 if (strcmp(state, SCF_STATE_STRING_ONLINE) != 0) {
2059 uu_warn(gettext("Instance \"%s\" is not online.\n"), wip->fmri);
2060 exit_status = 1;
2061 return (0);
2062 }
2063
2064 set_inst_action(wip->fmri, wip->inst, (flags & MARK_IMMEDIATE) ?
2065 SCF_PROPERTY_DEGRADE_IMMEDIATE : SCF_PROPERTY_DEGRADED);
2066
2067 return (0);
2068 }
2069
2070 static int
2071 force_maintenance(void *data, scf_walkinfo_t *wip)
2072 {
2073 int flags = (int)data;
2074 const char *prop;
2075
2076 if (svcsearch) {
2077 char state[MAX_SCF_STATE_STRING_SZ];
2078
2079 if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
2080 return (0);
2081 if (strcmp(state, svcstate) != 0)
2082 return (0);
2083 }
2084
2085 if (flags & MARK_IMMEDIATE) {
2086 prop = (flags & MARK_TEMPORARY) ?
2087 SCF_PROPERTY_MAINT_ON_IMMTEMP :
2088 SCF_PROPERTY_MAINT_ON_IMMEDIATE;
2089 } else {
2090 prop = (flags & MARK_TEMPORARY) ?
2091 SCF_PROPERTY_MAINT_ON_TEMPORARY :
2092 SCF_PROPERTY_MAINT_ON;
2093 }
2094
2095 set_inst_action(wip->fmri, wip->inst, prop);
2096
2097 return (0);
2098 }
2099
2100 static void
2101 set_milestone(const char *fmri, boolean_t temporary)
2102 {
2103 scf_instance_t *inst;
2104 scf_propertygroup_t *pg;
2105
2106 if (temporary) {
2107 set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS_OVR,
2108 SCF_PG_OPTIONS_OVR_TYPE, SCF_PG_OPTIONS_OVR_FLAGS,
2109 SCF_PROPERTY_MILESTONE, fmri);
2110 return;
2111 }
2112
2113 if ((inst = scf_instance_create(h)) == NULL ||
2114 (pg = scf_pg_create(h)) == NULL)
2115 scfdie();
2116
2117 if (get_inst(SCF_SERVICE_STARTD, inst) != 0) {
2118 scf_instance_destroy(inst);
2119 return;
2120 }
2121
2122 /*
2123 * Set the persistent milestone before deleting the override so we don't
2124 * glitch.
2125 */
2126 set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS,
2127 SCF_PG_OPTIONS_TYPE, SCF_PG_OPTIONS_FLAGS, SCF_PROPERTY_MILESTONE,
2128 fmri);
2129
2130 if (delete_prop(SCF_SERVICE_STARTD, inst, SCF_PG_OPTIONS_OVR,
2131 SCF_PROPERTY_MILESTONE) != 0)
2132 exit_status = 1;
2133
2134 scf_pg_destroy(pg);
2135 scf_instance_destroy(inst);
2136 }
2137
2138 static char const *milestones[] = {
2139 SCF_MILESTONE_SINGLE_USER,
2140 SCF_MILESTONE_MULTI_USER,
2141 SCF_MILESTONE_MULTI_USER_SERVER,
2142 NULL
2143 };
2144
2145 static void
2146 usage_milestone(void)
2147 {
2148 const char **ms;
2149
2150 (void) fprintf(stderr, gettext(
2151 "Usage: svcadm milestone [-d] <milestone>\n\n"
2152 "\t-d\tmake the specified milestone the default for system boot\n\n"
2153 "\tMilestones can be specified using an FMRI or abbreviation.\n"
2154 "\tThe major milestones are as follows:\n\n"
2155 "\tall\n"
2156 "\tnone\n"));
2157
2158 for (ms = milestones; *ms != NULL; ms++)
2159 (void) fprintf(stderr, "\t%s\n", *ms);
2160
2161 exit(UU_EXIT_USAGE);
2162 }
2163
2164 static const char *
2165 validate_milestone(const char *milestone)
2166 {
2167 const char **ms;
2168 const char *tmp;
2169 size_t len;
2170
2171 if (strcmp(milestone, "all") == 0)
2172 return (milestone);
2173
2174 if (strcmp(milestone, "none") == 0)
2175 return (milestone);
2176
2177 /*
2178 * Determine if this is a full or partial milestone
2179 */
2180 for (ms = milestones; *ms != NULL; ms++) {
2181 if ((tmp = strstr(*ms, milestone)) != NULL) {
2182 len = strlen(milestone);
2183
2184 /*
2185 * The beginning of the string must align with the start
2186 * of a milestone fmri, or on the boundary between
2187 * elements. The end of the string must align with the
2188 * end of the milestone, or at the instance boundary.
2189 */
2190 if ((tmp == *ms || tmp[-1] == '/') &&
2191 (tmp[len] == '\0' || tmp[len] == ':'))
2192 return (*ms);
2193 }
2194 }
2195
2196 (void) fprintf(stderr,
2197 gettext("\"%s\" is not a valid major milestone.\n"), milestone);
2198
2199 usage_milestone();
2200 /* NOTREACHED */
2201 }
2202
2203 /*PRINTFLIKE1*/
2204 static void
2205 pr_warn(const char *format, ...)
2206 {
2207 const char *pname = uu_getpname();
2208 va_list alist;
2209
2210 va_start(alist, format);
2211
2212 if (pname != NULL)
2213 (void) fprintf(stderr, "%s", pname);
2214
2215 if (g_zonename != NULL)
2216 (void) fprintf(stderr, " (%s)", g_zonename);
2217
2218 (void) fprintf(stderr, ": ");
2219
2220 (void) vfprintf(stderr, format, alist);
2221
2222 if (strrchr(format, '\n') == NULL)
2223 (void) fprintf(stderr, ": %s\n", strerror(errno));
2224
2225 va_end(alist);
2226 }
2227
2228 /*ARGSUSED*/
2229 static void
2230 quiet(const char *fmt, ...)
2231 {
2232 /* Do nothing */
2233 }
2234
2235 int
2236 main(int argc, char *argv[])
2237 {
2238 int o;
2239 int err;
2240 int sw_back;
2241 boolean_t do_zones = B_FALSE;
2242 boolean_t do_a_zone = B_FALSE;
2243 char zonename[ZONENAME_MAX];
2244 uint_t nzents = 0, zent = 0;
2245 zoneid_t *zids = NULL;
2246 int orig_optind, orig_argc;
2247 char **orig_argv;
2248
2249 (void) setlocale(LC_ALL, "");
2250 (void) textdomain(TEXT_DOMAIN);
2251
2252 (void) uu_setpname(argv[0]);
2253
2254 if (argc < 2)
2255 usage();
2256
2257 max_scf_fmri_sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
2258 if (max_scf_fmri_sz < 0)
2259 scfdie();
2260 ++max_scf_fmri_sz;
2261
2262 scratch_fmri = malloc(max_scf_fmri_sz);
2263 if (scratch_fmri == NULL)
2264 uu_die(emsg_nomem);
2265
2266 while ((o = getopt(argc, argv, "S:vZz:")) != -1) {
2267 switch (o) {
2268 case 'S':
2269 (void) strlcpy(svcstate, optarg, sizeof (svcstate));
2270 svcsearch = B_TRUE;
2271 break;
2272
2273 case 'v':
2274 verbose = 1;
2275 break;
2276
2277 case 'z':
2278 if (getzoneid() != GLOBAL_ZONEID)
2279 uu_die(gettext("svcadm -z may only be used "
2280 "from the global zone\n"));
2281 if (do_zones)
2282 usage();
2283
2284 (void) strlcpy(zonename, optarg, sizeof (zonename));
2285 do_a_zone = B_TRUE;
2286 break;
2287
2288 case 'Z':
2289 if (getzoneid() != GLOBAL_ZONEID)
2290 uu_die(gettext("svcadm -Z may only be used "
2291 "from the global zone\n"));
2292 if (do_a_zone)
2293 usage();
2294
2295 do_zones = B_TRUE;
2296 break;
2297
2298 default:
2299 usage();
2300 }
2301 }
2302
2303 while (do_zones) {
2304 uint_t found;
2305
2306 if (zone_list(NULL, &nzents) != 0)
2307 uu_die(gettext("could not get number of zones"));
2308
2309 if ((zids = malloc(nzents * sizeof (zoneid_t))) == NULL) {
2310 uu_die(gettext("could not allocate array for "
2311 "%d zone IDs"), nzents);
2312 }
2313
2314 found = nzents;
2315
2316 if (zone_list(zids, &found) != 0)
2317 uu_die(gettext("could not get zone list"));
2318
2319 /*
2320 * If the number of zones has not changed between our calls to
2321 * zone_list(), we're done -- otherwise, we must free our array
2322 * of zone IDs and take another lap.
2323 */
2324 if (found == nzents)
2325 break;
2326
2327 free(zids);
2328 }
2329
2330 emsg_permission_denied = gettext("%s: Permission denied.\n");
2331 emsg_nomem = gettext("Out of memory.\n");
2332 emsg_create_pg_perm_denied = gettext("%s: Couldn't create \"%s\" "
2333 "property group (permission denied).\n");
2334 emsg_pg_perm_denied = gettext("%s: Couldn't modify \"%s\" property "
2335 "group (permission denied).\n");
2336 emsg_prop_perm_denied = gettext("%s: Couldn't modify \"%s/%s\" "
2337 "property (permission denied).\n");
2338 emsg_no_service = gettext("No such service \"%s\".\n");
2339
2340 orig_optind = optind;
2341 orig_argc = argc;
2342 orig_argv = argv;
2343
2344 again:
2345 h = scf_handle_create(SCF_VERSION);
2346 if (h == NULL)
2347 scfdie();
2348
2349 if (do_zones) {
2350 zone_status_t status;
2351
2352 if (zone_getattr(zids[zent], ZONE_ATTR_STATUS, &status,
2353 sizeof (status)) < 0 || status != ZONE_IS_RUNNING) {
2354 /*
2355 * If this zone is not running or we cannot
2356 * get its status, we do not want to attempt
2357 * to bind an SCF handle to it, lest we
2358 * accidentally interfere with a zone that
2359 * is not yet running by looking up a door
2360 * to its svc.configd (which could potentially
2361 * block a mount with an EBUSY).
2362 */
2363 zent++;
2364 goto nextzone;
2365 }
2366
2367 if (getzonenamebyid(zids[zent++], zonename,
2368 sizeof (zonename)) < 0) {
2369 uu_warn(gettext("could not get name for "
2370 "zone %d; ignoring"), zids[zent - 1]);
2371 goto nextzone;
2372 }
2373
2374 g_zonename = zonename;
2375 }
2376
2377 if (do_a_zone || do_zones) {
2378 scf_value_t *zone;
2379
2380 if ((zone = scf_value_create(h)) == NULL)
2381 scfdie();
2382
2383 if (scf_value_set_astring(zone, zonename) != SCF_SUCCESS)
2384 scfdie();
2385
2386 if (scf_handle_decorate(h, "zone", zone) != SCF_SUCCESS) {
2387 if (do_a_zone) {
2388 uu_die(gettext("invalid zone '%s'\n"), optarg);
2389 } else {
2390 scf_value_destroy(zone);
2391 goto nextzone;
2392 }
2393 }
2394
2395 scf_value_destroy(zone);
2396 }
2397
2398 if (scf_handle_bind(h) == -1) {
2399 if (do_zones)
2400 goto nextzone;
2401
2402 uu_die(gettext("Couldn't bind to configuration repository: "
2403 "%s.\n"), scf_strerror(scf_error()));
2404 }
2405
2406 optind = orig_optind;
2407 argc = orig_argc;
2408 argv = orig_argv;
2409
2410 if (optind >= argc)
2411 usage();
2412
2413 if (strcmp(argv[optind], "enable") == 0) {
2414 enable_data_t ed = {
2415 .ed_flags = SET_ENABLED,
2416 .ed_comment = ""
2417 };
2418 int wait = 0;
2419 int error = 0;
2420
2421 ++optind;
2422
2423 while ((o = getopt(argc, argv, "rst")) != -1) {
2424 if (o == 'r')
2425 ed.ed_flags |= SET_RECURSIVE;
2426 else if (o == 't')
2427 ed.ed_flags |= SET_TEMPORARY;
2428 else if (o == 's')
2429 wait = 1;
2430 else if (o == '?')
2431 usage();
2432 else {
2433 assert(0);
2434 abort();
2435 }
2436 }
2437 argc -= optind;
2438 argv += optind;
2439
2440 if (argc == 0 && !svcsearch)
2441 usage();
2442
2443 if (argc > 0 && svcsearch)
2444 usage();
2445
2446 /*
2447 * We want to continue with -s processing if we had
2448 * invalid options, but not if an enable failed. We
2449 * squelch output the second time we walk fmris; we saw
2450 * the errors the first time.
2451 */
2452 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2453 set_fmri_enabled, &ed, &error, pr_warn)) != 0) {
2454
2455 pr_warn(gettext("failed to iterate over "
2456 "instances: %s\n"), scf_strerror(err));
2457 exit_status = UU_EXIT_FATAL;
2458
2459 } else if (wait && exit_status == 0 &&
2460 (err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2461 wait_fmri_enabled, NULL, &error, quiet)) != 0) {
2462
2463 pr_warn(gettext("failed to iterate over "
2464 "instances: %s\n"), scf_strerror(err));
2465 exit_status = UU_EXIT_FATAL;
2466 }
2467
2468 if (error > 0)
2469 exit_status = error;
2470
2471 } else if (strcmp(argv[optind], "disable") == 0) {
2472 enable_data_t ed = {
2473 .ed_flags = 0,
2474 .ed_comment = ""
2475 };
2476 int wait = 0;
2477 int error = 0;
2478
2479 ++optind;
2480
2481 while ((o = getopt(argc, argv, "c:st")) != -1) {
2482 if (o == 'c') {
2483 if (strlcpy(ed.ed_comment, optarg,
2484 sizeof (ed.ed_comment)) >=
2485 sizeof (ed.ed_comment)) {
2486 uu_die(gettext("disable -c comment "
2487 "too long.\n"));
2488 }
2489 } else if (o == 't')
2490 ed.ed_flags |= SET_TEMPORARY;
2491 else if (o == 's')
2492 wait = 1;
2493 else if (o == '?')
2494 usage();
2495 else {
2496 assert(0);
2497 abort();
2498 }
2499 }
2500 argc -= optind;
2501 argv += optind;
2502
2503 if (argc == 0 && !svcsearch)
2504 usage();
2505
2506 if (argc > 0 && svcsearch)
2507 usage();
2508
2509 /*
2510 * We want to continue with -s processing if we had
2511 * invalid options, but not if a disable failed. We
2512 * squelch output the second time we walk fmris; we saw
2513 * the errors the first time.
2514 */
2515 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2516 set_fmri_enabled, &ed, &exit_status,
2517 pr_warn)) != 0) {
2518
2519 pr_warn(gettext("failed to iterate over "
2520 "instances: %s\n"), scf_strerror(err));
2521 exit_status = UU_EXIT_FATAL;
2522
2523 } else if (wait && exit_status == 0 &&
2524 (err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2525 wait_fmri_disabled, NULL, &error, quiet)) != 0) {
2526
2527 pr_warn(gettext("failed to iterate over "
2528 "instances: %s\n"), scf_strerror(err));
2529 exit_status = UU_EXIT_FATAL;
2530 }
2531
2532 if (error > 0)
2533 exit_status = error;
2534
2535 } else if (strcmp(argv[optind], "restart") == 0) {
2536 boolean_t do_dump = B_FALSE;
2537
2538 ++optind;
2539
2540 while ((o = getopt(argc, argv, "d")) != -1) {
2541 if (o == 'd')
2542 do_dump = B_TRUE;
2543 else if (o == '?')
2544 usage();
2545 else {
2546 assert(0);
2547 abort();
2548 }
2549 }
2550 argc -= optind;
2551 argv += optind;
2552
2553 if (argc == 0 && !svcsearch)
2554 usage();
2555
2556 if (argc > 0 && svcsearch)
2557 usage();
2558
2559 if (do_dump) {
2560 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2561 set_fmri_action, (void *)SCF_PROPERTY_DODUMP,
2562 &exit_status, pr_warn)) != 0) {
2563 pr_warn(gettext("failed to iterate over "
2564 "instances: %s\n"), scf_strerror(err));
2565 exit_status = UU_EXIT_FATAL;
2566 }
2567 }
2568
2569 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2570 set_fmri_action, (void *)SCF_PROPERTY_RESTART, &exit_status,
2571 pr_warn)) != 0) {
2572 pr_warn(gettext("failed to iterate over "
2573 "instances: %s\n"), scf_strerror(err));
2574 exit_status = UU_EXIT_FATAL;
2575 }
2576
2577 } else if (strcmp(argv[optind], "refresh") == 0) {
2578 ++optind;
2579 argc -= optind;
2580 argv += optind;
2581
2582 if (argc == 0 && !svcsearch)
2583 usage();
2584
2585 if (argc > 0 && svcsearch)
2586 usage();
2587
2588 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2589 set_fmri_action, (void *)SCF_PROPERTY_REFRESH, &exit_status,
2590 pr_warn)) != 0) {
2591 pr_warn(gettext("failed to iterate over "
2592 "instances: %s\n"), scf_strerror(scf_error()));
2593 exit_status = UU_EXIT_FATAL;
2594 }
2595
2596 } else if (strcmp(argv[optind], "mark") == 0) {
2597 int flags = 0;
2598 scf_walk_callback callback;
2599
2600 ++optind;
2601
2602 while ((o = getopt(argc, argv, "It")) != -1) {
2603 if (o == 'I')
2604 flags |= MARK_IMMEDIATE;
2605 else if (o == 't')
2606 flags |= MARK_TEMPORARY;
2607 else if (o == '?')
2608 usage();
2609 else {
2610 assert(0);
2611 abort();
2612 }
2613 }
2614
2615 if (argc - optind < 2)
2616 usage();
2617
2618 if (strcmp(argv[optind], "degraded") == 0) {
2619 if (flags & MARK_TEMPORARY)
2620 uu_xdie(UU_EXIT_USAGE, gettext("-t may not be "
2621 "used with degraded.\n"));
2622 callback = force_degraded;
2623
2624 } else if (strcmp(argv[optind], "maintenance") == 0) {
2625 callback = force_maintenance;
2626 } else {
2627 usage();
2628 }
2629
2630 optind++;
2631 argc -= optind;
2632 argv += optind;
2633
2634 if (argc == 0 && !svcsearch)
2635 usage();
2636
2637 if (argc > 0 && svcsearch)
2638 usage();
2639
2640 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS, callback,
2641 NULL, &exit_status, pr_warn)) != 0) {
2642 pr_warn(gettext("failed to iterate over "
2643 "instances: %s\n"),
2644 scf_strerror(err));
2645 exit_status = UU_EXIT_FATAL;
2646 }
2647
2648 } else if (strcmp(argv[optind], "clear") == 0) {
2649 ++optind;
2650 argc -= optind;
2651 argv += optind;
2652
2653 if (argc == 0 && !svcsearch)
2654 usage();
2655
2656 if (svcsearch) {
2657 if (argc > 0)
2658 usage();
2659 if (strcmp(svcstate, SCF_STATE_STRING_MAINT) != 0 &&
2660 strcmp(svcstate, SCF_STATE_STRING_DEGRADED) != 0)
2661 uu_die(gettext("State must be '%s' or '%s'\n"),
2662 SCF_STATE_STRING_MAINT,
2663 SCF_STATE_STRING_DEGRADED);
2664 }
2665
2666 if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
2667 clear_instance, NULL, &exit_status, pr_warn)) != 0) {
2668 pr_warn(gettext("failed to iterate over "
2669 "instances: %s\n"), scf_strerror(err));
2670 exit_status = UU_EXIT_FATAL;
2671 }
2672
2673 } else if (strcmp(argv[optind], "milestone") == 0) {
2674 boolean_t temporary = B_TRUE;
2675 const char *milestone;
2676
2677 ++optind;
2678
2679 while ((o = getopt(argc, argv, "d")) != -1) {
2680 if (o == 'd')
2681 temporary = B_FALSE;
2682 else if (o == '?')
2683 usage_milestone();
2684 else {
2685 assert(0);
2686 abort();
2687 }
2688 }
2689
2690 if (optind >= argc)
2691 usage_milestone();
2692
2693 milestone = validate_milestone(argv[optind]);
2694
2695 set_milestone(milestone, temporary);
2696 } else if (strcmp(argv[optind], "_smf_backup") == 0) {
2697 const char *reason = NULL;
2698
2699 ++optind;
2700
2701 if (optind != argc - 1)
2702 usage();
2703
2704 if ((err = _scf_request_backup(h, argv[optind])) !=
2705 SCF_SUCCESS) {
2706 switch (scf_error()) {
2707 case SCF_ERROR_NOT_BOUND:
2708 case SCF_ERROR_CONNECTION_BROKEN:
2709 case SCF_ERROR_BACKEND_READONLY:
2710 scfdie();
2711 break;
2712
2713 case SCF_ERROR_PERMISSION_DENIED:
2714 case SCF_ERROR_INVALID_ARGUMENT:
2715 reason = scf_strerror(scf_error());
2716 break;
2717
2718 case SCF_ERROR_INTERNAL:
2719 reason =
2720 "unknown error (see console for details)";
2721 break;
2722 }
2723
2724 pr_warn("failed to backup repository: %s\n", reason);
2725 exit_status = UU_EXIT_FATAL;
2726 }
2727 } else if (strcmp(argv[optind], "_smf_repository_switch") == 0) {
2728 const char *reason = NULL;
2729
2730 ++optind;
2731
2732 /*
2733 * Check argument and setup scf_switch structure
2734 */
2735 if (optind != argc - 1)
2736 exit(1);
2737
2738 if (strcmp(argv[optind], "fast") == 0) {
2739 sw_back = 0;
2740 } else if (strcmp(argv[optind], "perm") == 0) {
2741 sw_back = 1;
2742 } else {
2743 exit(UU_EXIT_USAGE);
2744 }
2745
2746 /*
2747 * Call into switch primitive
2748 */
2749 if ((err = _scf_repository_switch(h, sw_back)) !=
2750 SCF_SUCCESS) {
2751 /*
2752 * Retrieve per thread SCF error code
2753 */
2754 switch (scf_error()) {
2755 case SCF_ERROR_NOT_BOUND:
2756 abort();
2757 /* NOTREACHED */
2758
2759 case SCF_ERROR_CONNECTION_BROKEN:
2760 case SCF_ERROR_BACKEND_READONLY:
2761 scfdie();
2762 /* NOTREACHED */
2763
2764 case SCF_ERROR_PERMISSION_DENIED:
2765 case SCF_ERROR_INVALID_ARGUMENT:
2766 reason = scf_strerror(scf_error());
2767 break;
2768
2769 case SCF_ERROR_INTERNAL:
2770 reason = "File operation error: (see console)";
2771 break;
2772
2773 default:
2774 abort();
2775 /* NOTREACHED */
2776 }
2777
2778 pr_warn("failed to switch repository: %s\n", reason);
2779 exit_status = UU_EXIT_FATAL;
2780 }
2781 } else {
2782 usage();
2783 }
2784
2785 if (scf_handle_unbind(h) == -1)
2786 scfdie();
2787 nextzone:
2788 scf_handle_destroy(h);
2789 if (do_zones && zent < nzents)
2790 goto again;
2791
2792 return (exit_status);
2793 }