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