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