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