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