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