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