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