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