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