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