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 /*
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
25 * Copyright 2024 Oxide Computer Company
26 */
27
28 #include <libintl.h>
29 #include <librestart.h>
30 #include <librestart_priv.h>
31 #include <libscf.h>
32 #include <libscf_priv.h>
33
34 #include <assert.h>
35 #include <ctype.h>
36 #include <dlfcn.h>
37 #include <errno.h>
38 #include <exec_attr.h>
39 #include <grp.h>
40 #include <libsysevent.h>
41 #include <libuutil.h>
42 #include <limits.h>
43 #include <link.h>
44 #include <malloc.h>
45 #include <pool.h>
46 #include <priv.h>
47 #include <project.h>
48 #include <pthread.h>
49 #include <pwd.h>
50 #include <secdb.h>
51 #include <signal.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <syslog.h>
55 #include <sys/corectl.h>
56 #include <sys/machelf.h>
57 #include <sys/secflags.h>
58 #include <sys/task.h>
59 #include <sys/types.h>
60 #include <time.h>
61 #include <unistd.h>
62 #include <ucontext.h>
63
64 #define min(a, b) ((a) > (b) ? (b) : (a))
65
66 #define MKW_TRUE ":true"
67 #define MKW_KILL ":kill"
68 #define MKW_KILL_PROC ":kill_process"
69
70 #define ALLOCFAIL ((char *)"Allocation failure.")
71 #define RCBROKEN ((char *)"Repository connection broken.")
72
73 #define MAX_COMMIT_RETRIES 10
74 #define MAX_COMMIT_RETRY_INT (5 * 1000000) /* 5 seconds */
75 #define INITIAL_COMMIT_RETRY_INT (10000) /* 1/100th second */
76
77 /*
78 * bad_fail() catches bugs in this and lower layers by reporting supposedly
79 * impossible function failures. The NDEBUG case keeps the strings out of the
80 * library but still calls abort() so we can root-cause from the coredump.
81 */
82 #ifndef NDEBUG
83 #define bad_fail(func, err) { \
84 (void) fprintf(stderr, \
85 "At %s:%d, %s() failed with unexpected error %d. Aborting.\n", \
86 __FILE__, __LINE__, (func), (err)); \
87 abort(); \
88 }
89 #else
90 #define bad_fail(func, err) abort()
91 #endif
92
93 struct restarter_event_handle {
94 char *reh_restarter_name;
95 char *reh_delegate_channel_name;
96 evchan_t *reh_delegate_channel;
97 char *reh_delegate_subscriber_id;
98 char *reh_master_channel_name;
99 evchan_t *reh_master_channel;
100 char *reh_master_subscriber_id;
101 int (*reh_handler)(restarter_event_t *);
102 };
103
104 struct restarter_event {
105 sysevent_t *re_sysevent;
106 restarter_event_type_t re_type;
107 char *re_instance_name;
108 restarter_event_handle_t *re_event_handle;
109 restarter_instance_state_t re_state;
110 restarter_instance_state_t re_next_state;
111 };
112
113 /*
114 * Long reasons must all parse/read correctly in the following contexts:
115 *
116 * "A service instance transitioned state: %s."
117 * "A service failed: %s."
118 * "Reason: %s."
119 * "The service transitioned state (%s) and ..."
120 *
121 * With the exception of restart_str_none they must also fit the following
122 * moulds:
123 *
124 * "An instance transitioned because %s, and ..."
125 * "An instance transitioned to <new-state> because %s, and ..."
126 *
127 * Note that whoever is rendering the long message must provide the
128 * terminal punctuation - don't include it here. Similarly, do not
129 * provide an initial capital letter in reason-long.
130 *
131 * The long reason strings are Volatile - within the grammatical constraints
132 * above we may improve them as need be. The intention is that a consumer
133 * may blindly render the string along the lines of the above examples,
134 * but has no other guarantees as to the exact wording. Long reasons
135 * are localized.
136 *
137 * We define revisions of the set of short reason strings in use. Within
138 * a given revision, all short reasons are Committed. Consumers must check
139 * the revision in use before relying on the semantics of the short reason
140 * codes - if the version exceeds that which they are familiar with they should
141 * fail gracefully. Having checked for version compatability, a consumer
142 * is assured that
143 *
144 * "short_reason_A iff semantic_A", provided:
145 *
146 * . the restarter uses this short reason code at all,
147 * . the short reason is not "none" (which a restarter could
148 * specifiy for any transition semantics)
149 *
150 * To split/refine such a Committed semantic_A into further cases,
151 * we are required to bump the revision number. This should be an
152 * infrequent occurence. If you bump the revision number you may
153 * need to make corresponding changes in any source that calls
154 * restarter_str_version (e.g., FMA event generation).
155 *
156 * To add additional reasons to the set you must also bump the version
157 * number.
158 */
159
160 /*
161 * The following describes revision 0 of the set of transition reasons.
162 * Read the preceding block comment before making any changes.
163 */
164 static const struct restarter_state_transition_reason restarter_str[] = {
165 /*
166 * Any transition for which the restarter has not provided a reason.
167 */
168 {
169 restarter_str_none,
170 "none",
171 "the restarter gave no reason"
172 },
173
174 /*
175 * A transition to maintenance state due to a
176 * 'svcadm mark maintenance <fmri>'. *Not* used if the libscf
177 * interface smf_maintain_instance(3SCF) is used to request maintenance.
178 */
179 {
180 restarter_str_administrative_request,
181 "administrative_request",
182 "maintenance was requested by an administrator"
183 },
184
185 /*
186 * A transition to maintenance state if a repository inconsistency
187 * exists when the service/instance state is first read by startd
188 * into the graph engine (this can also happen during startd restart).
189 */
190 {
191 restarter_str_bad_repo_state,
192 "bad_repo_state",
193 "an SMF repository inconsistecy exists"
194 },
195
196 /*
197 * A transition 'maintenance -> uninitialized' resulting always
198 * from 'svcadm clear <fmri>'. *Not* used if the libscf interface
199 * smf_restore_instance(3SCF) is used.
200 */
201 {
202 restarter_str_clear_request,
203 "clear_request",
204 "maintenance clear was requested by an administrator"
205 },
206
207 /*
208 * A transition 'online -> offline' due to a process core dump.
209 */
210 {
211 restarter_str_ct_ev_core,
212 "ct_ev_core",
213 "a process dumped core"
214 },
215
216 /*
217 * A transition 'online -> offline' due to an empty process contract,
218 * i.e., the last process in a contract type service has exited.
219 */
220 {
221 restarter_str_ct_ev_exit,
222 "ct_ev_exit",
223 "all processes in the service have exited"
224 },
225
226 /*
227 * A transition 'online -> offline' due to a hardware error.
228 */
229 {
230 restarter_str_ct_ev_hwerr,
231 "ct_ev_hwerr",
232 "a process was killed due to uncorrectable hardware error"
233 },
234
235 /*
236 * A transition 'online -> offline' due to a process in the service
237 * having received a fatal signal originating from outside the
238 * service process contract.
239 */
240 {
241 restarter_str_ct_ev_signal,
242 "ct_ev_signal",
243 "a process received a fatal signal from outside the service"
244 },
245
246 /*
247 * A transition 'offline -> online' when all dependencies for the
248 * service have been met.
249 */
250 {
251 restarter_str_dependencies_satisfied,
252 "dependencies_satisfied",
253 "all dependencies have been satisfied"
254 },
255
256 /*
257 * A transition 'online -> offline' because some dependency for the
258 * service is no-longer met.
259 */
260 {
261 restarter_str_dependency_activity,
262 "dependency_activity",
263 "a dependency activity required a stop"
264 },
265
266 /*
267 * A transition to maintenance state due to a cycle in the
268 * service dependencies.
269 */
270 {
271 restarter_str_dependency_cycle,
272 "dependency_cycle",
273 "a dependency cycle exists"
274 },
275
276 /*
277 * A transition 'online -> offline -> disabled' due to a
278 * 'svcadm disable [-t] <fmri>' or smf_disable_instance(3SCF) call.
279 */
280 {
281 restarter_str_disable_request,
282 "disable_request",
283 "a disable was requested"
284 },
285
286 /*
287 * A transition 'disabled -> offline' due to a
288 * 'svcadm enable [-t] <fmri>' or smf_enable_instance(3SCF) call.
289 */
290 {
291 restarter_str_enable_request,
292 "enable_request",
293 "an enable was requested"
294 },
295
296 /*
297 * A transition to maintenance state when a method fails
298 * repeatedly for a retryable reason.
299 */
300 {
301 restarter_str_fault_threshold_reached,
302 "fault_threshold_reached",
303 "a method is failing in a retryable manner but too often"
304 },
305
306 /*
307 * A transition to uninitialized state when startd reads the service
308 * configuration and inserts it into the graph engine.
309 */
310 {
311 restarter_str_insert_in_graph,
312 "insert_in_graph",
313 "the instance was inserted in the graph"
314 },
315
316 /*
317 * A transition to maintenance state due to an invalid dependency
318 * declared for the service.
319 */
320 {
321 restarter_str_invalid_dependency,
322 "invalid_dependency",
323 "a service has an invalid dependency"
324 },
325
326 /*
327 * A transition to maintenance state because the service-declared
328 * restarter is invalid.
329 */
330 {
331 restarter_str_invalid_restarter,
332 "invalid_restarter",
333 "the service restarter is invalid"
334 },
335
336 /*
337 * A transition to maintenance state because a restarter method
338 * exited with one of SMF_EXIT_ERR_CONFIG, SMF_EXIT_ERR_NOSMF,
339 * SMF_EXIT_ERR_PERM, or SMF_EXIT_ERR_FATAL.
340 */
341 {
342 restarter_str_method_failed,
343 "method_failed",
344 "a start, stop or refresh method failed"
345 },
346
347 /*
348 * A transition 'uninitialized -> {disabled|offline}' after
349 * "insert_in_graph" to match the state configured in the
350 * repository.
351 */
352 {
353 restarter_str_per_configuration,
354 "per_configuration",
355 "the SMF repository configuration specifies this state"
356 },
357
358 /*
359 * Refresh requested - no state change.
360 */
361 {
362 restarter_str_refresh,
363 NULL,
364 "a refresh was requested (no change of state)"
365 },
366
367 /*
368 * A transition 'online -> offline -> online' due to a
369 * 'svcadm restart <fmri> or equivlaent libscf API call.
370 * Both the 'online -> offline' and 'offline -> online' transtions
371 * specify this reason.
372 */
373 {
374 restarter_str_restart_request,
375 "restart_request",
376 "a restart was requested"
377 },
378
379 /*
380 * A transition to maintenance state because the start method is
381 * being executed successfully but too frequently.
382 */
383 {
384 restarter_str_restarting_too_quickly,
385 "restarting_too_quickly",
386 "the instance is restarting too quickly"
387 },
388
389 /*
390 * A transition to maintenance state due a service requesting
391 * 'svcadm mark maintenance <fmri>' or equivalent libscf API call.
392 * A command line 'svcadm mark maintenance <fmri>' does not produce
393 * this reason - it produces administrative_request instead.
394 */
395 {
396 restarter_str_service_request,
397 "service_request",
398 "maintenance was requested by another service"
399 },
400
401 /*
402 * An instanced inserted into the graph at its existing state
403 * during a startd restart - no state change.
404 */
405 {
406 restarter_str_startd_restart,
407 NULL,
408 "the instance was inserted in the graph due to startd restart"
409 }
410 };
411
412 uint32_t
restarter_str_version(void)413 restarter_str_version(void)
414 {
415 return (RESTARTER_STRING_VERSION);
416 }
417
418 const char *
restarter_get_str_short(restarter_str_t key)419 restarter_get_str_short(restarter_str_t key)
420 {
421 int i;
422 for (i = 0; i < sizeof (restarter_str) /
423 sizeof (struct restarter_state_transition_reason); i++)
424 if (key == restarter_str[i].str_key)
425 return (restarter_str[i].str_short);
426 return (NULL);
427 }
428
429 const char *
restarter_get_str_long(restarter_str_t key)430 restarter_get_str_long(restarter_str_t key)
431 {
432 int i;
433 for (i = 0; i < sizeof (restarter_str) /
434 sizeof (struct restarter_state_transition_reason); i++)
435 if (key == restarter_str[i].str_key)
436 return (dgettext(TEXT_DOMAIN,
437 restarter_str[i].str_long));
438 return (NULL);
439 }
440
441 /*
442 * A static no memory error message mc_error_t structure
443 * to be used in cases when memory errors are to be returned
444 * This avoids the need to attempt to allocate memory for the
445 * message, therefore getting into a cycle of no memory failures.
446 */
447 mc_error_t mc_nomem_err = {
448 0, ENOMEM, sizeof ("Out of memory") - 1, "Out of memory"
449 };
450
451 static const char * const allocfail = "Allocation failure.\n";
452 static const char * const rcbroken = "Repository connection broken.\n";
453
454 static int method_context_safety = 0; /* Can safely call pools/projects. */
455
456 int ndebug = 1;
457
458 /* PRINTFLIKE3 */
459 static mc_error_t *
mc_error_create(mc_error_t * e,int type,const char * format,...)460 mc_error_create(mc_error_t *e, int type, const char *format, ...)
461 {
462 mc_error_t *le;
463 va_list args;
464 int size;
465
466 /*
467 * If the type is ENOMEM and format is NULL, then
468 * go ahead and return the default nomem error.
469 * Otherwise, attempt to allocate the memory and if
470 * that fails then there is no reason to continue.
471 */
472 if (type == ENOMEM && format == NULL)
473 return (&mc_nomem_err);
474
475 if (e == NULL && (le = malloc(sizeof (mc_error_t))) == NULL)
476 return (&mc_nomem_err);
477 else
478 le = e;
479
480 le->type = type;
481 le->destroy = 1;
482 va_start(args, format);
483 size = vsnprintf(NULL, 0, format, args) + 1;
484 if (size >= RESTARTER_ERRMSGSZ) {
485 if ((le = realloc(e, sizeof (mc_error_t) +
486 (size - RESTARTER_ERRMSGSZ))) == NULL) {
487 size = RESTARTER_ERRMSGSZ - 1;
488 le = e;
489 }
490 }
491
492 le->size = size;
493 (void) vsnprintf(le->msg, le->size, format, args);
494 va_end(args);
495
496 return (le);
497 }
498
499 void
restarter_mc_error_destroy(mc_error_t * mc_err)500 restarter_mc_error_destroy(mc_error_t *mc_err)
501 {
502 if (mc_err == NULL)
503 return;
504
505 /*
506 * If the error messages was allocated then free.
507 */
508 if (mc_err->destroy) {
509 free(mc_err);
510 }
511 }
512
513 static void
free_restarter_event_handle(struct restarter_event_handle * h)514 free_restarter_event_handle(struct restarter_event_handle *h)
515 {
516 if (h == NULL)
517 return;
518
519 /*
520 * Just free the memory -- don't unbind the sysevent handle,
521 * as otherwise events may be lost if this is just a restarter
522 * restart.
523 */
524
525 if (h->reh_restarter_name != NULL)
526 free(h->reh_restarter_name);
527 if (h->reh_delegate_channel_name != NULL)
528 free(h->reh_delegate_channel_name);
529 if (h->reh_delegate_subscriber_id != NULL)
530 free(h->reh_delegate_subscriber_id);
531 if (h->reh_master_channel_name != NULL)
532 free(h->reh_master_channel_name);
533 if (h->reh_master_subscriber_id != NULL)
534 free(h->reh_master_subscriber_id);
535
536 free(h);
537 }
538
539 char *
_restarter_get_channel_name(const char * fmri,int type)540 _restarter_get_channel_name(const char *fmri, int type)
541 {
542 char *name;
543 char *chan_name = malloc(MAX_CHNAME_LEN);
544 char prefix_name[3];
545 int i;
546
547 if (chan_name == NULL)
548 return (NULL);
549
550 if (type == RESTARTER_CHANNEL_DELEGATE)
551 (void) strcpy(prefix_name, "d_");
552 else if (type == RESTARTER_CHANNEL_MASTER)
553 (void) strcpy(prefix_name, "m_");
554 else {
555 free(chan_name);
556 return (NULL);
557 }
558
559 /*
560 * Create a unique name
561 *
562 * Use the entire name, using a replacement of the /
563 * characters to get a better name.
564 *
565 * Remove the svc:/ from the beginning as this really
566 * isn't going to provide any uniqueness...
567 *
568 * An fmri name greater than MAX_CHNAME_LEN is going
569 * to be rejected as too long for the chan_name below
570 * in the snprintf call.
571 */
572 if ((name = strdup(strchr(fmri, '/') + 1)) == NULL) {
573 free(chan_name);
574 return (NULL);
575 }
576 i = 0;
577 while (name[i]) {
578 if (name[i] == '/') {
579 name[i] = '_';
580 }
581
582 i++;
583 }
584
585 /*
586 * Should check for [a-z],[A-Z],[0-9],.,_,-,:
587 */
588
589 if (snprintf(chan_name, MAX_CHNAME_LEN, "com.sun:scf:%s%s",
590 prefix_name, name) > MAX_CHNAME_LEN) {
591 free(chan_name);
592 chan_name = NULL;
593 }
594
595 free(name);
596 return (chan_name);
597 }
598
599 int
cb(sysevent_t * syse,void * cookie)600 cb(sysevent_t *syse, void *cookie)
601 {
602 restarter_event_handle_t *h = (restarter_event_handle_t *)cookie;
603 restarter_event_t *e;
604 nvlist_t *attr_list = NULL;
605 int ret = 0;
606
607 e = uu_zalloc(sizeof (restarter_event_t));
608 if (e == NULL)
609 uu_die(allocfail);
610 e->re_event_handle = h;
611 e->re_sysevent = syse;
612
613 if (sysevent_get_attr_list(syse, &attr_list) != 0)
614 uu_die(allocfail);
615
616 if ((nvlist_lookup_uint32(attr_list, RESTARTER_NAME_TYPE,
617 &(e->re_type)) != 0) ||
618 (nvlist_lookup_string(attr_list,
619 RESTARTER_NAME_INSTANCE, &(e->re_instance_name)) != 0)) {
620 uu_warn("%s: Can't decode nvlist for event %p\n",
621 h->reh_restarter_name, (void *)syse);
622
623 ret = 0;
624 } else {
625 ret = h->reh_handler(e);
626 }
627
628 uu_free(e);
629 nvlist_free(attr_list);
630 return (ret);
631 }
632
633 /*
634 * restarter_bind_handle(uint32_t, char *, int (*)(restarter_event_t *), int,
635 * restarter_event_handle_t **)
636 *
637 * Bind to a delegated restarter event channel.
638 * Each delegated restarter gets its own channel for resource management.
639 *
640 * Returns 0 on success or
641 * ENOTSUP version mismatch
642 * EINVAL restarter_name or event_handle is NULL
643 * ENOMEM out of memory, too many channels, or too many subscriptions
644 * EBUSY sysevent_evc_bind() could not establish binding
645 * EFAULT internal sysevent_evc_bind()/sysevent_evc_subscribe() error
646 * EMFILE out of file descriptors
647 * EPERM insufficient privilege for sysevent_evc_bind()
648 * EEXIST already subscribed
649 */
650 int
restarter_bind_handle(uint32_t version,const char * restarter_name,int (* event_handler)(restarter_event_t *),int flags,restarter_event_handle_t ** rehp)651 restarter_bind_handle(uint32_t version, const char *restarter_name,
652 int (*event_handler)(restarter_event_t *), int flags,
653 restarter_event_handle_t **rehp)
654 {
655 restarter_event_handle_t *h;
656 size_t sz;
657 int err;
658
659 if (version != RESTARTER_EVENT_VERSION)
660 return (ENOTSUP);
661
662 if (restarter_name == NULL || event_handler == NULL)
663 return (EINVAL);
664
665 if (flags & RESTARTER_FLAG_DEBUG)
666 ndebug++;
667
668 if ((h = uu_zalloc(sizeof (restarter_event_handle_t))) == NULL)
669 return (ENOMEM);
670
671 h->reh_delegate_subscriber_id = malloc(MAX_SUBID_LEN);
672 h->reh_master_subscriber_id = malloc(MAX_SUBID_LEN);
673 h->reh_restarter_name = strdup(restarter_name);
674 if (h->reh_delegate_subscriber_id == NULL ||
675 h->reh_master_subscriber_id == NULL ||
676 h->reh_restarter_name == NULL) {
677 free_restarter_event_handle(h);
678 return (ENOMEM);
679 }
680
681 sz = strlcpy(h->reh_delegate_subscriber_id, "del", MAX_SUBID_LEN);
682 assert(sz < MAX_SUBID_LEN);
683 sz = strlcpy(h->reh_master_subscriber_id, "master", MAX_SUBID_LEN);
684 assert(sz < MAX_SUBID_LEN);
685
686 h->reh_delegate_channel_name =
687 _restarter_get_channel_name(restarter_name,
688 RESTARTER_CHANNEL_DELEGATE);
689 h->reh_master_channel_name =
690 _restarter_get_channel_name(restarter_name,
691 RESTARTER_CHANNEL_MASTER);
692
693 if (h->reh_delegate_channel_name == NULL ||
694 h->reh_master_channel_name == NULL) {
695 free_restarter_event_handle(h);
696 return (ENOMEM);
697 }
698
699 if (sysevent_evc_bind(h->reh_delegate_channel_name,
700 &h->reh_delegate_channel, EVCH_CREAT|EVCH_HOLD_PEND) != 0) {
701 err = errno;
702 assert(err != EINVAL);
703 assert(err != ENOENT);
704 free_restarter_event_handle(h);
705 return (err);
706 }
707
708 if (sysevent_evc_bind(h->reh_master_channel_name,
709 &h->reh_master_channel, EVCH_CREAT|EVCH_HOLD_PEND) != 0) {
710 err = errno;
711 assert(err != EINVAL);
712 assert(err != ENOENT);
713 free_restarter_event_handle(h);
714 return (err);
715 }
716
717 h->reh_handler = event_handler;
718
719 assert(strlen(restarter_name) <= MAX_CLASS_LEN - 1);
720 assert(strlen(h->reh_delegate_subscriber_id) <= MAX_SUBID_LEN - 1);
721 assert(strlen(h->reh_master_subscriber_id) <= MAX_SUBID_LEN - 1);
722
723 if (sysevent_evc_subscribe(h->reh_delegate_channel,
724 h->reh_delegate_subscriber_id, EC_ALL, cb, h, EVCH_SUB_KEEP) != 0) {
725 err = errno;
726 assert(err != EINVAL);
727 free_restarter_event_handle(h);
728 return (err);
729 }
730
731 *rehp = h;
732 return (0);
733 }
734
735 restarter_event_handle_t *
restarter_event_get_handle(restarter_event_t * e)736 restarter_event_get_handle(restarter_event_t *e)
737 {
738 assert(e != NULL && e->re_event_handle != NULL);
739 return (e->re_event_handle);
740 }
741
742 restarter_event_type_t
restarter_event_get_type(restarter_event_t * e)743 restarter_event_get_type(restarter_event_t *e)
744 {
745 assert(e != NULL);
746 return (e->re_type);
747 }
748
749 ssize_t
restarter_event_get_instance(restarter_event_t * e,char * inst,size_t sz)750 restarter_event_get_instance(restarter_event_t *e, char *inst, size_t sz)
751 {
752 assert(e != NULL && inst != NULL);
753 return ((ssize_t)strlcpy(inst, e->re_instance_name, sz));
754 }
755
756 int
restarter_event_get_current_states(restarter_event_t * e,restarter_instance_state_t * state,restarter_instance_state_t * next_state)757 restarter_event_get_current_states(restarter_event_t *e,
758 restarter_instance_state_t *state, restarter_instance_state_t *next_state)
759 {
760 if (e == NULL)
761 return (-1);
762 *state = e->re_state;
763 *next_state = e->re_next_state;
764 return (0);
765 }
766
767 /*
768 * restarter_event_publish_retry() is a wrapper around sysevent_evc_publish().
769 * In case, the event cannot be sent at the first attempt (sysevent_evc_publish
770 * returned EAGAIN - sysevent queue full), this function retries a few time
771 * and return ENOSPC if it reaches the retry limit.
772 *
773 * The arguments to this function map the arguments of sysevent_evc_publish().
774 *
775 * On success, return 0. On error, return
776 *
777 * EFAULT - internal sysevent_evc_publish() error
778 * ENOMEM - internal sysevent_evc_publish() error
779 * EBADF - scp is invalid (sysevent_evc_publish() returned EINVAL)
780 * ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN)
781 */
782 int
restarter_event_publish_retry(evchan_t * scp,const char * class,const char * subclass,const char * vendor,const char * pub_name,nvlist_t * attr_list,uint32_t flags)783 restarter_event_publish_retry(evchan_t *scp, const char *class,
784 const char *subclass, const char *vendor, const char *pub_name,
785 nvlist_t *attr_list, uint32_t flags)
786 {
787 int retries, ret;
788 useconds_t retry_int = INITIAL_COMMIT_RETRY_INT;
789
790 for (retries = 0; retries < MAX_COMMIT_RETRIES; retries++) {
791 ret = sysevent_evc_publish(scp, class, subclass, vendor,
792 pub_name, attr_list, flags);
793 if (ret == 0)
794 break;
795
796 switch (ret) {
797 case EAGAIN:
798 /* Queue is full */
799 (void) usleep(retry_int);
800
801 retry_int = min(retry_int * 2, MAX_COMMIT_RETRY_INT);
802 break;
803
804 case EINVAL:
805 ret = EBADF;
806 /* FALLTHROUGH */
807
808 case EFAULT:
809 case ENOMEM:
810 return (ret);
811
812 case EOVERFLOW:
813 default:
814 /* internal error - abort */
815 bad_fail("sysevent_evc_publish", ret);
816 }
817 }
818
819 if (retries == MAX_COMMIT_RETRIES)
820 ret = ENOSPC;
821
822 return (ret);
823 }
824
825 /*
826 * Commit the state, next state, and auxiliary state into the repository.
827 * Let the graph engine know about the state change and error. On success,
828 * return 0. On error, return
829 * EPROTO - librestart compiled against different libscf
830 * ENOMEM - out of memory
831 * - repository server out of resources
832 * ENOTACTIVE - repository server not running
833 * ECONNABORTED - repository connection established, but then broken
834 * - unknown libscf error
835 * ENOENT - inst does not exist in the repository
836 * EPERM - insufficient permissions
837 * EACCESS - backend access denied
838 * EROFS - backend is readonly
839 * EFAULT - internal sysevent_evc_publish() error
840 * EBADF - h is invalid (sysevent_evc_publish() returned EINVAL)
841 * ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN)
842 */
843 int
restarter_set_states(restarter_event_handle_t * h,const char * inst,restarter_instance_state_t cur_state,restarter_instance_state_t new_cur_state,restarter_instance_state_t next_state,restarter_instance_state_t new_next_state,restarter_error_t e,restarter_str_t aux)844 restarter_set_states(restarter_event_handle_t *h, const char *inst,
845 restarter_instance_state_t cur_state,
846 restarter_instance_state_t new_cur_state,
847 restarter_instance_state_t next_state,
848 restarter_instance_state_t new_next_state, restarter_error_t e,
849 restarter_str_t aux)
850 {
851 nvlist_t *attr;
852 scf_handle_t *scf_h;
853 instance_data_t id;
854 int ret = 0;
855 const char *p = restarter_get_str_short(aux);
856
857 assert(h->reh_master_channel != NULL);
858 assert(h->reh_master_channel_name != NULL);
859 assert(h->reh_master_subscriber_id != NULL);
860
861 if ((scf_h = scf_handle_create(SCF_VERSION)) == NULL) {
862 switch (scf_error()) {
863 case SCF_ERROR_VERSION_MISMATCH:
864 return (EPROTO);
865
866 case SCF_ERROR_NO_MEMORY:
867 return (ENOMEM);
868
869 default:
870 bad_fail("scf_handle_create", scf_error());
871 }
872 }
873
874 if (scf_handle_bind(scf_h) == -1) {
875 scf_handle_destroy(scf_h);
876 switch (scf_error()) {
877 case SCF_ERROR_NO_SERVER:
878 return (ENOTACTIVE);
879
880 case SCF_ERROR_NO_RESOURCES:
881 return (ENOMEM);
882
883 case SCF_ERROR_INVALID_ARGUMENT:
884 case SCF_ERROR_IN_USE:
885 default:
886 bad_fail("scf_handle_bind", scf_error());
887 }
888 }
889
890 if (nvlist_alloc(&attr, NV_UNIQUE_NAME, 0) != 0 ||
891 nvlist_add_int32(attr, RESTARTER_NAME_STATE, new_cur_state) != 0 ||
892 nvlist_add_int32(attr, RESTARTER_NAME_NEXT_STATE, new_next_state)
893 != 0 ||
894 nvlist_add_int32(attr, RESTARTER_NAME_ERROR, e) != 0 ||
895 nvlist_add_string(attr, RESTARTER_NAME_INSTANCE, inst) != 0 ||
896 nvlist_add_int32(attr, RESTARTER_NAME_REASON, aux) != 0) {
897 ret = ENOMEM;
898 } else {
899 id.i_fmri = inst;
900 id.i_state = cur_state;
901 id.i_next_state = next_state;
902
903 ret = _restarter_commit_states(scf_h, &id, new_cur_state,
904 new_next_state, p);
905
906 if (ret == 0) {
907 ret = restarter_event_publish_retry(
908 h->reh_master_channel, "master", "state_change",
909 "com.sun", "librestart", attr, EVCH_NOSLEEP);
910 }
911 }
912
913 nvlist_free(attr);
914 (void) scf_handle_unbind(scf_h);
915 scf_handle_destroy(scf_h);
916
917 return (ret);
918 }
919
920 restarter_instance_state_t
restarter_string_to_state(char * string)921 restarter_string_to_state(char *string)
922 {
923 assert(string != NULL);
924
925 if (strcmp(string, SCF_STATE_STRING_NONE) == 0)
926 return (RESTARTER_STATE_NONE);
927 else if (strcmp(string, SCF_STATE_STRING_UNINIT) == 0)
928 return (RESTARTER_STATE_UNINIT);
929 else if (strcmp(string, SCF_STATE_STRING_MAINT) == 0)
930 return (RESTARTER_STATE_MAINT);
931 else if (strcmp(string, SCF_STATE_STRING_OFFLINE) == 0)
932 return (RESTARTER_STATE_OFFLINE);
933 else if (strcmp(string, SCF_STATE_STRING_DISABLED) == 0)
934 return (RESTARTER_STATE_DISABLED);
935 else if (strcmp(string, SCF_STATE_STRING_ONLINE) == 0)
936 return (RESTARTER_STATE_ONLINE);
937 else if (strcmp(string, SCF_STATE_STRING_DEGRADED) == 0)
938 return (RESTARTER_STATE_DEGRADED);
939 else {
940 return (RESTARTER_STATE_NONE);
941 }
942 }
943
944 ssize_t
restarter_state_to_string(restarter_instance_state_t state,char * string,size_t len)945 restarter_state_to_string(restarter_instance_state_t state, char *string,
946 size_t len)
947 {
948 assert(string != NULL);
949
950 if (state == RESTARTER_STATE_NONE)
951 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_NONE, len));
952 else if (state == RESTARTER_STATE_UNINIT)
953 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_UNINIT, len));
954 else if (state == RESTARTER_STATE_MAINT)
955 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_MAINT, len));
956 else if (state == RESTARTER_STATE_OFFLINE)
957 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_OFFLINE,
958 len));
959 else if (state == RESTARTER_STATE_DISABLED)
960 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_DISABLED,
961 len));
962 else if (state == RESTARTER_STATE_ONLINE)
963 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_ONLINE, len));
964 else if (state == RESTARTER_STATE_DEGRADED)
965 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_DEGRADED,
966 len));
967 else
968 return ((ssize_t)strlcpy(string, "unknown", len));
969 }
970
971 /*
972 * Sets pg to the name property group of s_inst. If it doesn't exist, it is
973 * added.
974 *
975 * Fails with
976 * ECONNABORTED - repository disconnection or unknown libscf error
977 * EBADF - inst is not set
978 * ECANCELED - inst is deleted
979 * EPERM - permission is denied
980 * EACCES - backend denied access
981 * EROFS - backend readonly
982 */
983 static int
instance_get_or_add_pg(scf_instance_t * inst,const char * name,const char * type,uint32_t flags,scf_propertygroup_t * pg)984 instance_get_or_add_pg(scf_instance_t *inst, const char *name,
985 const char *type, uint32_t flags, scf_propertygroup_t *pg)
986 {
987 again:
988 if (scf_instance_get_pg(inst, name, pg) == 0)
989 return (0);
990
991 switch (scf_error()) {
992 case SCF_ERROR_CONNECTION_BROKEN:
993 default:
994 return (ECONNABORTED);
995
996 case SCF_ERROR_NOT_SET:
997 return (EBADF);
998
999 case SCF_ERROR_DELETED:
1000 return (ECANCELED);
1001
1002 case SCF_ERROR_NOT_FOUND:
1003 break;
1004
1005 case SCF_ERROR_HANDLE_MISMATCH:
1006 case SCF_ERROR_INVALID_ARGUMENT:
1007 bad_fail("scf_instance_get_pg", scf_error());
1008 }
1009
1010 if (scf_instance_add_pg(inst, name, type, flags, pg) == 0)
1011 return (0);
1012
1013 switch (scf_error()) {
1014 case SCF_ERROR_CONNECTION_BROKEN:
1015 default:
1016 return (ECONNABORTED);
1017
1018 case SCF_ERROR_DELETED:
1019 return (ECANCELED);
1020
1021 case SCF_ERROR_EXISTS:
1022 goto again;
1023
1024 case SCF_ERROR_PERMISSION_DENIED:
1025 return (EPERM);
1026
1027 case SCF_ERROR_BACKEND_ACCESS:
1028 return (EACCES);
1029
1030 case SCF_ERROR_BACKEND_READONLY:
1031 return (EROFS);
1032
1033 case SCF_ERROR_HANDLE_MISMATCH:
1034 case SCF_ERROR_INVALID_ARGUMENT:
1035 case SCF_ERROR_NOT_SET: /* should be caught above */
1036 bad_fail("scf_instance_add_pg", scf_error());
1037 }
1038
1039 return (0);
1040 }
1041
1042 /*
1043 * Fails with
1044 * ECONNABORTED
1045 * ECANCELED - pg was deleted
1046 */
1047 static int
tx_set_value(scf_transaction_t * tx,scf_transaction_entry_t * ent,const char * pname,scf_type_t ty,scf_value_t * val)1048 tx_set_value(scf_transaction_t *tx, scf_transaction_entry_t *ent,
1049 const char *pname, scf_type_t ty, scf_value_t *val)
1050 {
1051 int r;
1052
1053 for (;;) {
1054 if (scf_transaction_property_change_type(tx, ent, pname,
1055 ty) == 0)
1056 break;
1057
1058 switch (scf_error()) {
1059 case SCF_ERROR_CONNECTION_BROKEN:
1060 default:
1061 return (ECONNABORTED);
1062
1063 case SCF_ERROR_DELETED:
1064 return (ECANCELED);
1065
1066 case SCF_ERROR_NOT_FOUND:
1067 break;
1068
1069 case SCF_ERROR_HANDLE_MISMATCH:
1070 case SCF_ERROR_INVALID_ARGUMENT:
1071 case SCF_ERROR_IN_USE:
1072 case SCF_ERROR_NOT_SET:
1073 bad_fail("scf_transaction_property_change_type",
1074 scf_error());
1075 }
1076
1077 if (scf_transaction_property_new(tx, ent, pname, ty) == 0)
1078 break;
1079
1080 switch (scf_error()) {
1081 case SCF_ERROR_CONNECTION_BROKEN:
1082 default:
1083 return (ECONNABORTED);
1084
1085 case SCF_ERROR_DELETED:
1086 return (ECANCELED);
1087
1088 case SCF_ERROR_EXISTS:
1089 break;
1090
1091 case SCF_ERROR_HANDLE_MISMATCH:
1092 case SCF_ERROR_INVALID_ARGUMENT:
1093 case SCF_ERROR_IN_USE:
1094 case SCF_ERROR_NOT_SET:
1095 bad_fail("scf_transaction_property_new", scf_error());
1096 }
1097 }
1098
1099 r = scf_entry_add_value(ent, val);
1100 assert(r == 0);
1101
1102 return (0);
1103 }
1104
1105 /*
1106 * Commit new_state, new_next_state, and aux to the repository for id. If
1107 * successful, also set id's state and next-state as given, and return 0.
1108 * Fails with
1109 * ENOMEM - out of memory
1110 * ECONNABORTED - repository connection broken
1111 * - unknown libscf error
1112 * EINVAL - id->i_fmri is invalid or not an instance FMRI
1113 * ENOENT - id->i_fmri does not exist
1114 * EPERM - insufficient permissions
1115 * EACCES - backend access denied
1116 * EROFS - backend is readonly
1117 */
1118 int
_restarter_commit_states(scf_handle_t * h,instance_data_t * id,restarter_instance_state_t new_state,restarter_instance_state_t new_state_next,const char * aux)1119 _restarter_commit_states(scf_handle_t *h, instance_data_t *id,
1120 restarter_instance_state_t new_state,
1121 restarter_instance_state_t new_state_next, const char *aux)
1122 {
1123 char str_state[MAX_SCF_STATE_STRING_SZ];
1124 char str_new_state[MAX_SCF_STATE_STRING_SZ];
1125 char str_state_next[MAX_SCF_STATE_STRING_SZ];
1126 char str_new_state_next[MAX_SCF_STATE_STRING_SZ];
1127 int ret = 0, r;
1128 struct timeval now;
1129 ssize_t sz;
1130
1131 scf_transaction_t *t = NULL;
1132 scf_transaction_entry_t *t_state = NULL, *t_state_next = NULL;
1133 scf_transaction_entry_t *t_stime = NULL, *t_aux = NULL;
1134 scf_value_t *v_state = NULL, *v_state_next = NULL, *v_stime = NULL;
1135 scf_value_t *v_aux = NULL;
1136 scf_instance_t *s_inst = NULL;
1137 scf_propertygroup_t *pg = NULL;
1138
1139 assert(new_state != RESTARTER_STATE_NONE);
1140
1141 if ((s_inst = scf_instance_create(h)) == NULL ||
1142 (pg = scf_pg_create(h)) == NULL ||
1143 (t = scf_transaction_create(h)) == NULL ||
1144 (t_state = scf_entry_create(h)) == NULL ||
1145 (t_state_next = scf_entry_create(h)) == NULL ||
1146 (t_stime = scf_entry_create(h)) == NULL ||
1147 (t_aux = scf_entry_create(h)) == NULL ||
1148 (v_state = scf_value_create(h)) == NULL ||
1149 (v_state_next = scf_value_create(h)) == NULL ||
1150 (v_stime = scf_value_create(h)) == NULL ||
1151 (v_aux = scf_value_create(h)) == NULL) {
1152 ret = ENOMEM;
1153 goto out;
1154 }
1155
1156 sz = restarter_state_to_string(new_state, str_new_state,
1157 sizeof (str_new_state));
1158 assert(sz < sizeof (str_new_state));
1159 sz = restarter_state_to_string(new_state_next, str_new_state_next,
1160 sizeof (str_new_state_next));
1161 assert(sz < sizeof (str_new_state_next));
1162 sz = restarter_state_to_string(id->i_state, str_state,
1163 sizeof (str_state));
1164 assert(sz < sizeof (str_state));
1165 sz = restarter_state_to_string(id->i_next_state, str_state_next,
1166 sizeof (str_state_next));
1167 assert(sz < sizeof (str_state_next));
1168
1169 ret = gettimeofday(&now, NULL);
1170 assert(ret != -1);
1171
1172 if (scf_handle_decode_fmri(h, id->i_fmri, NULL, NULL, s_inst,
1173 NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
1174 switch (scf_error()) {
1175 case SCF_ERROR_CONNECTION_BROKEN:
1176 default:
1177 ret = ECONNABORTED;
1178 break;
1179
1180 case SCF_ERROR_INVALID_ARGUMENT:
1181 case SCF_ERROR_CONSTRAINT_VIOLATED:
1182 ret = EINVAL;
1183 break;
1184
1185 case SCF_ERROR_NOT_FOUND:
1186 ret = ENOENT;
1187 break;
1188
1189 case SCF_ERROR_HANDLE_MISMATCH:
1190 bad_fail("scf_handle_decode_fmri", scf_error());
1191 }
1192 goto out;
1193 }
1194
1195
1196 if (scf_value_set_astring(v_state, str_new_state) != 0 ||
1197 scf_value_set_astring(v_state_next, str_new_state_next) != 0)
1198 bad_fail("scf_value_set_astring", scf_error());
1199
1200 if (aux) {
1201 if (scf_value_set_astring(v_aux, aux) != 0)
1202 bad_fail("scf_value_set_astring", scf_error());
1203 }
1204
1205 if (scf_value_set_time(v_stime, now.tv_sec, now.tv_usec * 1000) != 0)
1206 bad_fail("scf_value_set_time", scf_error());
1207
1208 add_pg:
1209 switch (r = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
1210 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg)) {
1211 case 0:
1212 break;
1213
1214 case ECONNABORTED:
1215 case EPERM:
1216 case EACCES:
1217 case EROFS:
1218 ret = r;
1219 goto out;
1220
1221 case ECANCELED:
1222 ret = ENOENT;
1223 goto out;
1224
1225 case EBADF:
1226 default:
1227 bad_fail("instance_get_or_add_pg", r);
1228 }
1229
1230 for (;;) {
1231 if (scf_transaction_start(t, pg) != 0) {
1232 switch (scf_error()) {
1233 case SCF_ERROR_CONNECTION_BROKEN:
1234 default:
1235 ret = ECONNABORTED;
1236 goto out;
1237
1238 case SCF_ERROR_NOT_SET:
1239 goto add_pg;
1240
1241 case SCF_ERROR_PERMISSION_DENIED:
1242 ret = EPERM;
1243 goto out;
1244
1245 case SCF_ERROR_BACKEND_ACCESS:
1246 ret = EACCES;
1247 goto out;
1248
1249 case SCF_ERROR_BACKEND_READONLY:
1250 ret = EROFS;
1251 goto out;
1252
1253 case SCF_ERROR_HANDLE_MISMATCH:
1254 case SCF_ERROR_IN_USE:
1255 bad_fail("scf_transaction_start", scf_error());
1256 }
1257 }
1258
1259 if ((r = tx_set_value(t, t_state, SCF_PROPERTY_STATE,
1260 SCF_TYPE_ASTRING, v_state)) != 0 ||
1261 (r = tx_set_value(t, t_state_next, SCF_PROPERTY_NEXT_STATE,
1262 SCF_TYPE_ASTRING, v_state_next)) != 0 ||
1263 (r = tx_set_value(t, t_stime, SCF_PROPERTY_STATE_TIMESTAMP,
1264 SCF_TYPE_TIME, v_stime)) != 0) {
1265 switch (r) {
1266 case ECONNABORTED:
1267 ret = ECONNABORTED;
1268 goto out;
1269
1270 case ECANCELED:
1271 scf_transaction_reset(t);
1272 goto add_pg;
1273
1274 default:
1275 bad_fail("tx_set_value", r);
1276 }
1277 }
1278
1279 if (aux) {
1280 if ((r = tx_set_value(t, t_aux, SCF_PROPERTY_AUX_STATE,
1281 SCF_TYPE_ASTRING, v_aux)) != 0) {
1282 switch (r) {
1283 case ECONNABORTED:
1284 ret = ECONNABORTED;
1285 goto out;
1286
1287 case ECANCELED:
1288 scf_transaction_reset(t);
1289 goto add_pg;
1290
1291 default:
1292 bad_fail("tx_set_value", r);
1293 }
1294 }
1295 }
1296
1297 ret = scf_transaction_commit(t);
1298 if (ret == 1)
1299 break;
1300 if (ret == -1) {
1301 switch (scf_error()) {
1302 case SCF_ERROR_CONNECTION_BROKEN:
1303 default:
1304 ret = ECONNABORTED;
1305 goto out;
1306
1307 case SCF_ERROR_PERMISSION_DENIED:
1308 ret = EPERM;
1309 goto out;
1310
1311 case SCF_ERROR_BACKEND_ACCESS:
1312 ret = EACCES;
1313 goto out;
1314
1315 case SCF_ERROR_BACKEND_READONLY:
1316 ret = EROFS;
1317 goto out;
1318
1319 case SCF_ERROR_NOT_SET:
1320 bad_fail("scf_transaction_commit", scf_error());
1321 }
1322 }
1323
1324 scf_transaction_reset(t);
1325 if (scf_pg_update(pg) == -1) {
1326 switch (scf_error()) {
1327 case SCF_ERROR_CONNECTION_BROKEN:
1328 default:
1329 ret = ECONNABORTED;
1330 goto out;
1331
1332 case SCF_ERROR_NOT_SET:
1333 goto add_pg;
1334 }
1335 }
1336 }
1337
1338 id->i_state = new_state;
1339 id->i_next_state = new_state_next;
1340 ret = 0;
1341
1342 out:
1343 scf_transaction_destroy(t);
1344 scf_entry_destroy(t_state);
1345 scf_entry_destroy(t_state_next);
1346 scf_entry_destroy(t_stime);
1347 scf_entry_destroy(t_aux);
1348 scf_value_destroy(v_state);
1349 scf_value_destroy(v_state_next);
1350 scf_value_destroy(v_stime);
1351 scf_value_destroy(v_aux);
1352 scf_pg_destroy(pg);
1353 scf_instance_destroy(s_inst);
1354
1355 return (ret);
1356 }
1357
1358 /*
1359 * Fails with
1360 * EINVAL - type is invalid
1361 * ENOMEM
1362 * ECONNABORTED - repository connection broken
1363 * EBADF - s_inst is not set
1364 * ECANCELED - s_inst is deleted
1365 * EPERM - permission denied
1366 * EACCES - backend access denied
1367 * EROFS - backend readonly
1368 */
1369 int
restarter_remove_contract(scf_instance_t * s_inst,ctid_t contract_id,restarter_contract_type_t type)1370 restarter_remove_contract(scf_instance_t *s_inst, ctid_t contract_id,
1371 restarter_contract_type_t type)
1372 {
1373 scf_handle_t *h;
1374 scf_transaction_t *t = NULL;
1375 scf_transaction_entry_t *t_cid = NULL;
1376 scf_propertygroup_t *pg = NULL;
1377 scf_property_t *prop = NULL;
1378 scf_value_t *val;
1379 scf_iter_t *iter = NULL;
1380 const char *pname;
1381 int ret = 0, primary;
1382 uint64_t c;
1383
1384 switch (type) {
1385 case RESTARTER_CONTRACT_PRIMARY:
1386 primary = 1;
1387 break;
1388 case RESTARTER_CONTRACT_TRANSIENT:
1389 primary = 0;
1390 break;
1391 default:
1392 return (EINVAL);
1393 }
1394
1395 h = scf_instance_handle(s_inst);
1396
1397 pg = scf_pg_create(h);
1398 prop = scf_property_create(h);
1399 iter = scf_iter_create(h);
1400 t = scf_transaction_create(h);
1401
1402 if (pg == NULL || prop == NULL || iter == NULL || t == NULL) {
1403 ret = ENOMEM;
1404 goto remove_contract_cleanup;
1405 }
1406
1407 add:
1408 scf_transaction_destroy_children(t);
1409 ret = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
1410 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
1411 if (ret != 0)
1412 goto remove_contract_cleanup;
1413
1414 pname = primary? SCF_PROPERTY_CONTRACT :
1415 SCF_PROPERTY_TRANSIENT_CONTRACT;
1416
1417 for (;;) {
1418 if (scf_transaction_start(t, pg) != 0) {
1419 switch (scf_error()) {
1420 case SCF_ERROR_CONNECTION_BROKEN:
1421 default:
1422 ret = ECONNABORTED;
1423 goto remove_contract_cleanup;
1424
1425 case SCF_ERROR_DELETED:
1426 goto add;
1427
1428 case SCF_ERROR_PERMISSION_DENIED:
1429 ret = EPERM;
1430 goto remove_contract_cleanup;
1431
1432 case SCF_ERROR_BACKEND_ACCESS:
1433 ret = EACCES;
1434 goto remove_contract_cleanup;
1435
1436 case SCF_ERROR_BACKEND_READONLY:
1437 ret = EROFS;
1438 goto remove_contract_cleanup;
1439
1440 case SCF_ERROR_HANDLE_MISMATCH:
1441 case SCF_ERROR_IN_USE:
1442 case SCF_ERROR_NOT_SET:
1443 bad_fail("scf_transaction_start", scf_error());
1444 }
1445 }
1446
1447 t_cid = scf_entry_create(h);
1448
1449 if (scf_pg_get_property(pg, pname, prop) == 0) {
1450 replace:
1451 if (scf_transaction_property_change_type(t, t_cid,
1452 pname, SCF_TYPE_COUNT) != 0) {
1453 switch (scf_error()) {
1454 case SCF_ERROR_CONNECTION_BROKEN:
1455 default:
1456 ret = ECONNABORTED;
1457 goto remove_contract_cleanup;
1458
1459 case SCF_ERROR_DELETED:
1460 scf_entry_destroy(t_cid);
1461 goto add;
1462
1463 case SCF_ERROR_NOT_FOUND:
1464 goto new;
1465
1466 case SCF_ERROR_HANDLE_MISMATCH:
1467 case SCF_ERROR_INVALID_ARGUMENT:
1468 case SCF_ERROR_IN_USE:
1469 case SCF_ERROR_NOT_SET:
1470 bad_fail(
1471 "scf_transaction_property_changetype",
1472 scf_error());
1473 }
1474 }
1475
1476 if (scf_property_is_type(prop, SCF_TYPE_COUNT) == 0) {
1477 if (scf_iter_property_values(iter, prop) != 0) {
1478 switch (scf_error()) {
1479 case SCF_ERROR_CONNECTION_BROKEN:
1480 default:
1481 ret = ECONNABORTED;
1482 goto remove_contract_cleanup;
1483
1484 case SCF_ERROR_NOT_SET:
1485 case SCF_ERROR_HANDLE_MISMATCH:
1486 bad_fail(
1487 "scf_iter_property_values",
1488 scf_error());
1489 }
1490 }
1491
1492 next_val:
1493 val = scf_value_create(h);
1494 if (val == NULL) {
1495 assert(scf_error() ==
1496 SCF_ERROR_NO_MEMORY);
1497 ret = ENOMEM;
1498 goto remove_contract_cleanup;
1499 }
1500
1501 ret = scf_iter_next_value(iter, val);
1502 if (ret == -1) {
1503 switch (scf_error()) {
1504 case SCF_ERROR_CONNECTION_BROKEN:
1505 ret = ECONNABORTED;
1506 goto remove_contract_cleanup;
1507
1508 case SCF_ERROR_DELETED:
1509 scf_value_destroy(val);
1510 goto add;
1511
1512 case SCF_ERROR_HANDLE_MISMATCH:
1513 case SCF_ERROR_INVALID_ARGUMENT:
1514 case SCF_ERROR_PERMISSION_DENIED:
1515 default:
1516 bad_fail("scf_iter_next_value",
1517 scf_error());
1518 }
1519 }
1520
1521 if (ret == 1) {
1522 ret = scf_value_get_count(val, &c);
1523 assert(ret == 0);
1524
1525 if (c != contract_id) {
1526 ret = scf_entry_add_value(t_cid,
1527 val);
1528 assert(ret == 0);
1529 } else {
1530 scf_value_destroy(val);
1531 }
1532
1533 goto next_val;
1534 }
1535
1536 scf_value_destroy(val);
1537 } else {
1538 switch (scf_error()) {
1539 case SCF_ERROR_CONNECTION_BROKEN:
1540 default:
1541 ret = ECONNABORTED;
1542 goto remove_contract_cleanup;
1543
1544 case SCF_ERROR_TYPE_MISMATCH:
1545 break;
1546
1547 case SCF_ERROR_INVALID_ARGUMENT:
1548 case SCF_ERROR_NOT_SET:
1549 bad_fail("scf_property_is_type",
1550 scf_error());
1551 }
1552 }
1553 } else {
1554 switch (scf_error()) {
1555 case SCF_ERROR_CONNECTION_BROKEN:
1556 default:
1557 ret = ECONNABORTED;
1558 goto remove_contract_cleanup;
1559
1560 case SCF_ERROR_DELETED:
1561 scf_entry_destroy(t_cid);
1562 goto add;
1563
1564 case SCF_ERROR_NOT_FOUND:
1565 break;
1566
1567 case SCF_ERROR_HANDLE_MISMATCH:
1568 case SCF_ERROR_INVALID_ARGUMENT:
1569 case SCF_ERROR_NOT_SET:
1570 bad_fail("scf_pg_get_property", scf_error());
1571 }
1572
1573 new:
1574 if (scf_transaction_property_new(t, t_cid, pname,
1575 SCF_TYPE_COUNT) != 0) {
1576 switch (scf_error()) {
1577 case SCF_ERROR_CONNECTION_BROKEN:
1578 default:
1579 ret = ECONNABORTED;
1580 goto remove_contract_cleanup;
1581
1582 case SCF_ERROR_DELETED:
1583 scf_entry_destroy(t_cid);
1584 goto add;
1585
1586 case SCF_ERROR_EXISTS:
1587 goto replace;
1588
1589 case SCF_ERROR_HANDLE_MISMATCH:
1590 case SCF_ERROR_INVALID_ARGUMENT:
1591 case SCF_ERROR_NOT_SET:
1592 bad_fail("scf_transaction_property_new",
1593 scf_error());
1594 }
1595 }
1596 }
1597
1598 ret = scf_transaction_commit(t);
1599 if (ret == -1) {
1600 switch (scf_error()) {
1601 case SCF_ERROR_CONNECTION_BROKEN:
1602 default:
1603 ret = ECONNABORTED;
1604 goto remove_contract_cleanup;
1605
1606 case SCF_ERROR_DELETED:
1607 goto add;
1608
1609 case SCF_ERROR_PERMISSION_DENIED:
1610 ret = EPERM;
1611 goto remove_contract_cleanup;
1612
1613 case SCF_ERROR_BACKEND_ACCESS:
1614 ret = EACCES;
1615 goto remove_contract_cleanup;
1616
1617 case SCF_ERROR_BACKEND_READONLY:
1618 ret = EROFS;
1619 goto remove_contract_cleanup;
1620
1621 case SCF_ERROR_NOT_SET:
1622 bad_fail("scf_transaction_commit", scf_error());
1623 }
1624 }
1625 if (ret == 1) {
1626 ret = 0;
1627 break;
1628 }
1629
1630 scf_transaction_destroy_children(t);
1631 if (scf_pg_update(pg) == -1) {
1632 switch (scf_error()) {
1633 case SCF_ERROR_CONNECTION_BROKEN:
1634 default:
1635 ret = ECONNABORTED;
1636 goto remove_contract_cleanup;
1637
1638 case SCF_ERROR_DELETED:
1639 goto add;
1640
1641 case SCF_ERROR_NOT_SET:
1642 bad_fail("scf_pg_update", scf_error());
1643 }
1644 }
1645 }
1646
1647 remove_contract_cleanup:
1648 scf_transaction_destroy_children(t);
1649 scf_transaction_destroy(t);
1650 scf_iter_destroy(iter);
1651 scf_property_destroy(prop);
1652 scf_pg_destroy(pg);
1653
1654 return (ret);
1655 }
1656
1657 /*
1658 * Fails with
1659 * EINVAL - type is invalid
1660 * ENOMEM
1661 * ECONNABORTED - repository disconnection
1662 * EBADF - s_inst is not set
1663 * ECANCELED - s_inst is deleted
1664 * EPERM
1665 * EACCES
1666 * EROFS
1667 */
1668 int
restarter_store_contract(scf_instance_t * s_inst,ctid_t contract_id,restarter_contract_type_t type)1669 restarter_store_contract(scf_instance_t *s_inst, ctid_t contract_id,
1670 restarter_contract_type_t type)
1671 {
1672 scf_handle_t *h;
1673 scf_transaction_t *t = NULL;
1674 scf_transaction_entry_t *t_cid = NULL;
1675 scf_value_t *val;
1676 scf_propertygroup_t *pg = NULL;
1677 scf_property_t *prop = NULL;
1678 scf_iter_t *iter = NULL;
1679 const char *pname;
1680 int ret = 0, primary;
1681
1682 if (type == RESTARTER_CONTRACT_PRIMARY)
1683 primary = 1;
1684 else if (type == RESTARTER_CONTRACT_TRANSIENT)
1685 primary = 0;
1686 else
1687 return (EINVAL);
1688
1689 h = scf_instance_handle(s_inst);
1690
1691 pg = scf_pg_create(h);
1692 prop = scf_property_create(h);
1693 iter = scf_iter_create(h);
1694 t = scf_transaction_create(h);
1695
1696 if (pg == NULL || prop == NULL || iter == NULL || t == NULL) {
1697 ret = ENOMEM;
1698 goto out;
1699 }
1700
1701 add:
1702 scf_transaction_destroy_children(t);
1703 ret = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
1704 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
1705 if (ret != 0)
1706 goto out;
1707
1708 pname = primary ? SCF_PROPERTY_CONTRACT :
1709 SCF_PROPERTY_TRANSIENT_CONTRACT;
1710
1711 for (;;) {
1712 if (scf_transaction_start(t, pg) != 0) {
1713 switch (scf_error()) {
1714 case SCF_ERROR_CONNECTION_BROKEN:
1715 default:
1716 ret = ECONNABORTED;
1717 goto out;
1718
1719 case SCF_ERROR_DELETED:
1720 goto add;
1721
1722 case SCF_ERROR_PERMISSION_DENIED:
1723 ret = EPERM;
1724 goto out;
1725
1726 case SCF_ERROR_BACKEND_ACCESS:
1727 ret = EACCES;
1728 goto out;
1729
1730 case SCF_ERROR_BACKEND_READONLY:
1731 ret = EROFS;
1732 goto out;
1733
1734 case SCF_ERROR_HANDLE_MISMATCH:
1735 case SCF_ERROR_IN_USE:
1736 case SCF_ERROR_NOT_SET:
1737 bad_fail("scf_transaction_start", scf_error());
1738 }
1739 }
1740
1741 t_cid = scf_entry_create(h);
1742 if (t_cid == NULL) {
1743 ret = ENOMEM;
1744 goto out;
1745 }
1746
1747 if (scf_pg_get_property(pg, pname, prop) == 0) {
1748 replace:
1749 if (scf_transaction_property_change_type(t, t_cid,
1750 pname, SCF_TYPE_COUNT) != 0) {
1751 switch (scf_error()) {
1752 case SCF_ERROR_CONNECTION_BROKEN:
1753 default:
1754 ret = ECONNABORTED;
1755 goto out;
1756
1757 case SCF_ERROR_DELETED:
1758 scf_entry_destroy(t_cid);
1759 goto add;
1760
1761 case SCF_ERROR_NOT_FOUND:
1762 goto new;
1763
1764 case SCF_ERROR_HANDLE_MISMATCH:
1765 case SCF_ERROR_INVALID_ARGUMENT:
1766 case SCF_ERROR_IN_USE:
1767 case SCF_ERROR_NOT_SET:
1768 bad_fail(
1769 "scf_transaction_propert_change_type",
1770 scf_error());
1771 }
1772 }
1773
1774 if (scf_property_is_type(prop, SCF_TYPE_COUNT) == 0) {
1775 if (scf_iter_property_values(iter, prop) != 0) {
1776 switch (scf_error()) {
1777 case SCF_ERROR_CONNECTION_BROKEN:
1778 default:
1779 ret = ECONNABORTED;
1780 goto out;
1781
1782 case SCF_ERROR_NOT_SET:
1783 case SCF_ERROR_HANDLE_MISMATCH:
1784 bad_fail(
1785 "scf_iter_property_values",
1786 scf_error());
1787 }
1788 }
1789
1790 next_val:
1791 val = scf_value_create(h);
1792 if (val == NULL) {
1793 assert(scf_error() ==
1794 SCF_ERROR_NO_MEMORY);
1795 ret = ENOMEM;
1796 goto out;
1797 }
1798
1799 ret = scf_iter_next_value(iter, val);
1800 if (ret == -1) {
1801 switch (scf_error()) {
1802 case SCF_ERROR_CONNECTION_BROKEN:
1803 default:
1804 ret = ECONNABORTED;
1805 goto out;
1806
1807 case SCF_ERROR_DELETED:
1808 scf_value_destroy(val);
1809 goto add;
1810
1811 case SCF_ERROR_HANDLE_MISMATCH:
1812 case SCF_ERROR_INVALID_ARGUMENT:
1813 case SCF_ERROR_PERMISSION_DENIED:
1814 bad_fail(
1815 "scf_iter_next_value",
1816 scf_error());
1817 }
1818 }
1819
1820 if (ret == 1) {
1821 ret = scf_entry_add_value(t_cid, val);
1822 assert(ret == 0);
1823
1824 goto next_val;
1825 }
1826
1827 scf_value_destroy(val);
1828 } else {
1829 switch (scf_error()) {
1830 case SCF_ERROR_CONNECTION_BROKEN:
1831 default:
1832 ret = ECONNABORTED;
1833 goto out;
1834
1835 case SCF_ERROR_TYPE_MISMATCH:
1836 break;
1837
1838 case SCF_ERROR_INVALID_ARGUMENT:
1839 case SCF_ERROR_NOT_SET:
1840 bad_fail("scf_property_is_type",
1841 scf_error());
1842 }
1843 }
1844 } else {
1845 switch (scf_error()) {
1846 case SCF_ERROR_CONNECTION_BROKEN:
1847 default:
1848 ret = ECONNABORTED;
1849 goto out;
1850
1851 case SCF_ERROR_DELETED:
1852 scf_entry_destroy(t_cid);
1853 goto add;
1854
1855 case SCF_ERROR_NOT_FOUND:
1856 break;
1857
1858 case SCF_ERROR_HANDLE_MISMATCH:
1859 case SCF_ERROR_INVALID_ARGUMENT:
1860 case SCF_ERROR_NOT_SET:
1861 bad_fail("scf_pg_get_property", scf_error());
1862 }
1863
1864 new:
1865 if (scf_transaction_property_new(t, t_cid, pname,
1866 SCF_TYPE_COUNT) != 0) {
1867 switch (scf_error()) {
1868 case SCF_ERROR_CONNECTION_BROKEN:
1869 default:
1870 ret = ECONNABORTED;
1871 goto out;
1872
1873 case SCF_ERROR_DELETED:
1874 scf_entry_destroy(t_cid);
1875 goto add;
1876
1877 case SCF_ERROR_EXISTS:
1878 goto replace;
1879
1880 case SCF_ERROR_HANDLE_MISMATCH:
1881 case SCF_ERROR_INVALID_ARGUMENT:
1882 case SCF_ERROR_NOT_SET:
1883 bad_fail("scf_transaction_property_new",
1884 scf_error());
1885 }
1886 }
1887 }
1888
1889 val = scf_value_create(h);
1890 if (val == NULL) {
1891 assert(scf_error() == SCF_ERROR_NO_MEMORY);
1892 ret = ENOMEM;
1893 goto out;
1894 }
1895
1896 scf_value_set_count(val, contract_id);
1897 ret = scf_entry_add_value(t_cid, val);
1898 assert(ret == 0);
1899
1900 ret = scf_transaction_commit(t);
1901 if (ret == -1) {
1902 switch (scf_error()) {
1903 case SCF_ERROR_CONNECTION_BROKEN:
1904 default:
1905 ret = ECONNABORTED;
1906 goto out;
1907
1908 case SCF_ERROR_DELETED:
1909 goto add;
1910
1911 case SCF_ERROR_PERMISSION_DENIED:
1912 ret = EPERM;
1913 goto out;
1914
1915 case SCF_ERROR_BACKEND_ACCESS:
1916 ret = EACCES;
1917 goto out;
1918
1919 case SCF_ERROR_BACKEND_READONLY:
1920 ret = EROFS;
1921 goto out;
1922
1923 case SCF_ERROR_NOT_SET:
1924 bad_fail("scf_transaction_commit", scf_error());
1925 }
1926 }
1927 if (ret == 1) {
1928 ret = 0;
1929 break;
1930 }
1931
1932 scf_transaction_destroy_children(t);
1933 if (scf_pg_update(pg) == -1) {
1934 switch (scf_error()) {
1935 case SCF_ERROR_CONNECTION_BROKEN:
1936 default:
1937 ret = ECONNABORTED;
1938 goto out;
1939
1940 case SCF_ERROR_DELETED:
1941 goto add;
1942
1943 case SCF_ERROR_NOT_SET:
1944 bad_fail("scf_pg_update", scf_error());
1945 }
1946 }
1947 }
1948
1949 out:
1950 scf_transaction_destroy_children(t);
1951 scf_transaction_destroy(t);
1952 scf_iter_destroy(iter);
1953 scf_property_destroy(prop);
1954 scf_pg_destroy(pg);
1955
1956 return (ret);
1957 }
1958
1959 int
restarter_rm_libs_loadable()1960 restarter_rm_libs_loadable()
1961 {
1962 void *libhndl;
1963
1964 if (method_context_safety)
1965 return (1);
1966
1967 if ((libhndl = dlopen("libpool.so", RTLD_LAZY | RTLD_LOCAL)) == NULL)
1968 return (0);
1969
1970 (void) dlclose(libhndl);
1971
1972 if ((libhndl = dlopen("libproject.so", RTLD_LAZY | RTLD_LOCAL)) == NULL)
1973 return (0);
1974
1975 (void) dlclose(libhndl);
1976
1977 method_context_safety = 1;
1978
1979 return (1);
1980 }
1981
1982 static int
get_astring_val(scf_propertygroup_t * pg,const char * name,char * buf,size_t bufsz,scf_property_t * prop,scf_value_t * val)1983 get_astring_val(scf_propertygroup_t *pg, const char *name, char *buf,
1984 size_t bufsz, scf_property_t *prop, scf_value_t *val)
1985 {
1986 ssize_t szret;
1987
1988 if (pg == NULL)
1989 return (-1);
1990
1991 if (scf_pg_get_property(pg, name, prop) != SCF_SUCCESS) {
1992 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
1993 uu_die(rcbroken);
1994 return (-1);
1995 }
1996
1997 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
1998 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
1999 uu_die(rcbroken);
2000 return (-1);
2001 }
2002
2003 szret = scf_value_get_astring(val, buf, bufsz);
2004
2005 return (szret >= 0 ? 0 : -1);
2006 }
2007
2008 static int
get_boolean_val(scf_propertygroup_t * pg,const char * name,uint8_t * b,scf_property_t * prop,scf_value_t * val)2009 get_boolean_val(scf_propertygroup_t *pg, const char *name, uint8_t *b,
2010 scf_property_t *prop, scf_value_t *val)
2011 {
2012 if (scf_pg_get_property(pg, name, prop) != SCF_SUCCESS) {
2013 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
2014 uu_die(rcbroken);
2015 return (-1);
2016 }
2017
2018 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
2019 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
2020 uu_die(rcbroken);
2021 return (-1);
2022 }
2023
2024 if (scf_value_get_boolean(val, b))
2025 return (-1);
2026
2027 return (0);
2028 }
2029
2030 /*
2031 * Try to load mcp->pwd, if it isn't already.
2032 * Fails with
2033 * ENOMEM - malloc() failed
2034 * ENOENT - no entry found
2035 * EIO - I/O error
2036 * EMFILE - process out of file descriptors
2037 * ENFILE - system out of file handles
2038 */
2039 static int
lookup_pwd(struct method_context * mcp)2040 lookup_pwd(struct method_context *mcp)
2041 {
2042 struct passwd *pwdp;
2043
2044 if (mcp->pwbuf != NULL && mcp->pwd.pw_uid == mcp->uid)
2045 return (0);
2046
2047 if (mcp->pwbuf == NULL) {
2048 mcp->pwbufsz = sysconf(_SC_GETPW_R_SIZE_MAX);
2049 assert(mcp->pwbufsz >= 0);
2050 mcp->pwbuf = malloc(mcp->pwbufsz);
2051 if (mcp->pwbuf == NULL)
2052 return (ENOMEM);
2053 }
2054
2055 do {
2056 errno = 0;
2057 pwdp = getpwuid_r(mcp->uid, &mcp->pwd, mcp->pwbuf,
2058 mcp->pwbufsz);
2059 } while (pwdp == NULL && errno == EINTR);
2060 if (pwdp != NULL)
2061 return (0);
2062
2063 free(mcp->pwbuf);
2064 mcp->pwbuf = NULL;
2065
2066 switch (errno) {
2067 case 0:
2068 default:
2069 /*
2070 * Until bug 5065780 is fixed, getpwuid_r() can fail with
2071 * ENOENT, particularly on the miniroot. Since the
2072 * documentation is inaccurate, we'll return ENOENT for unknown
2073 * errors.
2074 */
2075 return (ENOENT);
2076
2077 case EIO:
2078 case EMFILE:
2079 case ENFILE:
2080 return (errno);
2081
2082 case ERANGE:
2083 bad_fail("getpwuid_r", errno);
2084 /* NOTREACHED */
2085 }
2086 }
2087
2088 /*
2089 * Get the user id for str. Returns 0 on success or
2090 * ERANGE the uid is too big
2091 * EINVAL the string starts with a digit, but is not a valid uid
2092 * ENOMEM out of memory
2093 * ENOENT no passwd entry for str
2094 * EIO an I/O error has occurred
2095 * EMFILE/ENFILE out of file descriptors
2096 */
2097 int
get_uid(const char * str,struct method_context * ci,uid_t * uidp)2098 get_uid(const char *str, struct method_context *ci, uid_t *uidp)
2099 {
2100 if (isdigit(str[0])) {
2101 uid_t uid;
2102 char *cp;
2103
2104 errno = 0;
2105 uid = strtol(str, &cp, 10);
2106
2107 if (uid == 0 && errno != 0) {
2108 assert(errno != EINVAL);
2109 return (errno);
2110 }
2111
2112 for (; *cp != '\0'; ++cp)
2113 if (*cp != ' ' || *cp != '\t')
2114 return (EINVAL);
2115
2116 if (uid > UID_MAX)
2117 return (EINVAL);
2118
2119 *uidp = uid;
2120 return (0);
2121 } else {
2122 struct passwd *pwdp;
2123
2124 if (ci->pwbuf == NULL) {
2125 ci->pwbufsz = sysconf(_SC_GETPW_R_SIZE_MAX);
2126 ci->pwbuf = malloc(ci->pwbufsz);
2127 if (ci->pwbuf == NULL)
2128 return (ENOMEM);
2129 }
2130
2131 do {
2132 errno = 0;
2133 pwdp =
2134 getpwnam_r(str, &ci->pwd, ci->pwbuf, ci->pwbufsz);
2135 } while (pwdp == NULL && errno == EINTR);
2136
2137 if (pwdp != NULL) {
2138 *uidp = ci->pwd.pw_uid;
2139 return (0);
2140 } else {
2141 free(ci->pwbuf);
2142 ci->pwbuf = NULL;
2143 switch (errno) {
2144 case 0:
2145 return (ENOENT);
2146
2147 case ENOENT:
2148 case EIO:
2149 case EMFILE:
2150 case ENFILE:
2151 return (errno);
2152
2153 case ERANGE:
2154 default:
2155 bad_fail("getpwnam_r", errno);
2156 /* NOTREACHED */
2157 }
2158 }
2159 }
2160 }
2161
2162 gid_t
get_gid(const char * str)2163 get_gid(const char *str)
2164 {
2165 if (isdigit(str[0])) {
2166 gid_t gid;
2167 char *cp;
2168
2169 errno = 0;
2170 gid = strtol(str, &cp, 10);
2171
2172 if (gid == 0 && errno != 0)
2173 return ((gid_t)-1);
2174
2175 for (; *cp != '\0'; ++cp)
2176 if (*cp != ' ' || *cp != '\t')
2177 return ((gid_t)-1);
2178
2179 return (gid);
2180 } else {
2181 struct group grp, *ret;
2182 char *buffer;
2183 size_t buflen;
2184
2185 buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
2186 buffer = malloc(buflen);
2187 if (buffer == NULL)
2188 uu_die(allocfail);
2189
2190 errno = 0;
2191 ret = getgrnam_r(str, &grp, buffer, buflen);
2192 free(buffer);
2193
2194 return (ret == NULL ? (gid_t)-1 : grp.gr_gid);
2195 }
2196 }
2197
2198 /*
2199 * Fails with
2200 * ENOMEM - out of memory
2201 * ENOENT - no passwd entry
2202 * no project entry
2203 * EIO - an I/O error occurred
2204 * EMFILE - the process is out of file descriptors
2205 * ENFILE - the system is out of file handles
2206 * ERANGE - the project id is out of range
2207 * EINVAL - str is invalid
2208 * E2BIG - the project entry was too big
2209 * -1 - the name service switch is misconfigured
2210 */
2211 int
get_projid(const char * str,struct method_context * cip)2212 get_projid(const char *str, struct method_context *cip)
2213 {
2214 int ret;
2215 void *buf;
2216 const size_t bufsz = PROJECT_BUFSZ;
2217 struct project proj, *pp;
2218
2219 if (strcmp(str, ":default") == 0) {
2220 if (cip->uid == 0) {
2221 /* Don't change project for root services */
2222 cip->project = NULL;
2223 return (0);
2224 }
2225
2226 switch (ret = lookup_pwd(cip)) {
2227 case 0:
2228 break;
2229
2230 case ENOMEM:
2231 case ENOENT:
2232 case EIO:
2233 case EMFILE:
2234 case ENFILE:
2235 return (ret);
2236
2237 default:
2238 bad_fail("lookup_pwd", ret);
2239 }
2240
2241 buf = malloc(bufsz);
2242 if (buf == NULL)
2243 return (ENOMEM);
2244
2245 do {
2246 errno = 0;
2247 pp = getdefaultproj(cip->pwd.pw_name, &proj, buf,
2248 bufsz);
2249 } while (pp == NULL && errno == EINTR);
2250
2251 /* to be continued ... */
2252 } else {
2253 projid_t projid;
2254 char *cp;
2255
2256 if (!isdigit(str[0])) {
2257 cip->project = strdup(str);
2258 return (cip->project != NULL ? 0 : ENOMEM);
2259 }
2260
2261 errno = 0;
2262 projid = strtol(str, &cp, 10);
2263
2264 if (projid == 0 && errno != 0) {
2265 assert(errno == ERANGE);
2266 return (errno);
2267 }
2268
2269 for (; *cp != '\0'; ++cp)
2270 if (*cp != ' ' || *cp != '\t')
2271 return (EINVAL);
2272
2273 if (projid > MAXPROJID)
2274 return (ERANGE);
2275
2276 buf = malloc(bufsz);
2277 if (buf == NULL)
2278 return (ENOMEM);
2279
2280 do {
2281 errno = 0;
2282 pp = getprojbyid(projid, &proj, buf, bufsz);
2283 } while (pp == NULL && errno == EINTR);
2284 }
2285
2286 if (pp) {
2287 cip->project = strdup(pp->pj_name);
2288 free(buf);
2289 return (cip->project != NULL ? 0 : ENOMEM);
2290 }
2291
2292 free(buf);
2293
2294 switch (errno) {
2295 case 0:
2296 return (ENOENT);
2297
2298 case EIO:
2299 case EMFILE:
2300 case ENFILE:
2301 return (errno);
2302
2303 case ERANGE:
2304 return (E2BIG);
2305
2306 default:
2307 return (-1);
2308 }
2309 }
2310
2311 /*
2312 * Parse the supp_groups property value and populate ci->groups. Returns
2313 * EINVAL (get_gid() failed for one of the components), E2BIG (the property has
2314 * more than NGROUPS_MAX-1 groups), or 0 on success.
2315 */
2316 int
get_groups(char * str,struct method_context * ci)2317 get_groups(char *str, struct method_context *ci)
2318 {
2319 char *cp, *end, *next;
2320 uint_t i;
2321
2322 const char * const whitespace = " \t";
2323 const char * const illegal = ", \t";
2324
2325 if (str[0] == '\0') {
2326 ci->ngroups = 0;
2327 return (0);
2328 }
2329
2330 for (cp = str, i = 0; *cp != '\0'; ) {
2331 /* skip whitespace */
2332 cp += strspn(cp, whitespace);
2333
2334 /* find the end */
2335 end = cp + strcspn(cp, illegal);
2336
2337 /* skip whitespace after end */
2338 next = end + strspn(end, whitespace);
2339
2340 /* if there's a comma, it separates the fields */
2341 if (*next == ',')
2342 ++next;
2343
2344 *end = '\0';
2345
2346 if ((ci->groups[i] = get_gid(cp)) == (gid_t)-1) {
2347 ci->ngroups = 0;
2348 return (EINVAL);
2349 }
2350
2351 ++i;
2352 if (i > NGROUPS_MAX - 1) {
2353 ci->ngroups = 0;
2354 return (E2BIG);
2355 }
2356
2357 cp = next;
2358 }
2359
2360 ci->ngroups = i;
2361 return (0);
2362 }
2363
2364
2365 /*
2366 * Return an error message structure containing the error message
2367 * with context, and the error so the caller can make a decision
2368 * on what to do next.
2369 *
2370 * Because get_ids uses the mc_error_create() function which can
2371 * reallocate the merr, this function must return the merr pointer
2372 * in case it was reallocated.
2373 */
2374 static mc_error_t *
get_profile(scf_propertygroup_t * methpg,scf_propertygroup_t * instpg,scf_property_t * prop,scf_value_t * val,const char * cmdline,struct method_context * ci,mc_error_t * merr)2375 get_profile(scf_propertygroup_t *methpg, scf_propertygroup_t *instpg,
2376 scf_property_t *prop, scf_value_t *val, const char *cmdline,
2377 struct method_context *ci, mc_error_t *merr)
2378 {
2379 char *buf = ci->vbuf;
2380 ssize_t buf_sz = ci->vbuf_sz;
2381 char cmd[PATH_MAX];
2382 char *cp, *value;
2383 const char *cmdp;
2384 execattr_t *eap;
2385 mc_error_t *err = merr;
2386 int r;
2387
2388 if (!(get_astring_val(methpg, SCF_PROPERTY_PROFILE, buf, buf_sz, prop,
2389 val) == 0 || get_astring_val(instpg, SCF_PROPERTY_PROFILE, buf,
2390 buf_sz, prop, val) == 0))
2391 return (mc_error_create(merr, scf_error(),
2392 "Method context requires a profile, but the \"%s\" "
2393 "property could not be read. scf_error is %s",
2394 SCF_PROPERTY_PROFILE, scf_strerror(scf_error())));
2395
2396 /* Extract the command from the command line. */
2397 cp = strpbrk(cmdline, " \t");
2398
2399 if (cp == NULL) {
2400 cmdp = cmdline;
2401 } else {
2402 (void) strncpy(cmd, cmdline, cp - cmdline);
2403 cmd[cp - cmdline] = '\0';
2404 cmdp = cmd;
2405 }
2406
2407 /* Require that cmdp[0] == '/'? */
2408
2409 eap = getexecprof(buf, KV_COMMAND, cmdp, GET_ONE);
2410 if (eap == NULL)
2411 return (mc_error_create(merr, ENOENT,
2412 "Could not find the execution profile \"%s\", "
2413 "command %s.", buf, cmdp));
2414
2415 /* Based on pfexec.c */
2416
2417 /* Get the euid first so we don't override ci->pwd for the uid. */
2418 if ((value = kva_match(eap->attr, EXECATTR_EUID_KW)) != NULL) {
2419 if ((r = get_uid(value, ci, &ci->euid)) != 0) {
2420 ci->euid = (uid_t)-1;
2421 err = mc_error_create(merr, r,
2422 "Could not interpret profile euid value \"%s\", "
2423 "from the execution profile \"%s\", error %d.",
2424 value, buf, r);
2425 goto out;
2426 }
2427 }
2428
2429 if ((value = kva_match(eap->attr, EXECATTR_UID_KW)) != NULL) {
2430 if ((r = get_uid(value, ci, &ci->uid)) != 0) {
2431 ci->euid = ci->uid = (uid_t)-1;
2432 err = mc_error_create(merr, r,
2433 "Could not interpret profile uid value \"%s\", "
2434 "from the execution profile \"%s\", error %d.",
2435 value, buf, r);
2436 goto out;
2437 }
2438 ci->euid = ci->uid;
2439 }
2440
2441 if ((value = kva_match(eap->attr, EXECATTR_GID_KW)) != NULL) {
2442 ci->egid = ci->gid = get_gid(value);
2443 if (ci->gid == (gid_t)-1) {
2444 err = mc_error_create(merr, EINVAL,
2445 "Could not interpret profile gid value \"%s\", "
2446 "from the execution profile \"%s\".", value, buf);
2447 goto out;
2448 }
2449 }
2450
2451 if ((value = kva_match(eap->attr, EXECATTR_EGID_KW)) != NULL) {
2452 ci->egid = get_gid(value);
2453 if (ci->egid == (gid_t)-1) {
2454 err = mc_error_create(merr, EINVAL,
2455 "Could not interpret profile egid value \"%s\", "
2456 "from the execution profile \"%s\".", value, buf);
2457 goto out;
2458 }
2459 }
2460
2461 if ((value = kva_match(eap->attr, EXECATTR_LPRIV_KW)) != NULL) {
2462 ci->lpriv_set = priv_str_to_set(value, ",", NULL);
2463 if (ci->lpriv_set == NULL) {
2464 if (errno != EINVAL)
2465 err = mc_error_create(merr, ENOMEM,
2466 ALLOCFAIL);
2467 else
2468 err = mc_error_create(merr, EINVAL,
2469 "Could not interpret profile "
2470 "limitprivs value \"%s\", from "
2471 "the execution profile \"%s\".",
2472 value, buf);
2473 goto out;
2474 }
2475 }
2476
2477 if ((value = kva_match(eap->attr, EXECATTR_IPRIV_KW)) != NULL) {
2478 ci->priv_set = priv_str_to_set(value, ",", NULL);
2479 if (ci->priv_set == NULL) {
2480 if (errno != EINVAL)
2481 err = mc_error_create(merr, ENOMEM,
2482 ALLOCFAIL);
2483 else
2484 err = mc_error_create(merr, EINVAL,
2485 "Could not interpret profile privs value "
2486 "\"%s\", from the execution profile "
2487 "\"%s\".", value, buf);
2488 goto out;
2489 }
2490 }
2491
2492 out:
2493 free_execattr(eap);
2494
2495 return (err);
2496 }
2497
2498 /*
2499 * Return an error message structure containing the error message
2500 * with context, and the error so the caller can make a decision
2501 * on what to do next.
2502 *
2503 * Because get_ids uses the mc_error_create() function which can
2504 * reallocate the merr, this function must return the merr pointer
2505 * in case it was reallocated.
2506 */
2507 static mc_error_t *
get_ids(scf_propertygroup_t * methpg,scf_propertygroup_t * instpg,scf_property_t * prop,scf_value_t * val,struct method_context * ci,mc_error_t * merr)2508 get_ids(scf_propertygroup_t *methpg, scf_propertygroup_t *instpg,
2509 scf_property_t *prop, scf_value_t *val, struct method_context *ci,
2510 mc_error_t *merr)
2511 {
2512 char *vbuf = ci->vbuf;
2513 ssize_t vbuf_sz = ci->vbuf_sz;
2514 int r;
2515
2516 /*
2517 * This should never happen because the caller should fall through
2518 * another path of just setting the ids to defaults, instead of
2519 * attempting to get the ids here.
2520 */
2521 if (methpg == NULL && instpg == NULL)
2522 return (mc_error_create(merr, ENOENT,
2523 "No property groups to get ids from."));
2524
2525 if (!(get_astring_val(methpg, SCF_PROPERTY_USER,
2526 vbuf, vbuf_sz, prop, val) == 0 || get_astring_val(instpg,
2527 SCF_PROPERTY_USER, vbuf, vbuf_sz, prop,
2528 val) == 0))
2529 return (mc_error_create(merr, ENOENT,
2530 "Could not get \"%s\" property.", SCF_PROPERTY_USER));
2531
2532 if ((r = get_uid(vbuf, ci, &ci->uid)) != 0) {
2533 ci->uid = (uid_t)-1;
2534 return (mc_error_create(merr, r,
2535 "Could not interpret \"%s\" property value \"%s\", "
2536 "error %d.", SCF_PROPERTY_USER, vbuf, r));
2537 }
2538
2539 if (!(get_astring_val(methpg, SCF_PROPERTY_GROUP, vbuf, vbuf_sz, prop,
2540 val) == 0 || get_astring_val(instpg, SCF_PROPERTY_GROUP, vbuf,
2541 vbuf_sz, prop, val) == 0)) {
2542 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2543 (void) strcpy(vbuf, ":default");
2544 } else {
2545 return (mc_error_create(merr, ENOENT,
2546 "Could not get \"%s\" property.",
2547 SCF_PROPERTY_GROUP));
2548 }
2549 }
2550
2551 if (strcmp(vbuf, ":default") != 0) {
2552 ci->gid = get_gid(vbuf);
2553 if (ci->gid == (gid_t)-1) {
2554 return (mc_error_create(merr, ENOENT,
2555 "Could not interpret \"%s\" property value \"%s\".",
2556 SCF_PROPERTY_GROUP, vbuf));
2557 }
2558 } else {
2559 switch (r = lookup_pwd(ci)) {
2560 case 0:
2561 ci->gid = ci->pwd.pw_gid;
2562 break;
2563
2564 case ENOENT:
2565 ci->gid = (gid_t)-1;
2566 return (mc_error_create(merr, ENOENT,
2567 "No passwd entry for uid \"%d\".", ci->uid));
2568
2569 case ENOMEM:
2570 return (mc_error_create(merr, ENOMEM,
2571 "Out of memory."));
2572
2573 case EIO:
2574 case EMFILE:
2575 case ENFILE:
2576 return (mc_error_create(merr, ENFILE,
2577 "getpwuid_r() failed, error %d.", r));
2578
2579 default:
2580 bad_fail("lookup_pwd", r);
2581 }
2582 }
2583
2584 if (!(get_astring_val(methpg, SCF_PROPERTY_SUPP_GROUPS, vbuf, vbuf_sz,
2585 prop, val) == 0 || get_astring_val(instpg,
2586 SCF_PROPERTY_SUPP_GROUPS, vbuf, vbuf_sz, prop, val) == 0)) {
2587 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2588 (void) strcpy(vbuf, ":default");
2589 } else {
2590 return (mc_error_create(merr, ENOENT,
2591 "Could not get supplemental groups (\"%s\") "
2592 "property.", SCF_PROPERTY_SUPP_GROUPS));
2593 }
2594 }
2595
2596 if (strcmp(vbuf, ":default") != 0) {
2597 switch (r = get_groups(vbuf, ci)) {
2598 case 0:
2599 break;
2600
2601 case EINVAL:
2602 return (mc_error_create(merr, EINVAL,
2603 "Could not interpret supplemental groups (\"%s\") "
2604 "property value \"%s\".", SCF_PROPERTY_SUPP_GROUPS,
2605 vbuf));
2606
2607 case E2BIG:
2608 return (mc_error_create(merr, E2BIG,
2609 "Too many supplemental groups values in \"%s\".",
2610 vbuf));
2611
2612 default:
2613 bad_fail("get_groups", r);
2614 }
2615 } else {
2616 ci->ngroups = -1;
2617 }
2618
2619 if (!(get_astring_val(methpg, SCF_PROPERTY_PRIVILEGES, vbuf, vbuf_sz,
2620 prop, val) == 0 || get_astring_val(instpg, SCF_PROPERTY_PRIVILEGES,
2621 vbuf, vbuf_sz, prop, val) == 0)) {
2622 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2623 (void) strcpy(vbuf, ":default");
2624 } else {
2625 return (mc_error_create(merr, ENOENT,
2626 "Could not get \"%s\" property.",
2627 SCF_PROPERTY_PRIVILEGES));
2628 }
2629 }
2630
2631 /*
2632 * For default privs, we need to keep priv_set == NULL, as
2633 * we use this test elsewhere.
2634 */
2635 if (strcmp(vbuf, ":default") != 0) {
2636 ci->priv_set = priv_str_to_set(vbuf, ",", NULL);
2637 if (ci->priv_set == NULL) {
2638 if (errno != EINVAL) {
2639 return (mc_error_create(merr, ENOMEM,
2640 ALLOCFAIL));
2641 } else {
2642 return (mc_error_create(merr, EINVAL,
2643 "Could not interpret \"%s\" "
2644 "property value \"%s\".",
2645 SCF_PROPERTY_PRIVILEGES, vbuf));
2646 }
2647 }
2648 }
2649
2650 if (!(get_astring_val(methpg, SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf,
2651 vbuf_sz, prop, val) == 0 || get_astring_val(instpg,
2652 SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf, vbuf_sz, prop, val) == 0)) {
2653 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2654 (void) strcpy(vbuf, ":default");
2655 } else {
2656 return (mc_error_create(merr, ENOENT,
2657 "Could not get \"%s\" property.",
2658 SCF_PROPERTY_LIMIT_PRIVILEGES));
2659 }
2660 }
2661
2662 if (strcmp(vbuf, ":default") == 0)
2663 /*
2664 * L must default to all privileges so root NPA services see
2665 * iE = all. "zone" is all privileges available in the current
2666 * zone, equivalent to "all" in the global zone.
2667 */
2668 (void) strcpy(vbuf, "zone");
2669
2670 ci->lpriv_set = priv_str_to_set(vbuf, ",", NULL);
2671 if (ci->lpriv_set == NULL) {
2672 if (errno != EINVAL) {
2673 return (mc_error_create(merr, ENOMEM, ALLOCFAIL));
2674 } else {
2675 return (mc_error_create(merr, EINVAL,
2676 "Could not interpret \"%s\" property value \"%s\".",
2677 SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf));
2678 }
2679 }
2680
2681 return (merr);
2682 }
2683
2684 static int
get_environment(scf_handle_t * h,scf_propertygroup_t * pg,struct method_context * mcp,scf_property_t * prop,scf_value_t * val)2685 get_environment(scf_handle_t *h, scf_propertygroup_t *pg,
2686 struct method_context *mcp, scf_property_t *prop, scf_value_t *val)
2687 {
2688 scf_iter_t *iter;
2689 scf_type_t type;
2690 size_t i = 0;
2691 int ret;
2692
2693 if (scf_pg_get_property(pg, SCF_PROPERTY_ENVIRONMENT, prop) != 0) {
2694 if (scf_error() == SCF_ERROR_NOT_FOUND)
2695 return (ENOENT);
2696 return (scf_error());
2697 }
2698 if (scf_property_type(prop, &type) != 0)
2699 return (scf_error());
2700 if (type != SCF_TYPE_ASTRING)
2701 return (EINVAL);
2702 if ((iter = scf_iter_create(h)) == NULL)
2703 return (scf_error());
2704
2705 if (scf_iter_property_values(iter, prop) != 0) {
2706 ret = scf_error();
2707 scf_iter_destroy(iter);
2708 return (ret);
2709 }
2710
2711 mcp->env_sz = 10;
2712
2713 if ((mcp->env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz)) == NULL) {
2714 ret = ENOMEM;
2715 goto out;
2716 }
2717
2718 while ((ret = scf_iter_next_value(iter, val)) == 1) {
2719 ret = scf_value_get_as_string(val, mcp->vbuf, mcp->vbuf_sz);
2720 if (ret == -1) {
2721 ret = scf_error();
2722 goto out;
2723 }
2724
2725 if ((mcp->env[i] = strdup(mcp->vbuf)) == NULL) {
2726 ret = ENOMEM;
2727 goto out;
2728 }
2729
2730 if (++i == mcp->env_sz) {
2731 char **env;
2732 mcp->env_sz *= 2;
2733 env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz);
2734 if (env == NULL) {
2735 ret = ENOMEM;
2736 goto out;
2737 }
2738 (void) memcpy(env, mcp->env,
2739 sizeof (*mcp->env) * (mcp->env_sz / 2));
2740 free(mcp->env);
2741 mcp->env = env;
2742 }
2743 }
2744
2745 if (ret == -1)
2746 ret = scf_error();
2747
2748 out:
2749 scf_iter_destroy(iter);
2750 return (ret);
2751 }
2752
2753 /*
2754 * Fetch method context information from the repository, allocate and fill
2755 * a method_context structure, return it in *mcpp, and return NULL.
2756 *
2757 * If no method_context is defined, original init context is provided, where
2758 * the working directory is '/', and uid/gid are 0/0. But if a method_context
2759 * is defined at any level the smf_method(7) method_context defaults are used.
2760 *
2761 * Return an error message structure containing the error message
2762 * with context, and the error so the caller can make a decision
2763 * on what to do next.
2764 *
2765 * Error Types :
2766 * E2BIG Too many values or entry is too big
2767 * EINVAL Invalid value
2768 * EIO an I/O error has occured
2769 * ENOENT no entry for value
2770 * ENOMEM out of memory
2771 * ENOTSUP Version mismatch
2772 * ERANGE value is out of range
2773 * EMFILE/ENFILE out of file descriptors
2774 *
2775 * SCF_ERROR_BACKEND_ACCESS
2776 * SCF_ERROR_CONNECTION_BROKEN
2777 * SCF_ERROR_DELETED
2778 * SCF_ERROR_CONSTRAINT_VIOLATED
2779 * SCF_ERROR_HANDLE_DESTROYED
2780 * SCF_ERROR_INTERNAL
2781 * SCF_ERROR_INVALID_ARGUMENT
2782 * SCF_ERROR_NO_MEMORY
2783 * SCF_ERROR_NO_RESOURCES
2784 * SCF_ERROR_NOT_BOUND
2785 * SCF_ERROR_NOT_FOUND
2786 * SCF_ERROR_NOT_SET
2787 * SCF_ERROR_TYPE_MISMATCH
2788 *
2789 */
2790 mc_error_t *
restarter_get_method_context(uint_t version,scf_instance_t * inst,scf_snapshot_t * snap,const char * mname,const char * cmdline,struct method_context ** mcpp)2791 restarter_get_method_context(uint_t version, scf_instance_t *inst,
2792 scf_snapshot_t *snap, const char *mname, const char *cmdline,
2793 struct method_context **mcpp)
2794 {
2795 scf_handle_t *h;
2796 scf_propertygroup_t *methpg = NULL;
2797 scf_propertygroup_t *instpg = NULL;
2798 scf_propertygroup_t *pg = NULL;
2799 scf_property_t *prop = NULL;
2800 scf_value_t *val = NULL;
2801 scf_type_t ty;
2802 uint8_t use_profile;
2803 int ret = 0;
2804 mc_error_t *err = NULL;
2805 struct method_context *cip;
2806 enum {
2807 MCF_FOUND = 1 << 0,
2808 MCF_ENVIRONMENT = 1 << 1,
2809 MCF_PROFILE = 1 << 2,
2810 MCF_IDS = 1 << 3,
2811 MCF_CWD = 1 << 4,
2812 MCF_SECFLAGS = 1 << 5,
2813 MCF_COREFILE = 1 << 6,
2814 MCF_PROJECT = 1 << 7,
2815 MCF_RESPOOL = 1 << 8,
2816 } mc_flags = 0;
2817
2818 if ((err = malloc(sizeof (mc_error_t))) == NULL)
2819 return (mc_error_create(NULL, ENOMEM, NULL));
2820
2821 /* Set the type to zero to track if an error occured. */
2822 err->type = 0;
2823
2824 if (version != RESTARTER_METHOD_CONTEXT_VERSION)
2825 return (mc_error_create(err, ENOTSUP,
2826 "Invalid client version %d. (Expected %d)",
2827 version, RESTARTER_METHOD_CONTEXT_VERSION));
2828
2829 /* Get the handle before we allocate anything. */
2830 h = scf_instance_handle(inst);
2831 if (h == NULL)
2832 return (mc_error_create(err, scf_error(),
2833 scf_strerror(scf_error())));
2834
2835 cip = malloc(sizeof (*cip));
2836 if (cip == NULL)
2837 return (mc_error_create(err, ENOMEM, ALLOCFAIL));
2838
2839 (void) memset(cip, 0, sizeof (*cip));
2840 cip->uid = (uid_t)-1;
2841 cip->euid = (uid_t)-1;
2842 cip->gid = (gid_t)-1;
2843 cip->egid = (gid_t)-1;
2844
2845 cip->vbuf_sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
2846 assert(cip->vbuf_sz >= 0);
2847 cip->vbuf = malloc(cip->vbuf_sz);
2848 if (cip->vbuf == NULL) {
2849 free(cip);
2850 return (mc_error_create(err, ENOMEM, ALLOCFAIL));
2851 }
2852
2853 if ((instpg = scf_pg_create(h)) == NULL ||
2854 (methpg = scf_pg_create(h)) == NULL ||
2855 (prop = scf_property_create(h)) == NULL ||
2856 (val = scf_value_create(h)) == NULL) {
2857 err = mc_error_create(err, scf_error(),
2858 "Failed to create repository object: %s",
2859 scf_strerror(scf_error()));
2860 goto out;
2861 }
2862
2863 /*
2864 * The method environment, and the credentials/profile data,
2865 * may be found either in the pg for the method (methpg),
2866 * or in the instance/service SCF_PG_METHOD_CONTEXT pg (named
2867 * instpg below).
2868 */
2869
2870 if (scf_instance_get_pg_composed(inst, snap, mname, methpg) !=
2871 SCF_SUCCESS) {
2872 err = mc_error_create(err, scf_error(), "Unable to get the "
2873 "\"%s\" method, %s", mname, scf_strerror(scf_error()));
2874 goto out;
2875 }
2876
2877 if (scf_instance_get_pg_composed(inst, snap, SCF_PG_METHOD_CONTEXT,
2878 instpg) != SCF_SUCCESS) {
2879 if (scf_error() != SCF_ERROR_NOT_FOUND) {
2880 err = mc_error_create(err, scf_error(),
2881 "Unable to retrieve the \"%s\" property group, %s",
2882 SCF_PG_METHOD_CONTEXT, scf_strerror(scf_error()));
2883 goto out;
2884 }
2885 scf_pg_destroy(instpg);
2886 instpg = NULL;
2887 } else {
2888 /* Found a method_context */
2889 mc_flags |= MCF_FOUND;
2890 }
2891
2892 ret = get_environment(h, methpg, cip, prop, val);
2893 if (ret == ENOENT && instpg != NULL) {
2894 ret = get_environment(h, instpg, cip, prop, val);
2895 }
2896
2897 switch (ret) {
2898 case 0:
2899 mc_flags |= MCF_ENVIRONMENT;
2900 break;
2901 case ENOENT:
2902 break;
2903 case ENOMEM:
2904 err = mc_error_create(err, ret, "Out of memory.");
2905 goto out;
2906 case EINVAL:
2907 err = mc_error_create(err, ret, "Invalid method environment.");
2908 goto out;
2909 default:
2910 err = mc_error_create(err, ret,
2911 "Get method environment failed: %s", scf_strerror(ret));
2912 goto out;
2913 }
2914
2915 pg = methpg;
2916
2917 ret = scf_pg_get_property(pg, SCF_PROPERTY_USE_PROFILE, prop);
2918 if (ret && scf_error() == SCF_ERROR_NOT_FOUND && instpg != NULL) {
2919 pg = NULL;
2920 ret = scf_pg_get_property(instpg, SCF_PROPERTY_USE_PROFILE,
2921 prop);
2922 }
2923
2924 if (ret) {
2925 switch (scf_error()) {
2926 case SCF_ERROR_NOT_FOUND:
2927 /* No profile context: use default credentials */
2928 cip->uid = 0;
2929 cip->gid = 0;
2930 break;
2931
2932 case SCF_ERROR_CONNECTION_BROKEN:
2933 err = mc_error_create(err, SCF_ERROR_CONNECTION_BROKEN,
2934 RCBROKEN);
2935 goto out;
2936
2937 case SCF_ERROR_DELETED:
2938 err = mc_error_create(err, SCF_ERROR_NOT_FOUND,
2939 "Could not find property group \"%s\"",
2940 pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
2941 goto out;
2942
2943 case SCF_ERROR_HANDLE_MISMATCH:
2944 case SCF_ERROR_INVALID_ARGUMENT:
2945 case SCF_ERROR_NOT_SET:
2946 default:
2947 bad_fail("scf_pg_get_property", scf_error());
2948 }
2949 } else {
2950 if (scf_property_type(prop, &ty) != SCF_SUCCESS) {
2951 ret = scf_error();
2952 switch (ret) {
2953 case SCF_ERROR_CONNECTION_BROKEN:
2954 err = mc_error_create(err,
2955 SCF_ERROR_CONNECTION_BROKEN, RCBROKEN);
2956 break;
2957
2958 case SCF_ERROR_DELETED:
2959 err = mc_error_create(err,
2960 SCF_ERROR_NOT_FOUND,
2961 "Could not find property group \"%s\"",
2962 pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
2963 break;
2964
2965 case SCF_ERROR_NOT_SET:
2966 default:
2967 bad_fail("scf_property_type", ret);
2968 }
2969
2970 goto out;
2971 }
2972
2973 if (ty != SCF_TYPE_BOOLEAN) {
2974 err = mc_error_create(err,
2975 SCF_ERROR_TYPE_MISMATCH,
2976 "\"%s\" property is not boolean in property group "
2977 "\"%s\".", SCF_PROPERTY_USE_PROFILE,
2978 pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
2979 goto out;
2980 }
2981
2982 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
2983 ret = scf_error();
2984 switch (ret) {
2985 case SCF_ERROR_CONNECTION_BROKEN:
2986 err = mc_error_create(err,
2987 SCF_ERROR_CONNECTION_BROKEN, RCBROKEN);
2988 break;
2989
2990 case SCF_ERROR_CONSTRAINT_VIOLATED:
2991 err = mc_error_create(err,
2992 SCF_ERROR_CONSTRAINT_VIOLATED,
2993 "\"%s\" property has multiple values.",
2994 SCF_PROPERTY_USE_PROFILE);
2995 break;
2996
2997 case SCF_ERROR_NOT_FOUND:
2998 err = mc_error_create(err,
2999 SCF_ERROR_NOT_FOUND,
3000 "\"%s\" property has no values.",
3001 SCF_PROPERTY_USE_PROFILE);
3002 break;
3003 default:
3004 bad_fail("scf_property_get_value", ret);
3005 }
3006
3007 goto out;
3008 }
3009
3010 ret = scf_value_get_boolean(val, &use_profile);
3011 assert(ret == SCF_SUCCESS);
3012
3013 /* get ids & privileges */
3014 if (use_profile) {
3015 mc_flags |= MCF_PROFILE;
3016 err = get_profile(pg, instpg, prop, val, cmdline,
3017 cip, err);
3018 } else {
3019 mc_flags |= MCF_IDS;
3020 err = get_ids(pg, instpg, prop, val, cip, err);
3021 }
3022
3023 if (err->type != 0)
3024 goto out;
3025 }
3026
3027 /* get working directory */
3028 if ((methpg != NULL && scf_pg_get_property(methpg,
3029 SCF_PROPERTY_WORKING_DIRECTORY, prop) == SCF_SUCCESS) ||
3030 (instpg != NULL && scf_pg_get_property(instpg,
3031 SCF_PROPERTY_WORKING_DIRECTORY, prop) == SCF_SUCCESS)) {
3032 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3033 ret = scf_error();
3034 switch (ret) {
3035 case SCF_ERROR_CONNECTION_BROKEN:
3036 err = mc_error_create(err, ret, RCBROKEN);
3037 break;
3038
3039 case SCF_ERROR_CONSTRAINT_VIOLATED:
3040 err = mc_error_create(err, ret,
3041 "\"%s\" property has multiple values.",
3042 SCF_PROPERTY_WORKING_DIRECTORY);
3043 break;
3044
3045 case SCF_ERROR_NOT_FOUND:
3046 err = mc_error_create(err, ret,
3047 "\"%s\" property has no values.",
3048 SCF_PROPERTY_WORKING_DIRECTORY);
3049 break;
3050
3051 default:
3052 bad_fail("scf_property_get_value", ret);
3053 }
3054
3055 goto out;
3056 }
3057
3058 mc_flags |= MCF_CWD;
3059 ret = scf_value_get_astring(val, cip->vbuf, cip->vbuf_sz);
3060 assert(ret != -1);
3061 } else {
3062 ret = scf_error();
3063 switch (ret) {
3064 case SCF_ERROR_NOT_FOUND:
3065 /* okay if missing. */
3066 (void) strcpy(cip->vbuf, ":default");
3067 break;
3068
3069 case SCF_ERROR_CONNECTION_BROKEN:
3070 err = mc_error_create(err, ret, RCBROKEN);
3071 goto out;
3072
3073 case SCF_ERROR_DELETED:
3074 err = mc_error_create(err, ret,
3075 "Property group could not be found");
3076 goto out;
3077
3078 case SCF_ERROR_HANDLE_MISMATCH:
3079 case SCF_ERROR_INVALID_ARGUMENT:
3080 case SCF_ERROR_NOT_SET:
3081 default:
3082 bad_fail("scf_pg_get_property", ret);
3083 }
3084 }
3085
3086 if (strcmp(cip->vbuf, ":default") == 0 ||
3087 strcmp(cip->vbuf, ":home") == 0) {
3088 switch (ret = lookup_pwd(cip)) {
3089 case 0:
3090 break;
3091
3092 case ENOMEM:
3093 err = mc_error_create(err, ret, "Out of memory.");
3094 goto out;
3095
3096 case ENOENT:
3097 case EIO:
3098 case EMFILE:
3099 case ENFILE:
3100 err = mc_error_create(err, ret,
3101 "Could not get passwd entry.");
3102 goto out;
3103
3104 default:
3105 bad_fail("lookup_pwd", ret);
3106 }
3107
3108 cip->working_dir = strdup(cip->pwd.pw_dir);
3109 if (cip->working_dir == NULL) {
3110 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3111 goto out;
3112 }
3113 } else {
3114 cip->working_dir = strdup(cip->vbuf);
3115 if (cip->working_dir == NULL) {
3116 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3117 goto out;
3118 }
3119 }
3120
3121 /* get security flags */
3122 if ((methpg != NULL && scf_pg_get_property(methpg,
3123 SCF_PROPERTY_SECFLAGS, prop) == SCF_SUCCESS) ||
3124 (instpg != NULL && scf_pg_get_property(instpg,
3125 SCF_PROPERTY_SECFLAGS, prop) == SCF_SUCCESS)) {
3126 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3127 ret = scf_error();
3128 switch (ret) {
3129 case SCF_ERROR_CONNECTION_BROKEN:
3130 err = mc_error_create(err, ret, RCBROKEN);
3131 break;
3132
3133 case SCF_ERROR_CONSTRAINT_VIOLATED:
3134 err = mc_error_create(err, ret,
3135 "\"%s\" property has multiple values.",
3136 SCF_PROPERTY_SECFLAGS);
3137 break;
3138
3139 case SCF_ERROR_NOT_FOUND:
3140 err = mc_error_create(err, ret,
3141 "\"%s\" property has no values.",
3142 SCF_PROPERTY_SECFLAGS);
3143 break;
3144
3145 default:
3146 bad_fail("scf_property_get_value", ret);
3147 }
3148
3149 (void) strlcpy(cip->vbuf, ":default", cip->vbuf_sz);
3150 } else {
3151 ret = scf_value_get_astring(val, cip->vbuf,
3152 cip->vbuf_sz);
3153 assert(ret != -1);
3154 }
3155 mc_flags |= MCF_SECFLAGS;
3156 } else {
3157 ret = scf_error();
3158 switch (ret) {
3159 case SCF_ERROR_NOT_FOUND:
3160 /* okay if missing. */
3161 (void) strlcpy(cip->vbuf, ":default", cip->vbuf_sz);
3162 break;
3163
3164 case SCF_ERROR_CONNECTION_BROKEN:
3165 err = mc_error_create(err, ret, RCBROKEN);
3166 goto out;
3167
3168 case SCF_ERROR_DELETED:
3169 err = mc_error_create(err, ret,
3170 "Property group could not be found");
3171 goto out;
3172
3173 case SCF_ERROR_HANDLE_MISMATCH:
3174 case SCF_ERROR_INVALID_ARGUMENT:
3175 case SCF_ERROR_NOT_SET:
3176 default:
3177 bad_fail("scf_pg_get_property", ret);
3178 }
3179 }
3180
3181
3182 if (scf_default_secflags(h, &cip->def_secflags) != 0) {
3183 err = mc_error_create(err, EINVAL, "couldn't fetch "
3184 "default security-flags");
3185 goto out;
3186 }
3187
3188 if (strcmp(cip->vbuf, ":default") != 0) {
3189 if (secflags_parse(NULL, cip->vbuf,
3190 &cip->secflag_delta) != 0) {
3191 err = mc_error_create(err, EINVAL, "couldn't parse "
3192 "security flags: %s", cip->vbuf);
3193 goto out;
3194 }
3195 }
3196
3197 /* get (optional) corefile pattern */
3198 if ((methpg != NULL && scf_pg_get_property(methpg,
3199 SCF_PROPERTY_COREFILE_PATTERN, prop) == SCF_SUCCESS) ||
3200 (instpg != NULL && scf_pg_get_property(instpg,
3201 SCF_PROPERTY_COREFILE_PATTERN, prop) == SCF_SUCCESS)) {
3202 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3203 ret = scf_error();
3204 switch (ret) {
3205 case SCF_ERROR_CONNECTION_BROKEN:
3206 err = mc_error_create(err, ret, RCBROKEN);
3207 break;
3208
3209 case SCF_ERROR_CONSTRAINT_VIOLATED:
3210 err = mc_error_create(err, ret,
3211 "\"%s\" property has multiple values.",
3212 SCF_PROPERTY_COREFILE_PATTERN);
3213 break;
3214
3215 case SCF_ERROR_NOT_FOUND:
3216 err = mc_error_create(err, ret,
3217 "\"%s\" property has no values.",
3218 SCF_PROPERTY_COREFILE_PATTERN);
3219 break;
3220
3221 default:
3222 bad_fail("scf_property_get_value", ret);
3223 }
3224
3225 } else {
3226
3227 ret = scf_value_get_astring(val, cip->vbuf,
3228 cip->vbuf_sz);
3229 assert(ret != -1);
3230
3231 cip->corefile_pattern = strdup(cip->vbuf);
3232 if (cip->corefile_pattern == NULL) {
3233 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3234 goto out;
3235 }
3236 }
3237
3238 mc_flags |= MCF_COREFILE;
3239 } else {
3240 ret = scf_error();
3241 switch (ret) {
3242 case SCF_ERROR_NOT_FOUND:
3243 /* okay if missing. */
3244 break;
3245
3246 case SCF_ERROR_CONNECTION_BROKEN:
3247 err = mc_error_create(err, ret, RCBROKEN);
3248 goto out;
3249
3250 case SCF_ERROR_DELETED:
3251 err = mc_error_create(err, ret,
3252 "Property group could not be found");
3253 goto out;
3254
3255 case SCF_ERROR_HANDLE_MISMATCH:
3256 case SCF_ERROR_INVALID_ARGUMENT:
3257 case SCF_ERROR_NOT_SET:
3258 default:
3259 bad_fail("scf_pg_get_property", ret);
3260 }
3261 }
3262
3263 if (restarter_rm_libs_loadable()) {
3264 /* get project */
3265 if ((methpg != NULL && scf_pg_get_property(methpg,
3266 SCF_PROPERTY_PROJECT, prop) == SCF_SUCCESS) ||
3267 (instpg != NULL && scf_pg_get_property(instpg,
3268 SCF_PROPERTY_PROJECT, prop) == SCF_SUCCESS)) {
3269 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3270 ret = scf_error();
3271 switch (ret) {
3272 case SCF_ERROR_CONNECTION_BROKEN:
3273 err = mc_error_create(err, ret,
3274 RCBROKEN);
3275 break;
3276
3277 case SCF_ERROR_CONSTRAINT_VIOLATED:
3278 err = mc_error_create(err, ret,
3279 "\"%s\" property has multiple "
3280 "values.", SCF_PROPERTY_PROJECT);
3281 break;
3282
3283 case SCF_ERROR_NOT_FOUND:
3284 err = mc_error_create(err, ret,
3285 "\"%s\" property has no values.",
3286 SCF_PROPERTY_PROJECT);
3287 break;
3288
3289 default:
3290 bad_fail("scf_property_get_value", ret);
3291 }
3292
3293 (void) strcpy(cip->vbuf, ":default");
3294 } else {
3295 ret = scf_value_get_astring(val, cip->vbuf,
3296 cip->vbuf_sz);
3297 assert(ret != -1);
3298 }
3299
3300 mc_flags |= MCF_PROJECT;
3301 } else {
3302 (void) strcpy(cip->vbuf, ":default");
3303 }
3304
3305 switch (ret = get_projid(cip->vbuf, cip)) {
3306 case 0:
3307 break;
3308
3309 case ENOMEM:
3310 err = mc_error_create(err, ret, "Out of memory.");
3311 goto out;
3312
3313 case ENOENT:
3314 err = mc_error_create(err, ret,
3315 "Missing passwd or project entry for \"%s\".",
3316 cip->vbuf);
3317 goto out;
3318
3319 case EIO:
3320 err = mc_error_create(err, ret, "I/O error.");
3321 goto out;
3322
3323 case EMFILE:
3324 case ENFILE:
3325 err = mc_error_create(err, ret,
3326 "Out of file descriptors.");
3327 goto out;
3328
3329 case -1:
3330 err = mc_error_create(err, ret,
3331 "Name service switch is misconfigured.");
3332 goto out;
3333
3334 case ERANGE:
3335 case E2BIG:
3336 err = mc_error_create(err, ret,
3337 "Project ID \"%s\" too big.", cip->vbuf);
3338 goto out;
3339
3340 case EINVAL:
3341 err = mc_error_create(err, ret,
3342 "Project ID \"%s\" is invalid.", cip->vbuf);
3343 goto out;
3344
3345 default:
3346 bad_fail("get_projid", ret);
3347 }
3348
3349 /* get resource pool */
3350 if ((methpg != NULL && scf_pg_get_property(methpg,
3351 SCF_PROPERTY_RESOURCE_POOL, prop) == SCF_SUCCESS) ||
3352 (instpg != NULL && scf_pg_get_property(instpg,
3353 SCF_PROPERTY_RESOURCE_POOL, prop) == SCF_SUCCESS)) {
3354 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3355 ret = scf_error();
3356 switch (ret) {
3357 case SCF_ERROR_CONNECTION_BROKEN:
3358 err = mc_error_create(err, ret,
3359 RCBROKEN);
3360 break;
3361
3362 case SCF_ERROR_CONSTRAINT_VIOLATED:
3363 err = mc_error_create(err, ret,
3364 "\"%s\" property has multiple "
3365 "values.",
3366 SCF_PROPERTY_RESOURCE_POOL);
3367 break;
3368
3369 case SCF_ERROR_NOT_FOUND:
3370 err = mc_error_create(err, ret,
3371 "\"%s\" property has no "
3372 "values.",
3373 SCF_PROPERTY_RESOURCE_POOL);
3374 break;
3375
3376 default:
3377 bad_fail("scf_property_get_value", ret);
3378 }
3379
3380 (void) strcpy(cip->vbuf, ":default");
3381 } else {
3382 ret = scf_value_get_astring(val, cip->vbuf,
3383 cip->vbuf_sz);
3384 assert(ret != -1);
3385 }
3386
3387 mc_flags |= MCF_RESPOOL;
3388 } else {
3389 ret = scf_error();
3390 switch (ret) {
3391 case SCF_ERROR_NOT_FOUND:
3392 /* okay if missing. */
3393 (void) strcpy(cip->vbuf, ":default");
3394 break;
3395
3396 case SCF_ERROR_CONNECTION_BROKEN:
3397 err = mc_error_create(err, ret, RCBROKEN);
3398 goto out;
3399
3400 case SCF_ERROR_DELETED:
3401 err = mc_error_create(err, ret,
3402 "property group could not be found.");
3403 goto out;
3404
3405 case SCF_ERROR_HANDLE_MISMATCH:
3406 case SCF_ERROR_INVALID_ARGUMENT:
3407 case SCF_ERROR_NOT_SET:
3408 default:
3409 bad_fail("scf_pg_get_property", ret);
3410 }
3411 }
3412
3413 if (strcmp(cip->vbuf, ":default") != 0) {
3414 cip->resource_pool = strdup(cip->vbuf);
3415 if (cip->resource_pool == NULL) {
3416 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3417 goto out;
3418 }
3419 }
3420 }
3421
3422 if ((mc_flags & ~MCF_FOUND) == 0) {
3423 /*
3424 * A method_context was not used for any configurable
3425 * elements or attributes, so reset and use the simple
3426 * defaults that provide historic init behavior.
3427 */
3428 free(cip->pwbuf);
3429 free(cip->vbuf);
3430 free(cip->working_dir);
3431
3432 (void) memset(cip, 0, sizeof (*cip));
3433 cip->uid = 0;
3434 cip->gid = 0;
3435 cip->euid = (uid_t)-1;
3436 cip->egid = (gid_t)-1;
3437
3438 if (scf_default_secflags(h, &cip->def_secflags) != 0) {
3439 err = mc_error_create(err, EINVAL, "couldn't fetch "
3440 "default security-flags");
3441 goto out;
3442 }
3443 } else if ((mc_flags & (MCF_PROFILE | MCF_IDS | MCF_CWD)) == 0) {
3444 /*
3445 * A method context was used to configure some elements, but
3446 * not for any of profile/user/group/working_dir.
3447 * Reset the directory to '/' for historic init behaviour.
3448 */
3449 free(cip->working_dir);
3450 cip->working_dir = NULL;
3451 }
3452
3453 *mcpp = cip;
3454
3455 out:
3456 (void) scf_value_destroy(val);
3457 scf_property_destroy(prop);
3458 scf_pg_destroy(instpg);
3459 scf_pg_destroy(methpg);
3460
3461 if (cip->pwbuf != NULL) {
3462 free(cip->pwbuf);
3463 cip->pwbuf = NULL;
3464 }
3465
3466 free(cip->vbuf);
3467
3468 if (err->type != 0) {
3469 restarter_free_method_context(cip);
3470 } else {
3471 restarter_mc_error_destroy(err);
3472 err = NULL;
3473 }
3474
3475 return (err);
3476 }
3477
3478 /*
3479 * Modify the current process per the given method_context. On success, returns
3480 * 0. Note that the environment is not modified by this function to include the
3481 * environment variables in cip->env.
3482 *
3483 * On failure, sets *fp to NULL or the name of the function which failed,
3484 * and returns one of the following error codes. The words in parentheses are
3485 * the values to which *fp may be set for the error case.
3486 * ENOMEM - malloc() failed
3487 * EIO - an I/O error occurred (getpwuid_r, chdir)
3488 * EMFILE - process is out of file descriptors (getpwuid_r)
3489 * ENFILE - system is out of file handles (getpwuid_r)
3490 * EINVAL - gid or egid is out of range (setregid)
3491 * ngroups is too big (setgroups)
3492 * project's project id is bad (setproject)
3493 * uid or euid is out of range (setreuid)
3494 * poolname is invalid (pool_set_binding)
3495 * EPERM - insufficient privilege (setregid, initgroups, setgroups, setppriv,
3496 * setproject, setreuid, settaskid)
3497 * ENOENT - uid has a passwd entry but no shadow entry
3498 * working_dir does not exist (chdir)
3499 * uid has no passwd entry
3500 * the pool could not be found (pool_set_binding)
3501 * EFAULT - lpriv_set or priv_set has a bad address (setppriv)
3502 * working_dir has a bad address (chdir)
3503 * EACCES - could not access working_dir (chdir)
3504 * in a TASK_FINAL task (setproject, settaskid)
3505 * no resource pool accepting default binding exists (setproject)
3506 * ELOOP - too many symbolic links in working_dir (chdir)
3507 * ENAMETOOLONG - working_dir is too long (chdir)
3508 * ENOLINK - working_dir is on an inaccessible remote machine (chdir)
3509 * ENOTDIR - working_dir is not a directory (chdir)
3510 * ESRCH - uid is not a user of project (setproject)
3511 * project is invalid (setproject)
3512 * the resource pool specified for project is unknown (setproject)
3513 * EBADF - the configuration for the pool is invalid (pool_set_binding)
3514 * -1 - core_set_process_path() failed (core_set_process_path)
3515 * a resource control assignment failed (setproject)
3516 * a system error occurred during pool_set_binding (pool_set_binding)
3517 */
3518 int
restarter_set_method_context(struct method_context * cip,const char ** fp)3519 restarter_set_method_context(struct method_context *cip, const char **fp)
3520 {
3521 pid_t mypid = -1;
3522 int r, ret;
3523
3524 cip->pwbuf = NULL;
3525 *fp = NULL;
3526
3527 if (cip->gid != (gid_t)-1) {
3528 if (setregid(cip->gid,
3529 cip->egid != (gid_t)-1 ? cip->egid : cip->gid) != 0) {
3530 *fp = "setregid";
3531
3532 ret = errno;
3533 assert(ret == EINVAL || ret == EPERM);
3534 goto out;
3535 }
3536 } else {
3537 if (cip->pwbuf == NULL) {
3538 switch (ret = lookup_pwd(cip)) {
3539 case 0:
3540 break;
3541
3542 case ENOMEM:
3543 case ENOENT:
3544 *fp = NULL;
3545 goto out;
3546
3547 case EIO:
3548 case EMFILE:
3549 case ENFILE:
3550 *fp = "getpwuid_r";
3551 goto out;
3552
3553 default:
3554 bad_fail("lookup_pwd", ret);
3555 }
3556 }
3557
3558 if (setregid(cip->pwd.pw_gid,
3559 cip->egid != (gid_t)-1 ?
3560 cip->egid : cip->pwd.pw_gid) != 0) {
3561 *fp = "setregid";
3562
3563 ret = errno;
3564 assert(ret == EINVAL || ret == EPERM);
3565 goto out;
3566 }
3567 }
3568
3569 if (cip->ngroups == -1) {
3570 if (cip->pwbuf == NULL) {
3571 switch (ret = lookup_pwd(cip)) {
3572 case 0:
3573 break;
3574
3575 case ENOMEM:
3576 case ENOENT:
3577 *fp = NULL;
3578 goto out;
3579
3580 case EIO:
3581 case EMFILE:
3582 case ENFILE:
3583 *fp = "getpwuid_r";
3584 goto out;
3585
3586 default:
3587 bad_fail("lookup_pwd", ret);
3588 }
3589 }
3590
3591 /* Ok if cip->gid == -1 */
3592 if (initgroups(cip->pwd.pw_name, cip->gid) != 0) {
3593 *fp = "initgroups";
3594 ret = errno;
3595 assert(ret == EPERM);
3596 goto out;
3597 }
3598 } else if (cip->ngroups > 0 &&
3599 setgroups(cip->ngroups, cip->groups) != 0) {
3600 *fp = "setgroups";
3601
3602 ret = errno;
3603 assert(ret == EINVAL || ret == EPERM);
3604 goto out;
3605 }
3606
3607 if (cip->corefile_pattern != NULL) {
3608 mypid = getpid();
3609
3610 if (core_set_process_path(cip->corefile_pattern,
3611 strlen(cip->corefile_pattern) + 1, mypid) != 0) {
3612 *fp = "core_set_process_path";
3613 ret = -1;
3614 goto out;
3615 }
3616 }
3617
3618
3619 if (psecflags(P_PID, P_MYID, PSF_INHERIT,
3620 &cip->def_secflags.ss_default) != 0) {
3621 *fp = "psecflags (default inherit)";
3622 ret = errno;
3623 goto out;
3624 }
3625
3626 if (psecflags(P_PID, P_MYID, PSF_LOWER,
3627 &cip->def_secflags.ss_lower) != 0) {
3628 *fp = "psecflags (default lower)";
3629 ret = errno;
3630 goto out;
3631 }
3632
3633 if (psecflags(P_PID, P_MYID, PSF_UPPER,
3634 &cip->def_secflags.ss_upper) != 0) {
3635 *fp = "psecflags (default upper)";
3636 ret = errno;
3637 goto out;
3638 }
3639
3640 if (psecflags(P_PID, P_MYID, PSF_INHERIT,
3641 &cip->secflag_delta) != 0) {
3642 *fp = "psecflags (from manifest)";
3643 ret = errno;
3644 goto out;
3645 }
3646
3647 if (restarter_rm_libs_loadable()) {
3648 if (cip->project == NULL) {
3649 if (settaskid(getprojid(), TASK_NORMAL) == -1) {
3650 switch (errno) {
3651 case EACCES:
3652 case EPERM:
3653 *fp = "settaskid";
3654 ret = errno;
3655 goto out;
3656
3657 case EINVAL:
3658 default:
3659 bad_fail("settaskid", errno);
3660 }
3661 }
3662 } else {
3663 switch (ret = lookup_pwd(cip)) {
3664 case 0:
3665 break;
3666
3667 case ENOMEM:
3668 case ENOENT:
3669 *fp = NULL;
3670 goto out;
3671
3672 case EIO:
3673 case EMFILE:
3674 case ENFILE:
3675 *fp = "getpwuid_r";
3676 goto out;
3677
3678 default:
3679 bad_fail("lookup_pwd", ret);
3680 }
3681
3682 *fp = "setproject";
3683
3684 switch (setproject(cip->project, cip->pwd.pw_name,
3685 TASK_NORMAL)) {
3686 case 0:
3687 break;
3688
3689 case SETPROJ_ERR_TASK:
3690 case SETPROJ_ERR_POOL:
3691 ret = errno;
3692 goto out;
3693
3694 default:
3695 ret = -1;
3696 goto out;
3697 }
3698 }
3699
3700 if (cip->resource_pool != NULL) {
3701 if (mypid == -1)
3702 mypid = getpid();
3703
3704 *fp = "pool_set_binding";
3705
3706 if (pool_set_binding(cip->resource_pool, P_PID,
3707 mypid) != PO_SUCCESS) {
3708 switch (pool_error()) {
3709 case POE_INVALID_SEARCH:
3710 ret = ENOENT;
3711 break;
3712
3713 case POE_BADPARAM:
3714 ret = EINVAL;
3715 break;
3716
3717 case POE_INVALID_CONF:
3718 ret = EBADF;
3719 break;
3720
3721 case POE_SYSTEM:
3722 ret = -1;
3723 break;
3724
3725 default:
3726 bad_fail("pool_set_binding",
3727 pool_error());
3728 }
3729
3730 goto out;
3731 }
3732 }
3733 }
3734
3735 /*
3736 * Now, we have to assume our ID. If the UID is 0, we want it to be
3737 * privilege-aware, otherwise the limit set gets used instead of E/P.
3738 * We can do this by setting P as well, which keeps
3739 * PA status (see priv_can_clear_PA()).
3740 */
3741
3742 *fp = "setppriv";
3743
3744 if (cip->lpriv_set != NULL) {
3745 if (setppriv(PRIV_SET, PRIV_LIMIT, cip->lpriv_set) != 0) {
3746 ret = errno;
3747 assert(ret == EFAULT || ret == EPERM);
3748 goto out;
3749 }
3750 }
3751 if (cip->priv_set != NULL) {
3752 if (setppriv(PRIV_SET, PRIV_INHERITABLE, cip->priv_set) != 0) {
3753 ret = errno;
3754 assert(ret == EFAULT || ret == EPERM);
3755 goto out;
3756 }
3757 }
3758
3759 /*
3760 * If the limit privset is already set, then must be privilege
3761 * aware. Otherwise, don't assume anything, and force privilege
3762 * aware status.
3763 */
3764
3765 if (cip->lpriv_set == NULL && cip->priv_set != NULL) {
3766 ret = setpflags(PRIV_AWARE, 1);
3767 assert(ret == 0);
3768 }
3769
3770 *fp = "setreuid";
3771 if (setreuid(cip->uid,
3772 cip->euid != (uid_t)-1 ? cip->euid : cip->uid) != 0) {
3773 ret = errno;
3774 assert(ret == EINVAL || ret == EPERM);
3775 goto out;
3776 }
3777
3778 *fp = "setppriv";
3779 if (cip->priv_set != NULL) {
3780 if (setppriv(PRIV_SET, PRIV_PERMITTED, cip->priv_set) != 0) {
3781 ret = errno;
3782 assert(ret == EFAULT || ret == EPERM);
3783 goto out;
3784 }
3785 }
3786
3787 /*
3788 * The last thing to do is chdir to the specified working directory.
3789 * This should come after the uid switching as only the user might
3790 * have access to the specified directory.
3791 */
3792 if (cip->working_dir != NULL) {
3793 do {
3794 r = chdir(cip->working_dir);
3795 } while (r != 0 && errno == EINTR);
3796 if (r != 0) {
3797 *fp = "chdir";
3798 ret = errno;
3799 goto out;
3800 }
3801 }
3802
3803 ret = 0;
3804 out:
3805 free(cip->pwbuf);
3806 cip->pwbuf = NULL;
3807 return (ret);
3808 }
3809
3810 void
restarter_free_method_context(struct method_context * mcp)3811 restarter_free_method_context(struct method_context *mcp)
3812 {
3813 size_t i;
3814
3815 if (mcp->lpriv_set != NULL)
3816 priv_freeset(mcp->lpriv_set);
3817 if (mcp->priv_set != NULL)
3818 priv_freeset(mcp->priv_set);
3819
3820 if (mcp->env != NULL) {
3821 for (i = 0; i < mcp->env_sz; i++)
3822 free(mcp->env[i]);
3823 free(mcp->env);
3824 }
3825
3826 free(mcp->working_dir);
3827 free(mcp->corefile_pattern);
3828 free(mcp->project);
3829 free(mcp->resource_pool);
3830 free(mcp);
3831 }
3832
3833 /*
3834 * Method keyword functions
3835 */
3836
3837 int
restarter_is_null_method(const char * meth)3838 restarter_is_null_method(const char *meth)
3839 {
3840 return (strcmp(meth, MKW_TRUE) == 0);
3841 }
3842
3843 static int
is_kill_method(const char * method,const char * kill_str,size_t kill_str_len)3844 is_kill_method(const char *method, const char *kill_str,
3845 size_t kill_str_len)
3846 {
3847 const char *cp;
3848 int sig;
3849
3850 if (strncmp(method, kill_str, kill_str_len) != 0 ||
3851 (method[kill_str_len] != '\0' &&
3852 !isspace(method[kill_str_len])))
3853 return (-1);
3854
3855 cp = method + kill_str_len;
3856 while (*cp != '\0' && isspace(*cp))
3857 ++cp;
3858
3859 if (*cp == '\0')
3860 return (SIGTERM);
3861
3862 if (*cp != '-')
3863 return (-1);
3864
3865 return (str2sig(cp + 1, &sig) == 0 ? sig : -1);
3866 }
3867
3868 int
restarter_is_kill_proc_method(const char * method)3869 restarter_is_kill_proc_method(const char *method)
3870 {
3871 return (is_kill_method(method, MKW_KILL_PROC,
3872 sizeof (MKW_KILL_PROC) - 1));
3873 }
3874
3875 int
restarter_is_kill_method(const char * method)3876 restarter_is_kill_method(const char *method)
3877 {
3878 return (is_kill_method(method, MKW_KILL, sizeof (MKW_KILL) - 1));
3879 }
3880
3881 /*
3882 * Stubs for now.
3883 */
3884
3885 /* ARGSUSED */
3886 int
restarter_event_get_enabled(restarter_event_t * e)3887 restarter_event_get_enabled(restarter_event_t *e)
3888 {
3889 return (-1);
3890 }
3891
3892 /* ARGSUSED */
3893 uint64_t
restarter_event_get_seq(restarter_event_t * e)3894 restarter_event_get_seq(restarter_event_t *e)
3895 {
3896 return (-1);
3897 }
3898
3899 /* ARGSUSED */
3900 void
restarter_event_get_time(restarter_event_t * e,hrtime_t * time)3901 restarter_event_get_time(restarter_event_t *e, hrtime_t *time)
3902 {
3903 }
3904
3905 /*
3906 * Check for and validate fmri specified in restarter_actions/auxiliary_fmri
3907 * 0 - Success
3908 * 1 - Failure
3909 */
3910 int
restarter_inst_validate_ractions_aux_fmri(scf_instance_t * inst)3911 restarter_inst_validate_ractions_aux_fmri(scf_instance_t *inst)
3912 {
3913 scf_handle_t *h;
3914 scf_propertygroup_t *pg;
3915 scf_property_t *prop;
3916 scf_value_t *val;
3917 char *aux_fmri;
3918 size_t size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
3919 int ret = 1;
3920
3921 if ((aux_fmri = malloc(size)) == NULL)
3922 return (1);
3923
3924 h = scf_instance_handle(inst);
3925
3926 pg = scf_pg_create(h);
3927 prop = scf_property_create(h);
3928 val = scf_value_create(h);
3929 if (pg == NULL || prop == NULL || val == NULL)
3930 goto out;
3931
3932 if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
3933 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
3934 pg) != SCF_SUCCESS)
3935 goto out;
3936
3937 if (get_astring_val(pg, SCF_PROPERTY_AUX_FMRI, aux_fmri, size,
3938 prop, val) != SCF_SUCCESS)
3939 goto out;
3940
3941 if (scf_parse_fmri(aux_fmri, NULL, NULL, NULL, NULL, NULL,
3942 NULL) != SCF_SUCCESS)
3943 goto out;
3944
3945 ret = 0;
3946
3947 out:
3948 free(aux_fmri);
3949 scf_value_destroy(val);
3950 scf_property_destroy(prop);
3951 scf_pg_destroy(pg);
3952 return (ret);
3953 }
3954
3955 /*
3956 * Get instance's boolean value in restarter_actions/auxiliary_tty
3957 * Return -1 on failure
3958 */
3959 int
restarter_inst_ractions_from_tty(scf_instance_t * inst)3960 restarter_inst_ractions_from_tty(scf_instance_t *inst)
3961 {
3962 scf_handle_t *h;
3963 scf_propertygroup_t *pg;
3964 scf_property_t *prop;
3965 scf_value_t *val;
3966 uint8_t has_tty;
3967 int ret = -1;
3968
3969 h = scf_instance_handle(inst);
3970 pg = scf_pg_create(h);
3971 prop = scf_property_create(h);
3972 val = scf_value_create(h);
3973 if (pg == NULL || prop == NULL || val == NULL)
3974 goto out;
3975
3976 if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
3977 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
3978 pg) != SCF_SUCCESS)
3979 goto out;
3980
3981 if (get_boolean_val(pg, SCF_PROPERTY_AUX_TTY, &has_tty, prop,
3982 val) != SCF_SUCCESS)
3983 goto out;
3984
3985 ret = has_tty;
3986
3987 out:
3988 scf_value_destroy(val);
3989 scf_property_destroy(prop);
3990 scf_pg_destroy(pg);
3991 return (ret);
3992 }
3993
3994 /*
3995 * If the instance's dump-on-restart property exists, remove it and return true,
3996 * otherwise return false.
3997 */
3998 int
restarter_inst_dump(scf_instance_t * inst)3999 restarter_inst_dump(scf_instance_t *inst)
4000 {
4001 scf_handle_t *h;
4002 scf_propertygroup_t *pg;
4003 scf_property_t *prop;
4004 scf_value_t *val;
4005 int ret = 0;
4006
4007 h = scf_instance_handle(inst);
4008 pg = scf_pg_create(h);
4009 prop = scf_property_create(h);
4010 val = scf_value_create(h);
4011 if (pg == NULL || prop == NULL || val == NULL)
4012 goto out;
4013
4014 if (scf_instance_get_pg(inst, SCF_PG_RESTARTER_ACTIONS, pg) !=
4015 SCF_SUCCESS) {
4016 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
4017 uu_die(rcbroken);
4018 goto out;
4019 }
4020
4021 if (scf_pg_get_property(pg, SCF_PROPERTY_DODUMP, prop) != SCF_SUCCESS) {
4022 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
4023 uu_die(rcbroken);
4024 goto out;
4025 }
4026
4027 ret = 1;
4028
4029 if (scf_instance_delete_prop(inst, SCF_PG_RESTARTER_ACTIONS,
4030 SCF_PROPERTY_DODUMP) != SCF_SUCCESS) {
4031 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
4032 uu_die(rcbroken);
4033 goto out;
4034 }
4035
4036 out:
4037 scf_value_destroy(val);
4038 scf_property_destroy(prop);
4039 scf_pg_destroy(pg);
4040 return (ret);
4041 }
4042
4043 static int
restarter_inst_set_astring_prop(scf_instance_t * inst,const char * pgname,const char * pgtype,uint32_t pgflags,const char * pname,const char * str)4044 restarter_inst_set_astring_prop(scf_instance_t *inst, const char *pgname,
4045 const char *pgtype, uint32_t pgflags, const char *pname, const char *str)
4046 {
4047 scf_handle_t *h;
4048 scf_propertygroup_t *pg;
4049 scf_transaction_t *t;
4050 scf_transaction_entry_t *e;
4051 scf_value_t *v;
4052 int ret = 1, r;
4053
4054 h = scf_instance_handle(inst);
4055
4056 pg = scf_pg_create(h);
4057 t = scf_transaction_create(h);
4058 e = scf_entry_create(h);
4059 v = scf_value_create(h);
4060 if (pg == NULL || t == NULL || e == NULL || v == NULL)
4061 goto out;
4062
4063 if (instance_get_or_add_pg(inst, pgname, pgtype, pgflags, pg))
4064 goto out;
4065
4066 if (scf_value_set_astring(v, str) != SCF_SUCCESS)
4067 goto out;
4068
4069 for (;;) {
4070 if (scf_transaction_start(t, pg) != 0)
4071 goto out;
4072
4073 if (tx_set_value(t, e, pname, SCF_TYPE_ASTRING, v) != 0)
4074 goto out;
4075
4076 if ((r = scf_transaction_commit(t)) == 1)
4077 break;
4078
4079 if (r == -1)
4080 goto out;
4081
4082 scf_transaction_reset(t);
4083 if (scf_pg_update(pg) == -1)
4084 goto out;
4085 }
4086 ret = 0;
4087
4088 out:
4089 scf_transaction_destroy(t);
4090 scf_entry_destroy(e);
4091 scf_value_destroy(v);
4092 scf_pg_destroy(pg);
4093
4094 return (ret);
4095 }
4096
4097 int
restarter_inst_set_aux_fmri(scf_instance_t * inst)4098 restarter_inst_set_aux_fmri(scf_instance_t *inst)
4099 {
4100 scf_handle_t *h;
4101 scf_propertygroup_t *pg;
4102 scf_property_t *prop;
4103 scf_value_t *val;
4104 char *aux_fmri;
4105 size_t size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
4106 int ret = 1;
4107
4108 if ((aux_fmri = malloc(size)) == NULL)
4109 return (1);
4110
4111 h = scf_instance_handle(inst);
4112
4113 pg = scf_pg_create(h);
4114 prop = scf_property_create(h);
4115 val = scf_value_create(h);
4116 if (pg == NULL || prop == NULL || val == NULL)
4117 goto out;
4118
4119 /*
4120 * Get auxiliary_fmri value from restarter_actions pg
4121 */
4122 if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
4123 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
4124 pg) != SCF_SUCCESS)
4125 goto out;
4126
4127 if (get_astring_val(pg, SCF_PROPERTY_AUX_FMRI, aux_fmri, size,
4128 prop, val) != SCF_SUCCESS)
4129 goto out;
4130
4131 /*
4132 * Populate restarter/auxiliary_fmri with the obtained fmri.
4133 */
4134 ret = restarter_inst_set_astring_prop(inst, SCF_PG_RESTARTER,
4135 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS,
4136 SCF_PROPERTY_AUX_FMRI, aux_fmri);
4137
4138 out:
4139 free(aux_fmri);
4140 scf_value_destroy(val);
4141 scf_property_destroy(prop);
4142 scf_pg_destroy(pg);
4143 return (ret);
4144 }
4145
4146 int
restarter_inst_reset_aux_fmri(scf_instance_t * inst)4147 restarter_inst_reset_aux_fmri(scf_instance_t *inst)
4148 {
4149 return (scf_instance_delete_prop(inst,
4150 SCF_PG_RESTARTER, SCF_PROPERTY_AUX_FMRI));
4151 }
4152
4153 int
restarter_inst_reset_ractions_aux_fmri(scf_instance_t * inst)4154 restarter_inst_reset_ractions_aux_fmri(scf_instance_t *inst)
4155 {
4156 return (scf_instance_delete_prop(inst,
4157 SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_FMRI));
4158 }
4159