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