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