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