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