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