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