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