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