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