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 2007 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 case SCF_ERROR_PERMISSION_DENIED: 1073 default: 1074 bad_fail("scf_iter_next_value", 1075 scf_error()); 1076 } 1077 } 1078 1079 if (ret == 1) { 1080 ret = scf_value_get_count(val, &c); 1081 assert(ret == 0); 1082 1083 if (c != contract_id) { 1084 ret = scf_entry_add_value(t_cid, 1085 val); 1086 assert(ret == 0); 1087 } else { 1088 scf_value_destroy(val); 1089 } 1090 1091 goto next_val; 1092 } 1093 1094 scf_value_destroy(val); 1095 } else { 1096 switch (scf_error()) { 1097 case SCF_ERROR_CONNECTION_BROKEN: 1098 default: 1099 ret = ECONNABORTED; 1100 goto remove_contract_cleanup; 1101 1102 case SCF_ERROR_TYPE_MISMATCH: 1103 break; 1104 1105 case SCF_ERROR_INVALID_ARGUMENT: 1106 case SCF_ERROR_NOT_SET: 1107 bad_fail("scf_property_is_type", 1108 scf_error()); 1109 } 1110 } 1111 } else { 1112 switch (scf_error()) { 1113 case SCF_ERROR_CONNECTION_BROKEN: 1114 default: 1115 ret = ECONNABORTED; 1116 goto remove_contract_cleanup; 1117 1118 case SCF_ERROR_DELETED: 1119 scf_entry_destroy(t_cid); 1120 goto add; 1121 1122 case SCF_ERROR_NOT_FOUND: 1123 break; 1124 1125 case SCF_ERROR_HANDLE_MISMATCH: 1126 case SCF_ERROR_INVALID_ARGUMENT: 1127 case SCF_ERROR_NOT_SET: 1128 bad_fail("scf_pg_get_property", scf_error()); 1129 } 1130 1131 new: 1132 if (scf_transaction_property_new(t, t_cid, pname, 1133 SCF_TYPE_COUNT) != 0) { 1134 switch (scf_error()) { 1135 case SCF_ERROR_CONNECTION_BROKEN: 1136 default: 1137 ret = ECONNABORTED; 1138 goto remove_contract_cleanup; 1139 1140 case SCF_ERROR_DELETED: 1141 scf_entry_destroy(t_cid); 1142 goto add; 1143 1144 case SCF_ERROR_EXISTS: 1145 goto replace; 1146 1147 case SCF_ERROR_HANDLE_MISMATCH: 1148 case SCF_ERROR_INVALID_ARGUMENT: 1149 case SCF_ERROR_NOT_SET: 1150 bad_fail("scf_transaction_property_new", 1151 scf_error()); 1152 } 1153 } 1154 } 1155 1156 ret = scf_transaction_commit(t); 1157 if (ret == -1) { 1158 switch (scf_error()) { 1159 case SCF_ERROR_CONNECTION_BROKEN: 1160 default: 1161 ret = ECONNABORTED; 1162 goto remove_contract_cleanup; 1163 1164 case SCF_ERROR_DELETED: 1165 goto add; 1166 1167 case SCF_ERROR_PERMISSION_DENIED: 1168 ret = EPERM; 1169 goto remove_contract_cleanup; 1170 1171 case SCF_ERROR_BACKEND_ACCESS: 1172 ret = EACCES; 1173 goto remove_contract_cleanup; 1174 1175 case SCF_ERROR_BACKEND_READONLY: 1176 ret = EROFS; 1177 goto remove_contract_cleanup; 1178 1179 case SCF_ERROR_NOT_SET: 1180 bad_fail("scf_transaction_commit", scf_error()); 1181 } 1182 } 1183 if (ret == 1) { 1184 ret = 0; 1185 break; 1186 } 1187 1188 scf_transaction_destroy_children(t); 1189 if (scf_pg_update(pg) == -1) { 1190 switch (scf_error()) { 1191 case SCF_ERROR_CONNECTION_BROKEN: 1192 default: 1193 ret = ECONNABORTED; 1194 goto remove_contract_cleanup; 1195 1196 case SCF_ERROR_DELETED: 1197 goto add; 1198 1199 case SCF_ERROR_NOT_SET: 1200 bad_fail("scf_pg_update", scf_error()); 1201 } 1202 } 1203 } 1204 1205 remove_contract_cleanup: 1206 scf_transaction_destroy_children(t); 1207 scf_transaction_destroy(t); 1208 scf_iter_destroy(iter); 1209 scf_property_destroy(prop); 1210 scf_pg_destroy(pg); 1211 1212 return (ret); 1213 } 1214 1215 /* 1216 * Fails with 1217 * EINVAL - type is invalid 1218 * ENOMEM 1219 * ECONNABORTED - repository disconnection 1220 * EBADF - s_inst is not set 1221 * ECANCELED - s_inst is deleted 1222 * EPERM 1223 * EACCES 1224 * EROFS 1225 */ 1226 int 1227 restarter_store_contract(scf_instance_t *s_inst, ctid_t contract_id, 1228 restarter_contract_type_t type) 1229 { 1230 scf_handle_t *h; 1231 scf_transaction_t *t = NULL; 1232 scf_transaction_entry_t *t_cid = NULL; 1233 scf_value_t *val; 1234 scf_propertygroup_t *pg = NULL; 1235 scf_property_t *prop = NULL; 1236 scf_iter_t *iter = NULL; 1237 const char *pname; 1238 int ret = 0, primary; 1239 1240 if (type == RESTARTER_CONTRACT_PRIMARY) 1241 primary = 1; 1242 else if (type == RESTARTER_CONTRACT_TRANSIENT) 1243 primary = 0; 1244 else 1245 return (EINVAL); 1246 1247 h = scf_instance_handle(s_inst); 1248 1249 pg = scf_pg_create(h); 1250 prop = scf_property_create(h); 1251 iter = scf_iter_create(h); 1252 t = scf_transaction_create(h); 1253 1254 if (pg == NULL || prop == NULL || iter == NULL || t == NULL) { 1255 ret = ENOMEM; 1256 goto out; 1257 } 1258 1259 add: 1260 scf_transaction_destroy_children(t); 1261 ret = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER, 1262 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg); 1263 if (ret != 0) 1264 goto out; 1265 1266 pname = primary ? SCF_PROPERTY_CONTRACT : 1267 SCF_PROPERTY_TRANSIENT_CONTRACT; 1268 1269 for (;;) { 1270 if (scf_transaction_start(t, pg) != 0) { 1271 switch (scf_error()) { 1272 case SCF_ERROR_CONNECTION_BROKEN: 1273 default: 1274 ret = ECONNABORTED; 1275 goto out; 1276 1277 case SCF_ERROR_DELETED: 1278 goto add; 1279 1280 case SCF_ERROR_PERMISSION_DENIED: 1281 ret = EPERM; 1282 goto out; 1283 1284 case SCF_ERROR_BACKEND_ACCESS: 1285 ret = EACCES; 1286 goto out; 1287 1288 case SCF_ERROR_BACKEND_READONLY: 1289 ret = EROFS; 1290 goto out; 1291 1292 case SCF_ERROR_HANDLE_MISMATCH: 1293 case SCF_ERROR_IN_USE: 1294 case SCF_ERROR_NOT_SET: 1295 bad_fail("scf_transaction_start", scf_error()); 1296 } 1297 } 1298 1299 t_cid = scf_entry_create(h); 1300 if (t_cid == NULL) { 1301 ret = ENOMEM; 1302 goto out; 1303 } 1304 1305 if (scf_pg_get_property(pg, pname, prop) == 0) { 1306 replace: 1307 if (scf_transaction_property_change_type(t, t_cid, 1308 pname, SCF_TYPE_COUNT) != 0) { 1309 switch (scf_error()) { 1310 case SCF_ERROR_CONNECTION_BROKEN: 1311 default: 1312 ret = ECONNABORTED; 1313 goto out; 1314 1315 case SCF_ERROR_DELETED: 1316 scf_entry_destroy(t_cid); 1317 goto add; 1318 1319 case SCF_ERROR_NOT_FOUND: 1320 goto new; 1321 1322 case SCF_ERROR_HANDLE_MISMATCH: 1323 case SCF_ERROR_INVALID_ARGUMENT: 1324 case SCF_ERROR_IN_USE: 1325 case SCF_ERROR_NOT_SET: 1326 bad_fail( 1327 "scf_transaction_propert_change_type", 1328 scf_error()); 1329 } 1330 } 1331 1332 if (scf_property_is_type(prop, SCF_TYPE_COUNT) == 0) { 1333 if (scf_iter_property_values(iter, prop) != 0) { 1334 switch (scf_error()) { 1335 case SCF_ERROR_CONNECTION_BROKEN: 1336 default: 1337 ret = ECONNABORTED; 1338 goto out; 1339 1340 case SCF_ERROR_NOT_SET: 1341 case SCF_ERROR_HANDLE_MISMATCH: 1342 bad_fail( 1343 "scf_iter_property_values", 1344 scf_error()); 1345 } 1346 } 1347 1348 next_val: 1349 val = scf_value_create(h); 1350 if (val == NULL) { 1351 assert(scf_error() == 1352 SCF_ERROR_NO_MEMORY); 1353 ret = ENOMEM; 1354 goto out; 1355 } 1356 1357 ret = scf_iter_next_value(iter, val); 1358 if (ret == -1) { 1359 switch (scf_error()) { 1360 case SCF_ERROR_CONNECTION_BROKEN: 1361 default: 1362 ret = ECONNABORTED; 1363 goto out; 1364 1365 case SCF_ERROR_DELETED: 1366 scf_value_destroy(val); 1367 goto add; 1368 1369 case SCF_ERROR_HANDLE_MISMATCH: 1370 case SCF_ERROR_INVALID_ARGUMENT: 1371 case SCF_ERROR_PERMISSION_DENIED: 1372 bad_fail( 1373 "scf_iter_next_value", 1374 scf_error()); 1375 } 1376 } 1377 1378 if (ret == 1) { 1379 ret = scf_entry_add_value(t_cid, val); 1380 assert(ret == 0); 1381 1382 goto next_val; 1383 } 1384 1385 scf_value_destroy(val); 1386 } else { 1387 switch (scf_error()) { 1388 case SCF_ERROR_CONNECTION_BROKEN: 1389 default: 1390 ret = ECONNABORTED; 1391 goto out; 1392 1393 case SCF_ERROR_TYPE_MISMATCH: 1394 break; 1395 1396 case SCF_ERROR_INVALID_ARGUMENT: 1397 case SCF_ERROR_NOT_SET: 1398 bad_fail("scf_property_is_type", 1399 scf_error()); 1400 } 1401 } 1402 } else { 1403 switch (scf_error()) { 1404 case SCF_ERROR_CONNECTION_BROKEN: 1405 default: 1406 ret = ECONNABORTED; 1407 goto out; 1408 1409 case SCF_ERROR_DELETED: 1410 scf_entry_destroy(t_cid); 1411 goto add; 1412 1413 case SCF_ERROR_NOT_FOUND: 1414 break; 1415 1416 case SCF_ERROR_HANDLE_MISMATCH: 1417 case SCF_ERROR_INVALID_ARGUMENT: 1418 case SCF_ERROR_NOT_SET: 1419 bad_fail("scf_pg_get_property", scf_error()); 1420 } 1421 1422 new: 1423 if (scf_transaction_property_new(t, t_cid, pname, 1424 SCF_TYPE_COUNT) != 0) { 1425 switch (scf_error()) { 1426 case SCF_ERROR_CONNECTION_BROKEN: 1427 default: 1428 ret = ECONNABORTED; 1429 goto out; 1430 1431 case SCF_ERROR_DELETED: 1432 scf_entry_destroy(t_cid); 1433 goto add; 1434 1435 case SCF_ERROR_EXISTS: 1436 goto replace; 1437 1438 case SCF_ERROR_HANDLE_MISMATCH: 1439 case SCF_ERROR_INVALID_ARGUMENT: 1440 case SCF_ERROR_NOT_SET: 1441 bad_fail("scf_transaction_property_new", 1442 scf_error()); 1443 } 1444 } 1445 } 1446 1447 val = scf_value_create(h); 1448 if (val == NULL) { 1449 assert(scf_error() == SCF_ERROR_NO_MEMORY); 1450 ret = ENOMEM; 1451 goto out; 1452 } 1453 1454 scf_value_set_count(val, contract_id); 1455 ret = scf_entry_add_value(t_cid, val); 1456 assert(ret == 0); 1457 1458 ret = scf_transaction_commit(t); 1459 if (ret == -1) { 1460 switch (scf_error()) { 1461 case SCF_ERROR_CONNECTION_BROKEN: 1462 default: 1463 ret = ECONNABORTED; 1464 goto out; 1465 1466 case SCF_ERROR_DELETED: 1467 goto add; 1468 1469 case SCF_ERROR_PERMISSION_DENIED: 1470 ret = EPERM; 1471 goto out; 1472 1473 case SCF_ERROR_BACKEND_ACCESS: 1474 ret = EACCES; 1475 goto out; 1476 1477 case SCF_ERROR_BACKEND_READONLY: 1478 ret = EROFS; 1479 goto out; 1480 1481 case SCF_ERROR_NOT_SET: 1482 bad_fail("scf_transaction_commit", scf_error()); 1483 } 1484 } 1485 if (ret == 1) { 1486 ret = 0; 1487 break; 1488 } 1489 1490 scf_transaction_destroy_children(t); 1491 if (scf_pg_update(pg) == -1) { 1492 switch (scf_error()) { 1493 case SCF_ERROR_CONNECTION_BROKEN: 1494 default: 1495 ret = ECONNABORTED; 1496 goto out; 1497 1498 case SCF_ERROR_DELETED: 1499 goto add; 1500 1501 case SCF_ERROR_NOT_SET: 1502 bad_fail("scf_pg_update", scf_error()); 1503 } 1504 } 1505 } 1506 1507 out: 1508 scf_transaction_destroy_children(t); 1509 scf_transaction_destroy(t); 1510 scf_iter_destroy(iter); 1511 scf_property_destroy(prop); 1512 scf_pg_destroy(pg); 1513 1514 return (ret); 1515 } 1516 1517 int 1518 restarter_rm_libs_loadable() 1519 { 1520 void *libhndl; 1521 1522 if (method_context_safety) 1523 return (1); 1524 1525 if ((libhndl = dlopen("libpool.so", RTLD_LAZY | RTLD_LOCAL)) == NULL) 1526 return (0); 1527 1528 (void) dlclose(libhndl); 1529 1530 if ((libhndl = dlopen("libproject.so", RTLD_LAZY | RTLD_LOCAL)) == NULL) 1531 return (0); 1532 1533 (void) dlclose(libhndl); 1534 1535 method_context_safety = 1; 1536 1537 return (1); 1538 } 1539 1540 1541 static int 1542 get_astring_val(scf_propertygroup_t *pg, const char *name, char *buf, 1543 size_t bufsz, scf_property_t *prop, scf_value_t *val) 1544 { 1545 ssize_t szret; 1546 1547 if (scf_pg_get_property(pg, name, prop) != SCF_SUCCESS) { 1548 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN) 1549 uu_die(rcbroken); 1550 return (-1); 1551 } 1552 1553 if (scf_property_get_value(prop, val) != SCF_SUCCESS) { 1554 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN) 1555 uu_die(rcbroken); 1556 return (-1); 1557 } 1558 1559 szret = scf_value_get_astring(val, buf, bufsz); 1560 1561 return (szret >= 0 ? 0 : -1); 1562 } 1563 1564 /* 1565 * Try to load mcp->pwd, if it isn't already. 1566 * Fails with 1567 * ENOMEM - malloc() failed 1568 * ENOENT - no entry found 1569 * EIO - I/O error 1570 * EMFILE - process out of file descriptors 1571 * ENFILE - system out of file handles 1572 */ 1573 static int 1574 lookup_pwd(struct method_context *mcp) 1575 { 1576 struct passwd *pwdp; 1577 1578 if (mcp->pwbuf != NULL && mcp->pwd.pw_uid == mcp->uid) 1579 return (0); 1580 1581 if (mcp->pwbuf == NULL) { 1582 mcp->pwbufsz = sysconf(_SC_GETPW_R_SIZE_MAX); 1583 assert(mcp->pwbufsz >= 0); 1584 mcp->pwbuf = malloc(mcp->pwbufsz); 1585 if (mcp->pwbuf == NULL) 1586 return (ENOMEM); 1587 } 1588 1589 do { 1590 errno = 0; 1591 pwdp = getpwuid_r(mcp->uid, &mcp->pwd, mcp->pwbuf, 1592 mcp->pwbufsz); 1593 } while (pwdp == NULL && errno == EINTR); 1594 if (pwdp != NULL) 1595 return (0); 1596 1597 free(mcp->pwbuf); 1598 mcp->pwbuf = NULL; 1599 1600 switch (errno) { 1601 case 0: 1602 default: 1603 /* 1604 * Until bug 5065780 is fixed, getpwuid_r() can fail with 1605 * ENOENT, particularly on the miniroot. Since the 1606 * documentation is inaccurate, we'll return ENOENT for unknown 1607 * errors. 1608 */ 1609 return (ENOENT); 1610 1611 case EIO: 1612 case EMFILE: 1613 case ENFILE: 1614 return (errno); 1615 1616 case ERANGE: 1617 bad_fail("getpwuid_r", errno); 1618 /* NOTREACHED */ 1619 } 1620 } 1621 1622 /* 1623 * Get the user id for str. Returns 0 on success or 1624 * ERANGE the uid is too big 1625 * EINVAL the string starts with a digit, but is not a valid uid 1626 * ENOMEM out of memory 1627 * ENOENT no passwd entry for str 1628 * EIO an I/O error has occurred 1629 * EMFILE/ENFILE out of file descriptors 1630 */ 1631 int 1632 get_uid(const char *str, struct method_context *ci, uid_t *uidp) 1633 { 1634 if (isdigit(str[0])) { 1635 uid_t uid; 1636 char *cp; 1637 1638 errno = 0; 1639 uid = strtol(str, &cp, 10); 1640 1641 if (uid == 0 && errno != 0) { 1642 assert(errno != EINVAL); 1643 return (errno); 1644 } 1645 1646 for (; *cp != '\0'; ++cp) 1647 if (*cp != ' ' || *cp != '\t') 1648 return (EINVAL); 1649 1650 if (uid > UID_MAX) 1651 return (EINVAL); 1652 1653 *uidp = uid; 1654 return (0); 1655 } else { 1656 struct passwd *pwdp; 1657 1658 if (ci->pwbuf == NULL) { 1659 ci->pwbufsz = sysconf(_SC_GETPW_R_SIZE_MAX); 1660 ci->pwbuf = malloc(ci->pwbufsz); 1661 if (ci->pwbuf == NULL) 1662 return (ENOMEM); 1663 } 1664 1665 do { 1666 errno = 0; 1667 pwdp = 1668 getpwnam_r(str, &ci->pwd, ci->pwbuf, ci->pwbufsz); 1669 } while (pwdp == NULL && errno == EINTR); 1670 1671 if (pwdp != NULL) { 1672 *uidp = ci->pwd.pw_uid; 1673 return (0); 1674 } else { 1675 free(ci->pwbuf); 1676 ci->pwbuf = NULL; 1677 switch (errno) { 1678 case 0: 1679 return (ENOENT); 1680 1681 case ENOENT: 1682 case EIO: 1683 case EMFILE: 1684 case ENFILE: 1685 return (errno); 1686 1687 case ERANGE: 1688 default: 1689 bad_fail("getpwnam_r", errno); 1690 /* NOTREACHED */ 1691 } 1692 } 1693 } 1694 } 1695 1696 gid_t 1697 get_gid(const char *str) 1698 { 1699 if (isdigit(str[0])) { 1700 gid_t gid; 1701 char *cp; 1702 1703 errno = 0; 1704 gid = strtol(str, &cp, 10); 1705 1706 if (gid == 0 && errno != 0) 1707 return ((gid_t)-1); 1708 1709 for (; *cp != '\0'; ++cp) 1710 if (*cp != ' ' || *cp != '\t') 1711 return ((gid_t)-1); 1712 1713 return (gid); 1714 } else { 1715 struct group grp, *ret; 1716 char *buffer; 1717 size_t buflen; 1718 1719 buflen = sysconf(_SC_GETGR_R_SIZE_MAX); 1720 buffer = malloc(buflen); 1721 if (buffer == NULL) 1722 uu_die(allocfail); 1723 1724 errno = 0; 1725 ret = getgrnam_r(str, &grp, buffer, buflen); 1726 free(buffer); 1727 1728 return (ret == NULL ? (gid_t)-1 : grp.gr_gid); 1729 } 1730 } 1731 1732 /* 1733 * Fails with 1734 * ENOMEM - out of memory 1735 * ENOENT - no passwd entry 1736 * no project entry 1737 * EIO - an I/O error occurred 1738 * EMFILE - the process is out of file descriptors 1739 * ENFILE - the system is out of file handles 1740 * ERANGE - the project id is out of range 1741 * EINVAL - str is invalid 1742 * E2BIG - the project entry was too big 1743 * -1 - the name service switch is misconfigured 1744 */ 1745 int 1746 get_projid(const char *str, struct method_context *cip) 1747 { 1748 int ret; 1749 void *buf; 1750 const size_t bufsz = PROJECT_BUFSZ; 1751 struct project proj, *pp; 1752 1753 if (strcmp(str, ":default") == 0) { 1754 if (cip->uid == 0) { 1755 /* Don't change project for root services */ 1756 cip->project = NULL; 1757 return (0); 1758 } 1759 1760 switch (ret = lookup_pwd(cip)) { 1761 case 0: 1762 break; 1763 1764 case ENOMEM: 1765 case ENOENT: 1766 case EIO: 1767 case EMFILE: 1768 case ENFILE: 1769 return (ret); 1770 1771 default: 1772 bad_fail("lookup_pwd", ret); 1773 } 1774 1775 buf = malloc(bufsz); 1776 if (buf == NULL) 1777 return (ENOMEM); 1778 1779 do { 1780 errno = 0; 1781 pp = getdefaultproj(cip->pwd.pw_name, &proj, buf, 1782 bufsz); 1783 } while (pp == NULL && errno == EINTR); 1784 1785 /* to be continued ... */ 1786 } else { 1787 projid_t projid; 1788 char *cp; 1789 1790 if (!isdigit(str[0])) { 1791 cip->project = strdup(str); 1792 return (cip->project != NULL ? 0 : ENOMEM); 1793 } 1794 1795 errno = 0; 1796 projid = strtol(str, &cp, 10); 1797 1798 if (projid == 0 && errno != 0) { 1799 assert(errno == ERANGE); 1800 return (errno); 1801 } 1802 1803 for (; *cp != '\0'; ++cp) 1804 if (*cp != ' ' || *cp != '\t') 1805 return (EINVAL); 1806 1807 if (projid > MAXPROJID) 1808 return (ERANGE); 1809 1810 buf = malloc(bufsz); 1811 if (buf == NULL) 1812 return (ENOMEM); 1813 1814 do { 1815 errno = 0; 1816 pp = getprojbyid(projid, &proj, buf, bufsz); 1817 } while (pp == NULL && errno == EINTR); 1818 } 1819 1820 if (pp) { 1821 cip->project = strdup(pp->pj_name); 1822 free(buf); 1823 return (cip->project != NULL ? 0 : ENOMEM); 1824 } 1825 1826 free(buf); 1827 1828 switch (errno) { 1829 case 0: 1830 return (ENOENT); 1831 1832 case EIO: 1833 case EMFILE: 1834 case ENFILE: 1835 return (errno); 1836 1837 case ERANGE: 1838 return (E2BIG); 1839 1840 default: 1841 return (-1); 1842 } 1843 } 1844 1845 /* 1846 * Parse the supp_groups property value and populate ci->groups. Returns 1847 * EINVAL (get_gid() failed for one of the components), E2BIG (the property has 1848 * more than NGROUPS_MAX-1 groups), or 0 on success. 1849 */ 1850 int 1851 get_groups(char *str, struct method_context *ci) 1852 { 1853 char *cp, *end, *next; 1854 uint_t i; 1855 1856 const char * const whitespace = " \t"; 1857 const char * const illegal = ", \t"; 1858 1859 if (str[0] == '\0') { 1860 ci->ngroups = 0; 1861 return (0); 1862 } 1863 1864 for (cp = str, i = 0; *cp != '\0'; ) { 1865 /* skip whitespace */ 1866 cp += strspn(cp, whitespace); 1867 1868 /* find the end */ 1869 end = cp + strcspn(cp, illegal); 1870 1871 /* skip whitespace after end */ 1872 next = end + strspn(end, whitespace); 1873 1874 /* if there's a comma, it separates the fields */ 1875 if (*next == ',') 1876 ++next; 1877 1878 *end = '\0'; 1879 1880 if ((ci->groups[i] = get_gid(cp)) == (gid_t)-1) { 1881 ci->ngroups = 0; 1882 return (EINVAL); 1883 } 1884 1885 ++i; 1886 if (i > NGROUPS_MAX - 1) { 1887 ci->ngroups = 0; 1888 return (E2BIG); 1889 } 1890 1891 cp = next; 1892 } 1893 1894 ci->ngroups = i; 1895 return (0); 1896 } 1897 1898 /* 1899 * Eventually, we will return a structured error in the case of 1900 * retryable or abortable failures such as memory allocation errors and 1901 * repository connection failures. For now, these failures are just 1902 * encoded in the failure string. 1903 */ 1904 static const char * 1905 get_profile(scf_propertygroup_t *pg, scf_property_t *prop, scf_value_t *val, 1906 const char *cmdline, struct method_context *ci) 1907 { 1908 char *buf = ci->vbuf; 1909 ssize_t buf_sz = ci->vbuf_sz; 1910 char cmd[PATH_MAX]; 1911 char *cp, *value; 1912 const char *cmdp; 1913 execattr_t *eap; 1914 char *errstr = NULL; 1915 1916 if (get_astring_val(pg, SCF_PROPERTY_PROFILE, buf, buf_sz, prop, val) != 1917 0) 1918 return ("Could not get profile property."); 1919 1920 /* Extract the command from the command line. */ 1921 cp = strpbrk(cmdline, " \t"); 1922 1923 if (cp == NULL) { 1924 cmdp = cmdline; 1925 } else { 1926 (void) strncpy(cmd, cmdline, cp - cmdline); 1927 cmd[cp - cmdline] = '\0'; 1928 cmdp = cmd; 1929 } 1930 1931 /* Require that cmdp[0] == '/'? */ 1932 1933 eap = getexecprof(buf, KV_COMMAND, cmdp, GET_ONE); 1934 if (eap == NULL) 1935 return ("Could not find profile."); 1936 1937 /* Based on pfexec.c */ 1938 1939 /* Get the euid first so we don't override ci->pwd for the uid. */ 1940 if ((value = kva_match(eap->attr, EXECATTR_EUID_KW)) != NULL) { 1941 if (get_uid(value, ci, &ci->euid) != 0) { 1942 ci->euid = (uid_t)-1; 1943 errstr = "Could not interpret profile euid."; 1944 goto out; 1945 } 1946 } 1947 1948 if ((value = kva_match(eap->attr, EXECATTR_UID_KW)) != NULL) { 1949 if (get_uid(value, ci, &ci->uid) != 0) { 1950 ci->euid = ci->uid = (uid_t)-1; 1951 errstr = "Could not interpret profile uid."; 1952 goto out; 1953 } 1954 ci->euid = ci->uid; 1955 } 1956 1957 if ((value = kva_match(eap->attr, EXECATTR_GID_KW)) != NULL) { 1958 ci->egid = ci->gid = get_gid(value); 1959 if (ci->gid == (gid_t)-1) { 1960 errstr = "Could not interpret profile gid."; 1961 goto out; 1962 } 1963 } 1964 1965 if ((value = kva_match(eap->attr, EXECATTR_EGID_KW)) != NULL) { 1966 ci->egid = get_gid(value); 1967 if (ci->egid == (gid_t)-1) { 1968 errstr = "Could not interpret profile egid."; 1969 goto out; 1970 } 1971 } 1972 1973 if ((value = kva_match(eap->attr, EXECATTR_LPRIV_KW)) != NULL) { 1974 ci->lpriv_set = priv_str_to_set(value, ",", NULL); 1975 if (ci->lpriv_set == NULL) { 1976 if (errno != EINVAL) 1977 errstr = ALLOCFAIL; 1978 else 1979 errstr = "Could not interpret profile " 1980 "limitprivs."; 1981 goto out; 1982 } 1983 } 1984 1985 if ((value = kva_match(eap->attr, EXECATTR_IPRIV_KW)) != NULL) { 1986 ci->priv_set = priv_str_to_set(value, ",", NULL); 1987 if (ci->priv_set == NULL) { 1988 if (errno != EINVAL) 1989 errstr = ALLOCFAIL; 1990 else 1991 errstr = "Could not interpret profile privs."; 1992 goto out; 1993 } 1994 } 1995 1996 out: 1997 free_execattr(eap); 1998 1999 return (errstr); 2000 } 2001 2002 /* 2003 * Eventually, we will return a structured error in the case of 2004 * retryable or abortable failures such as memory allocation errors and 2005 * repository connection failures. For now, these failures are just 2006 * encoded in the failure string. 2007 */ 2008 static const char * 2009 get_ids(scf_propertygroup_t *pg, scf_property_t *prop, scf_value_t *val, 2010 struct method_context *ci) 2011 { 2012 const char *errstr = NULL; 2013 char *vbuf = ci->vbuf; 2014 ssize_t vbuf_sz = ci->vbuf_sz; 2015 int r; 2016 2017 if (get_astring_val(pg, SCF_PROPERTY_USER, vbuf, vbuf_sz, prop, val) != 2018 0) { 2019 errstr = "Could not get user property."; 2020 goto out; 2021 } 2022 2023 if (get_uid(vbuf, ci, &ci->uid) != 0) { 2024 ci->uid = (uid_t)-1; 2025 errstr = "Could not interpret user property."; 2026 goto out; 2027 } 2028 2029 if (get_astring_val(pg, SCF_PROPERTY_GROUP, vbuf, vbuf_sz, prop, val) != 2030 0) { 2031 errstr = "Could not get group property."; 2032 goto out; 2033 } 2034 2035 if (strcmp(vbuf, ":default") != 0) { 2036 ci->gid = get_gid(vbuf); 2037 if (ci->gid == (gid_t)-1) { 2038 errstr = "Could not interpret group property."; 2039 goto out; 2040 } 2041 } else { 2042 switch (r = lookup_pwd(ci)) { 2043 case 0: 2044 ci->gid = ci->pwd.pw_gid; 2045 break; 2046 2047 case ENOENT: 2048 ci->gid = (gid_t)-1; 2049 errstr = "No passwd entry."; 2050 goto out; 2051 2052 case ENOMEM: 2053 errstr = "Out of memory."; 2054 goto out; 2055 2056 case EIO: 2057 case EMFILE: 2058 case ENFILE: 2059 errstr = "getpwuid_r() failed."; 2060 goto out; 2061 2062 default: 2063 bad_fail("lookup_pwd", r); 2064 } 2065 } 2066 2067 if (get_astring_val(pg, SCF_PROPERTY_SUPP_GROUPS, vbuf, vbuf_sz, prop, 2068 val) != 0) { 2069 errstr = "Could not get supplemental groups property."; 2070 goto out; 2071 } 2072 2073 if (strcmp(vbuf, ":default") != 0) { 2074 switch (r = get_groups(vbuf, ci)) { 2075 case 0: 2076 break; 2077 2078 case EINVAL: 2079 errstr = 2080 "Could not interpret supplemental groups property."; 2081 goto out; 2082 2083 case E2BIG: 2084 errstr = "Too many supplemental groups."; 2085 goto out; 2086 2087 default: 2088 bad_fail("get_groups", r); 2089 } 2090 } else { 2091 ci->ngroups = -1; 2092 } 2093 2094 if (get_astring_val(pg, SCF_PROPERTY_PRIVILEGES, vbuf, vbuf_sz, prop, 2095 val) != 0) { 2096 errstr = "Could not get privileges property."; 2097 goto out; 2098 } 2099 2100 /* 2101 * For default privs, we need to keep priv_set == NULL, as 2102 * we use this test elsewhere. 2103 */ 2104 if (strcmp(vbuf, ":default") != 0) { 2105 ci->priv_set = priv_str_to_set(vbuf, ",", NULL); 2106 if (ci->priv_set == NULL) { 2107 if (errno != EINVAL) { 2108 errstr = ALLOCFAIL; 2109 } else { 2110 errstr = "Could not interpret privileges " 2111 "property."; 2112 } 2113 goto out; 2114 } 2115 } 2116 2117 if (get_astring_val(pg, SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf, vbuf_sz, 2118 prop, val) != 0) { 2119 errstr = "Could not get limit_privileges property."; 2120 goto out; 2121 } 2122 2123 if (strcmp(vbuf, ":default") == 0) 2124 /* 2125 * L must default to all privileges so root NPA services see 2126 * iE = all. "zone" is all privileges available in the current 2127 * zone, equivalent to "all" in the global zone. 2128 */ 2129 (void) strcpy(vbuf, "zone"); 2130 2131 ci->lpriv_set = priv_str_to_set(vbuf, ",", NULL); 2132 if (ci->lpriv_set == NULL) { 2133 if (errno != EINVAL) 2134 errstr = ALLOCFAIL; 2135 else { 2136 errstr = "Could not interpret limit_privileges " 2137 "property."; 2138 } 2139 goto out; 2140 } 2141 2142 out: 2143 return (errstr); 2144 } 2145 2146 static int 2147 get_environment(scf_handle_t *h, scf_propertygroup_t *pg, 2148 struct method_context *mcp, scf_property_t *prop, scf_value_t *val) 2149 { 2150 scf_iter_t *iter; 2151 scf_type_t type; 2152 size_t i = 0; 2153 int ret; 2154 2155 if (scf_pg_get_property(pg, SCF_PROPERTY_ENVIRONMENT, prop) != 0) { 2156 if (scf_error() == SCF_ERROR_NOT_FOUND) 2157 return (ENOENT); 2158 return (scf_error()); 2159 } 2160 if (scf_property_type(prop, &type) != 0) 2161 return (scf_error()); 2162 if (type != SCF_TYPE_ASTRING) 2163 return (EINVAL); 2164 if ((iter = scf_iter_create(h)) == NULL) 2165 return (scf_error()); 2166 2167 if (scf_iter_property_values(iter, prop) != 0) { 2168 ret = scf_error(); 2169 scf_iter_destroy(iter); 2170 return (ret); 2171 } 2172 2173 mcp->env_sz = 10; 2174 2175 if ((mcp->env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz)) == NULL) { 2176 ret = ENOMEM; 2177 goto out; 2178 } 2179 2180 while ((ret = scf_iter_next_value(iter, val)) == 1) { 2181 ret = scf_value_get_as_string(val, mcp->vbuf, mcp->vbuf_sz); 2182 if (ret == -1) { 2183 ret = scf_error(); 2184 goto out; 2185 } 2186 2187 if ((mcp->env[i] = strdup(mcp->vbuf)) == NULL) { 2188 ret = ENOMEM; 2189 goto out; 2190 } 2191 2192 if (++i == mcp->env_sz) { 2193 char **env; 2194 mcp->env_sz *= 2; 2195 env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz); 2196 if (env == NULL) { 2197 ret = ENOMEM; 2198 goto out; 2199 } 2200 (void) memcpy(env, mcp->env, 2201 sizeof (*mcp->env) * (mcp->env_sz / 2)); 2202 free(mcp->env); 2203 mcp->env = env; 2204 } 2205 } 2206 2207 if (ret == -1) 2208 ret = scf_error(); 2209 2210 out: 2211 scf_iter_destroy(iter); 2212 return (ret); 2213 } 2214 2215 /* 2216 * Fetch method context information from the repository, allocate and fill 2217 * a method_context structure, return it in *mcpp, and return NULL. On error, 2218 * return a human-readable string which indicates the error. 2219 * 2220 * Eventually, we will return a structured error in the case of 2221 * retryable or abortable failures such as memory allocation errors and 2222 * repository connection failures. For now, these failures are just 2223 * encoded in the failure string. 2224 */ 2225 const char * 2226 restarter_get_method_context(uint_t version, scf_instance_t *inst, 2227 scf_snapshot_t *snap, const char *mname, const char *cmdline, 2228 struct method_context **mcpp) 2229 { 2230 scf_handle_t *h; 2231 scf_propertygroup_t *methpg = NULL; 2232 scf_propertygroup_t *instpg = NULL; 2233 scf_propertygroup_t *pg = NULL; 2234 scf_property_t *prop = NULL; 2235 scf_value_t *val = NULL; 2236 scf_type_t ty; 2237 uint8_t use_profile; 2238 int ret; 2239 const char *errstr = NULL; 2240 struct method_context *cip; 2241 2242 2243 if (version != RESTARTER_METHOD_CONTEXT_VERSION) 2244 return ("Unknown method_context version."); 2245 2246 /* Get the handle before we allocate anything. */ 2247 h = scf_instance_handle(inst); 2248 if (h == NULL) 2249 return (scf_strerror(scf_error())); 2250 2251 cip = malloc(sizeof (*cip)); 2252 if (cip == NULL) 2253 return (ALLOCFAIL); 2254 2255 (void) memset(cip, 0, sizeof (*cip)); 2256 cip->uid = (uid_t)-1; 2257 cip->euid = (uid_t)-1; 2258 cip->gid = (gid_t)-1; 2259 cip->egid = (gid_t)-1; 2260 2261 cip->vbuf_sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 2262 assert(cip->vbuf_sz >= 0); 2263 cip->vbuf = malloc(cip->vbuf_sz); 2264 if (cip->vbuf == NULL) { 2265 free(cip); 2266 return (ALLOCFAIL); 2267 } 2268 2269 if ((instpg = scf_pg_create(h)) == NULL || 2270 (methpg = scf_pg_create(h)) == NULL || 2271 (prop = scf_property_create(h)) == NULL || 2272 (val = scf_value_create(h)) == NULL) { 2273 errstr = ALLOCFAIL; 2274 goto out; 2275 } 2276 2277 /* 2278 * The method environment, and the credentials/profile data, 2279 * may be found either in the pg for the method (methpg), 2280 * or in the instance/service SCF_PG_METHOD_CONTEXT pg (named 2281 * instpg below). 2282 */ 2283 2284 if (scf_instance_get_pg_composed(inst, snap, mname, methpg) != 2285 SCF_SUCCESS) { 2286 errstr = scf_strerror(scf_error()); 2287 goto out; 2288 } 2289 2290 if (scf_instance_get_pg_composed(inst, snap, SCF_PG_METHOD_CONTEXT, 2291 instpg) != SCF_SUCCESS) { 2292 if (scf_error() != SCF_ERROR_NOT_FOUND) { 2293 errstr = scf_strerror(scf_error()); 2294 goto out; 2295 } 2296 scf_pg_destroy(instpg); 2297 instpg = NULL; 2298 } 2299 2300 ret = get_environment(h, methpg, cip, prop, val); 2301 if (ret == ENOENT && instpg != NULL) { 2302 ret = get_environment(h, instpg, cip, prop, val); 2303 } 2304 2305 switch (ret) { 2306 case 0: 2307 case ENOENT: 2308 break; 2309 case ENOMEM: 2310 errstr = "Out of memory."; 2311 goto out; 2312 case EINVAL: 2313 errstr = "Invalid method environment."; 2314 goto out; 2315 default: 2316 errstr = scf_strerror(ret); 2317 goto out; 2318 } 2319 2320 pg = methpg; 2321 2322 ret = scf_pg_get_property(pg, SCF_PROPERTY_USE_PROFILE, prop); 2323 if (ret && scf_error() == SCF_ERROR_NOT_FOUND && instpg != NULL) { 2324 pg = instpg; 2325 ret = scf_pg_get_property(pg, SCF_PROPERTY_USE_PROFILE, prop); 2326 } 2327 2328 if (ret) { 2329 switch (scf_error()) { 2330 case SCF_ERROR_NOT_FOUND: 2331 /* No context: use defaults */ 2332 cip->uid = 0; 2333 cip->gid = 0; 2334 *mcpp = cip; 2335 goto out; 2336 2337 case SCF_ERROR_CONNECTION_BROKEN: 2338 errstr = RCBROKEN; 2339 goto out; 2340 2341 case SCF_ERROR_DELETED: 2342 errstr = "\"use_profile\" property deleted."; 2343 goto out; 2344 2345 case SCF_ERROR_HANDLE_MISMATCH: 2346 case SCF_ERROR_INVALID_ARGUMENT: 2347 case SCF_ERROR_NOT_SET: 2348 default: 2349 bad_fail("scf_pg_get_property", scf_error()); 2350 } 2351 } 2352 2353 if (scf_property_type(prop, &ty) != SCF_SUCCESS) { 2354 switch (scf_error()) { 2355 case SCF_ERROR_CONNECTION_BROKEN: 2356 errstr = RCBROKEN; 2357 break; 2358 2359 case SCF_ERROR_DELETED: 2360 errstr = "\"use profile\" property deleted."; 2361 break; 2362 2363 case SCF_ERROR_NOT_SET: 2364 default: 2365 bad_fail("scf_property_type", scf_error()); 2366 } 2367 2368 goto out; 2369 } 2370 2371 if (ty != SCF_TYPE_BOOLEAN) { 2372 errstr = "\"use profile\" property is not boolean."; 2373 goto out; 2374 } 2375 2376 if (scf_property_get_value(prop, val) != SCF_SUCCESS) { 2377 switch (scf_error()) { 2378 case SCF_ERROR_CONNECTION_BROKEN: 2379 errstr = RCBROKEN; 2380 break; 2381 2382 case SCF_ERROR_CONSTRAINT_VIOLATED: 2383 errstr = 2384 "\"use profile\" property has multiple values."; 2385 break; 2386 2387 case SCF_ERROR_NOT_FOUND: 2388 errstr = "\"use profile\" property has no values."; 2389 break; 2390 2391 default: 2392 bad_fail("scf_property_get_value", scf_error()); 2393 } 2394 2395 goto out; 2396 } 2397 2398 ret = scf_value_get_boolean(val, &use_profile); 2399 assert(ret == SCF_SUCCESS); 2400 2401 /* get ids & privileges */ 2402 if (use_profile) 2403 errstr = get_profile(pg, prop, val, cmdline, cip); 2404 else 2405 errstr = get_ids(pg, prop, val, cip); 2406 if (errstr != NULL) 2407 goto out; 2408 2409 /* get working directory */ 2410 if (get_astring_val(pg, SCF_PROPERTY_WORKING_DIRECTORY, cip->vbuf, 2411 cip->vbuf_sz, prop, val) != 0) { 2412 errstr = "Could not get value for working directory."; 2413 goto out; 2414 } 2415 2416 if (strcmp(cip->vbuf, ":default") == 0 || 2417 strcmp(cip->vbuf, ":home") == 0) { 2418 switch (ret = lookup_pwd(cip)) { 2419 case 0: 2420 break; 2421 2422 case ENOMEM: 2423 errstr = "Out of memory."; 2424 goto out; 2425 2426 case ENOENT: 2427 case EIO: 2428 case EMFILE: 2429 case ENFILE: 2430 errstr = "Could not get passwd entry."; 2431 goto out; 2432 2433 default: 2434 bad_fail("lookup_pwd", ret); 2435 } 2436 2437 cip->working_dir = strdup(cip->pwd.pw_dir); 2438 if (cip->working_dir == NULL) { 2439 errstr = ALLOCFAIL; 2440 goto out; 2441 } 2442 } else { 2443 cip->working_dir = strdup(cip->vbuf); 2444 if (cip->working_dir == NULL) { 2445 errstr = ALLOCFAIL; 2446 goto out; 2447 } 2448 } 2449 2450 /* get (optional) corefile pattern */ 2451 if (scf_pg_get_property(pg, SCF_PROPERTY_COREFILE_PATTERN, prop) == 2452 SCF_SUCCESS) { 2453 if (get_astring_val(pg, SCF_PROPERTY_COREFILE_PATTERN, 2454 cip->vbuf, cip->vbuf_sz, prop, val) != 0) { 2455 errstr = "Could not get value for corefile pattern."; 2456 goto out; 2457 } 2458 2459 cip->corefile_pattern = strdup(cip->vbuf); 2460 if (cip->corefile_pattern == NULL) { 2461 errstr = ALLOCFAIL; 2462 goto out; 2463 } 2464 } else { 2465 switch (scf_error()) { 2466 case SCF_ERROR_NOT_FOUND: 2467 /* okay if missing. */ 2468 break; 2469 2470 case SCF_ERROR_CONNECTION_BROKEN: 2471 errstr = RCBROKEN; 2472 goto out; 2473 2474 case SCF_ERROR_DELETED: 2475 errstr = "\"corefile_pattern\" property deleted."; 2476 goto out; 2477 2478 case SCF_ERROR_HANDLE_MISMATCH: 2479 case SCF_ERROR_INVALID_ARGUMENT: 2480 case SCF_ERROR_NOT_SET: 2481 default: 2482 bad_fail("scf_pg_get_property", scf_error()); 2483 } 2484 } 2485 2486 if (restarter_rm_libs_loadable()) { 2487 /* get project */ 2488 if (get_astring_val(pg, SCF_PROPERTY_PROJECT, cip->vbuf, 2489 cip->vbuf_sz, prop, val) != 0) { 2490 errstr = "Could not get project."; 2491 goto out; 2492 } 2493 2494 switch (ret = get_projid(cip->vbuf, cip)) { 2495 case 0: 2496 break; 2497 2498 case ENOMEM: 2499 errstr = "Out of memory."; 2500 goto out; 2501 2502 case ENOENT: 2503 errstr = "Missing passwd or project entry."; 2504 goto out; 2505 2506 case EIO: 2507 errstr = "I/O error."; 2508 goto out; 2509 2510 case EMFILE: 2511 case ENFILE: 2512 errstr = "Out of file descriptors."; 2513 goto out; 2514 2515 case -1: 2516 errstr = "Name service switch is misconfigured."; 2517 goto out; 2518 2519 case ERANGE: 2520 errstr = "Project ID too big."; 2521 goto out; 2522 2523 case EINVAL: 2524 errstr = "Project ID is invalid."; 2525 goto out; 2526 2527 case E2BIG: 2528 errstr = "Project entry is too big."; 2529 goto out; 2530 2531 default: 2532 bad_fail("get_projid", ret); 2533 } 2534 2535 /* get resource pool */ 2536 if (get_astring_val(pg, SCF_PROPERTY_RESOURCE_POOL, cip->vbuf, 2537 cip->vbuf_sz, prop, val) != 0) { 2538 errstr = "Could not get value of resource pool."; 2539 goto out; 2540 } 2541 2542 if (strcmp(cip->vbuf, ":default") != 0) { 2543 cip->resource_pool = strdup(cip->vbuf); 2544 if (cip->resource_pool == NULL) { 2545 errstr = ALLOCFAIL; 2546 goto out; 2547 } 2548 } 2549 } 2550 2551 *mcpp = cip; 2552 2553 out: 2554 (void) scf_value_destroy(val); 2555 scf_property_destroy(prop); 2556 scf_pg_destroy(instpg); 2557 scf_pg_destroy(methpg); 2558 2559 if (cip->pwbuf != NULL) 2560 free(cip->pwbuf); 2561 free(cip->vbuf); 2562 2563 if (errstr != NULL) 2564 restarter_free_method_context(cip); 2565 2566 return (errstr); 2567 } 2568 2569 /* 2570 * Modify the current process per the given method_context. On success, returns 2571 * 0. Note that the environment is not modified by this function to include the 2572 * environment variables in cip->env. 2573 * 2574 * On failure, sets *fp to NULL or the name of the function which failed, 2575 * and returns one of the following error codes. The words in parentheses are 2576 * the values to which *fp may be set for the error case. 2577 * ENOMEM - malloc() failed 2578 * EIO - an I/O error occurred (getpwuid_r, chdir) 2579 * EMFILE - process is out of file descriptors (getpwuid_r) 2580 * ENFILE - system is out of file handles (getpwuid_r) 2581 * EINVAL - gid or egid is out of range (setregid) 2582 * ngroups is too big (setgroups) 2583 * project's project id is bad (setproject) 2584 * uid or euid is out of range (setreuid) 2585 * poolname is invalid (pool_set_binding) 2586 * EPERM - insufficient privilege (setregid, initgroups, setgroups, setppriv, 2587 * setproject, setreuid, settaskid) 2588 * ENOENT - uid has a passwd entry but no shadow entry 2589 * working_dir does not exist (chdir) 2590 * uid has no passwd entry 2591 * the pool could not be found (pool_set_binding) 2592 * EFAULT - lpriv_set or priv_set has a bad address (setppriv) 2593 * working_dir has a bad address (chdir) 2594 * EACCES - could not access working_dir (chdir) 2595 * in a TASK_FINAL task (setproject, settaskid) 2596 * no resource pool accepting default binding exists (setproject) 2597 * ELOOP - too many symbolic links in working_dir (chdir) 2598 * ENAMETOOLONG - working_dir is too long (chdir) 2599 * ENOLINK - working_dir is on an inaccessible remote machine (chdir) 2600 * ENOTDIR - working_dir is not a directory (chdir) 2601 * ESRCH - uid is not a user of project (setproject) 2602 * project is invalid (setproject) 2603 * the resource pool specified for project is unknown (setproject) 2604 * EBADF - the configuration for the pool is invalid (pool_set_binding) 2605 * -1 - core_set_process_path() failed (core_set_process_path) 2606 * a resource control assignment failed (setproject) 2607 * a system error occurred during pool_set_binding (pool_set_binding) 2608 */ 2609 int 2610 restarter_set_method_context(struct method_context *cip, const char **fp) 2611 { 2612 pid_t mypid = -1; 2613 int r, ret; 2614 2615 cip->pwbuf = NULL; 2616 *fp = NULL; 2617 2618 if (cip->gid != (gid_t)-1) { 2619 if (setregid(cip->gid, 2620 cip->egid != (gid_t)-1 ? cip->egid : cip->gid) != 0) { 2621 *fp = "setregid"; 2622 2623 ret = errno; 2624 assert(ret == EINVAL || ret == EPERM); 2625 goto out; 2626 } 2627 } else { 2628 if (cip->pwbuf == NULL) { 2629 switch (ret = lookup_pwd(cip)) { 2630 case 0: 2631 break; 2632 2633 case ENOMEM: 2634 case ENOENT: 2635 *fp = NULL; 2636 goto out; 2637 2638 case EIO: 2639 case EMFILE: 2640 case ENFILE: 2641 *fp = "getpwuid_r"; 2642 goto out; 2643 2644 default: 2645 bad_fail("lookup_pwd", ret); 2646 } 2647 } 2648 2649 if (setregid(cip->pwd.pw_gid, 2650 cip->egid != (gid_t)-1 ? 2651 cip->egid : cip->pwd.pw_gid) != 0) { 2652 *fp = "setregid"; 2653 2654 ret = errno; 2655 assert(ret == EINVAL || ret == EPERM); 2656 goto out; 2657 } 2658 } 2659 2660 if (cip->ngroups == -1) { 2661 if (cip->pwbuf == NULL) { 2662 switch (ret = lookup_pwd(cip)) { 2663 case 0: 2664 break; 2665 2666 case ENOMEM: 2667 case ENOENT: 2668 *fp = NULL; 2669 goto out; 2670 2671 case EIO: 2672 case EMFILE: 2673 case ENFILE: 2674 *fp = "getpwuid_r"; 2675 goto out; 2676 2677 default: 2678 bad_fail("lookup_pwd", ret); 2679 } 2680 } 2681 2682 /* Ok if cip->gid == -1 */ 2683 if (initgroups(cip->pwd.pw_name, cip->gid) != 0) { 2684 *fp = "initgroups"; 2685 ret = errno; 2686 assert(ret == EPERM); 2687 goto out; 2688 } 2689 } else if (cip->ngroups > 0 && 2690 setgroups(cip->ngroups, cip->groups) != 0) { 2691 *fp = "setgroups"; 2692 2693 ret = errno; 2694 assert(ret == EINVAL || ret == EPERM); 2695 goto out; 2696 } 2697 2698 *fp = "setppriv"; 2699 2700 if (cip->lpriv_set != NULL) { 2701 if (setppriv(PRIV_SET, PRIV_LIMIT, cip->lpriv_set) != 0) { 2702 ret = errno; 2703 assert(ret == EFAULT || ret == EPERM); 2704 goto out; 2705 } 2706 } 2707 if (cip->priv_set != NULL) { 2708 if (setppriv(PRIV_SET, PRIV_INHERITABLE, cip->priv_set) != 0) { 2709 ret = errno; 2710 assert(ret == EFAULT || ret == EPERM); 2711 goto out; 2712 } 2713 } 2714 2715 if (cip->corefile_pattern != NULL) { 2716 mypid = getpid(); 2717 2718 if (core_set_process_path(cip->corefile_pattern, 2719 strlen(cip->corefile_pattern) + 1, mypid) != 0) { 2720 *fp = "core_set_process_path"; 2721 ret = -1; 2722 goto out; 2723 } 2724 } 2725 2726 if (restarter_rm_libs_loadable()) { 2727 if (cip->project == NULL) { 2728 if (settaskid(getprojid(), TASK_NORMAL) == -1) { 2729 switch (errno) { 2730 case EACCES: 2731 case EPERM: 2732 *fp = "settaskid"; 2733 ret = errno; 2734 goto out; 2735 2736 case EINVAL: 2737 default: 2738 bad_fail("settaskid", errno); 2739 } 2740 } 2741 } else { 2742 switch (ret = lookup_pwd(cip)) { 2743 case 0: 2744 break; 2745 2746 case ENOMEM: 2747 case ENOENT: 2748 *fp = NULL; 2749 goto out; 2750 2751 case EIO: 2752 case EMFILE: 2753 case ENFILE: 2754 *fp = "getpwuid_r"; 2755 goto out; 2756 2757 default: 2758 bad_fail("lookup_pwd", ret); 2759 } 2760 2761 *fp = "setproject"; 2762 2763 switch (setproject(cip->project, cip->pwd.pw_name, 2764 TASK_NORMAL)) { 2765 case 0: 2766 break; 2767 2768 case SETPROJ_ERR_TASK: 2769 case SETPROJ_ERR_POOL: 2770 ret = errno; 2771 goto out; 2772 2773 default: 2774 ret = -1; 2775 goto out; 2776 } 2777 } 2778 2779 if (cip->resource_pool != NULL) { 2780 if (mypid == -1) 2781 mypid = getpid(); 2782 2783 *fp = "pool_set_binding"; 2784 2785 if (pool_set_binding(cip->resource_pool, P_PID, 2786 mypid) != PO_SUCCESS) { 2787 switch (pool_error()) { 2788 case POE_INVALID_SEARCH: 2789 ret = ENOENT; 2790 break; 2791 2792 case POE_BADPARAM: 2793 ret = EINVAL; 2794 break; 2795 2796 case POE_INVALID_CONF: 2797 ret = EBADF; 2798 break; 2799 2800 case POE_SYSTEM: 2801 ret = -1; 2802 break; 2803 2804 default: 2805 bad_fail("pool_set_binding", 2806 pool_error()); 2807 } 2808 2809 goto out; 2810 } 2811 } 2812 } 2813 2814 /* 2815 * Now, we have to assume our ID. If the UID is 0, we want it to be 2816 * privilege-aware, otherwise the limit set gets used instead of E/P. 2817 * We can do this by setting P as well, which keeps 2818 * PA status (see priv_can_clear_PA()). 2819 */ 2820 2821 *fp = "setreuid"; 2822 if (setreuid(cip->uid, 2823 cip->euid != (uid_t)-1 ? cip->euid : cip->uid) != 0) { 2824 ret = errno; 2825 assert(ret == EINVAL || ret == EPERM); 2826 goto out; 2827 } 2828 2829 *fp = "setppriv"; 2830 if (cip->priv_set != NULL) { 2831 if (setppriv(PRIV_SET, PRIV_PERMITTED, cip->priv_set) != 0) { 2832 ret = errno; 2833 assert(ret == EFAULT || ret == EPERM); 2834 goto out; 2835 } 2836 } 2837 2838 /* 2839 * The last thing to do is chdir to the specified working directory. 2840 * This should come after the uid switching as only the user might 2841 * have access to the specified directory. 2842 */ 2843 if (cip->working_dir != NULL) { 2844 do { 2845 r = chdir(cip->working_dir); 2846 } while (r != 0 && errno == EINTR); 2847 if (r != 0) { 2848 *fp = "chdir"; 2849 ret = errno; 2850 goto out; 2851 } 2852 } 2853 2854 ret = 0; 2855 out: 2856 free(cip->pwbuf); 2857 cip->pwbuf = NULL; 2858 return (ret); 2859 } 2860 2861 void 2862 restarter_free_method_context(struct method_context *mcp) 2863 { 2864 size_t i; 2865 2866 if (mcp->lpriv_set != NULL) 2867 priv_freeset(mcp->lpriv_set); 2868 if (mcp->priv_set != NULL) 2869 priv_freeset(mcp->priv_set); 2870 2871 if (mcp->env != NULL) { 2872 for (i = 0; i < mcp->env_sz; i++) 2873 free(mcp->env[i]); 2874 free(mcp->env); 2875 } 2876 2877 free(mcp->working_dir); 2878 free(mcp->corefile_pattern); 2879 free(mcp->project); 2880 free(mcp->resource_pool); 2881 free(mcp); 2882 } 2883 2884 /* 2885 * Method keyword functions 2886 */ 2887 2888 int 2889 restarter_is_null_method(const char *meth) 2890 { 2891 return (strcmp(meth, MKW_TRUE) == 0); 2892 } 2893 2894 static int 2895 is_kill_method(const char *method, const char *kill_str, 2896 size_t kill_str_len) 2897 { 2898 const char *cp; 2899 int sig; 2900 2901 if (strncmp(method, kill_str, kill_str_len) != 0 || 2902 (method[kill_str_len] != '\0' && 2903 !isspace(method[kill_str_len]))) 2904 return (-1); 2905 2906 cp = method + kill_str_len; 2907 while (*cp != '\0' && isspace(*cp)) 2908 ++cp; 2909 2910 if (*cp == '\0') 2911 return (SIGTERM); 2912 2913 if (*cp != '-') 2914 return (-1); 2915 2916 return (str2sig(cp + 1, &sig) == 0 ? sig : -1); 2917 } 2918 2919 int 2920 restarter_is_kill_proc_method(const char *method) 2921 { 2922 return (is_kill_method(method, MKW_KILL_PROC, 2923 sizeof (MKW_KILL_PROC) - 1)); 2924 } 2925 2926 int 2927 restarter_is_kill_method(const char *method) 2928 { 2929 return (is_kill_method(method, MKW_KILL, sizeof (MKW_KILL) - 1)); 2930 } 2931 2932 /* 2933 * Stubs for now. 2934 */ 2935 2936 /* ARGSUSED */ 2937 int 2938 restarter_event_get_enabled(restarter_event_t *e) 2939 { 2940 return (-1); 2941 } 2942 2943 /* ARGSUSED */ 2944 uint64_t 2945 restarter_event_get_seq(restarter_event_t *e) 2946 { 2947 return (-1); 2948 } 2949 2950 /* ARGSUSED */ 2951 void 2952 restarter_event_get_time(restarter_event_t *e, hrtime_t *time) 2953 { 2954 } 2955