1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <librestart.h> 28 #include <librestart_priv.h> 29 #include <libscf.h> 30 #include <libscf_priv.h> 31 32 #include <assert.h> 33 #include <ctype.h> 34 #include <dlfcn.h> 35 #include <errno.h> 36 #include <exec_attr.h> 37 #include <grp.h> 38 #include <libsysevent.h> 39 #include <libuutil.h> 40 #include <limits.h> 41 #include <link.h> 42 #include <malloc.h> 43 #include <pool.h> 44 #include <priv.h> 45 #include <project.h> 46 #include <pthread.h> 47 #include <pwd.h> 48 #include <secdb.h> 49 #include <signal.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <syslog.h> 53 #include <sys/corectl.h> 54 #include <sys/machelf.h> 55 #include <sys/task.h> 56 #include <sys/types.h> 57 #include <time.h> 58 #include <unistd.h> 59 #include <ucontext.h> 60 61 #define min(a, b) ((a) > (b) ? (b) : (a)) 62 63 #define MKW_TRUE ":true" 64 #define MKW_KILL ":kill" 65 #define MKW_KILL_PROC ":kill_process" 66 67 #define ALLOCFAIL ((char *)"Allocation failure.") 68 #define RCBROKEN ((char *)"Repository connection broken.") 69 70 #define MAX_COMMIT_RETRIES 10 71 #define MAX_COMMIT_RETRY_INT (5 * 1000000) /* 5 seconds */ 72 #define INITIAL_COMMIT_RETRY_INT (10000) /* 1/100th second */ 73 74 /* 75 * bad_fail() catches bugs in this and lower layers by reporting supposedly 76 * impossible function failures. The NDEBUG case keeps the strings out of the 77 * library but still calls abort() so we can root-cause from the coredump. 78 */ 79 #ifndef NDEBUG 80 #define bad_fail(func, err) { \ 81 (void) fprintf(stderr, \ 82 "At %s:%d, %s() failed with unexpected error %d. Aborting.\n", \ 83 __FILE__, __LINE__, (func), (err)); \ 84 abort(); \ 85 } 86 #else 87 #define bad_fail(func, err) abort() 88 #endif 89 90 struct restarter_event_handle { 91 char *reh_restarter_name; 92 char *reh_delegate_channel_name; 93 evchan_t *reh_delegate_channel; 94 char *reh_delegate_subscriber_id; 95 char *reh_master_channel_name; 96 evchan_t *reh_master_channel; 97 char *reh_master_subscriber_id; 98 int (*reh_handler)(restarter_event_t *); 99 }; 100 101 struct restarter_event { 102 sysevent_t *re_sysevent; 103 restarter_event_type_t re_type; 104 char *re_instance_name; 105 restarter_event_handle_t *re_event_handle; 106 restarter_instance_state_t re_state; 107 restarter_instance_state_t re_next_state; 108 }; 109 110 /* 111 * A static no memory error message mc_error_t structure 112 * to be used in cases when memory errors are to be returned 113 * This avoids the need to attempt to allocate memory for the 114 * message, therefore getting into a cycle of no memory failures. 115 */ 116 mc_error_t mc_nomem_err = { 117 0, ENOMEM, sizeof ("Out of memory") - 1, "Out of memory" 118 }; 119 120 static const char * const allocfail = "Allocation failure.\n"; 121 static const char * const rcbroken = "Repository connection broken.\n"; 122 123 static int method_context_safety = 0; /* Can safely call pools/projects. */ 124 125 int ndebug = 1; 126 127 /* PRINTFLIKE3 */ 128 static mc_error_t * 129 mc_error_create(mc_error_t *e, int type, const char *format, ...) 130 { 131 mc_error_t *le; 132 va_list args; 133 int size; 134 135 /* 136 * If the type is ENOMEM and format is NULL, then 137 * go ahead and return the default nomem error. 138 * Otherwise, attempt to allocate the memory and if 139 * that fails then there is no reason to continue. 140 */ 141 if (type == ENOMEM && format == NULL) 142 return (&mc_nomem_err); 143 144 if (e == NULL && (le = malloc(sizeof (mc_error_t))) == NULL) 145 return (&mc_nomem_err); 146 else 147 le = e; 148 149 le->type = type; 150 le->destroy = 1; 151 va_start(args, format); 152 size = vsnprintf(NULL, 0, format, args) + 1; 153 if (size >= RESTARTER_ERRMSGSZ) { 154 if ((le = realloc(e, sizeof (mc_error_t) + 155 (size - RESTARTER_ERRMSGSZ))) == NULL) { 156 size = RESTARTER_ERRMSGSZ - 1; 157 le = e; 158 } 159 } 160 161 le->size = size; 162 (void) vsnprintf(le->msg, le->size, format, args); 163 va_end(args); 164 165 return (le); 166 } 167 168 void 169 restarter_mc_error_destroy(mc_error_t *mc_err) 170 { 171 if (mc_err == NULL) 172 return; 173 174 /* 175 * If the error messages was allocated then free. 176 */ 177 if (mc_err->destroy) { 178 free(mc_err); 179 } 180 } 181 182 static void 183 free_restarter_event_handle(struct restarter_event_handle *h) 184 { 185 if (h == NULL) 186 return; 187 188 /* 189 * Just free the memory -- don't unbind the sysevent handle, 190 * as otherwise events may be lost if this is just a restarter 191 * restart. 192 */ 193 194 if (h->reh_restarter_name != NULL) 195 free(h->reh_restarter_name); 196 if (h->reh_delegate_channel_name != NULL) 197 free(h->reh_delegate_channel_name); 198 if (h->reh_delegate_subscriber_id != NULL) 199 free(h->reh_delegate_subscriber_id); 200 if (h->reh_master_channel_name != NULL) 201 free(h->reh_master_channel_name); 202 if (h->reh_master_subscriber_id != NULL) 203 free(h->reh_master_subscriber_id); 204 205 free(h); 206 } 207 208 char * 209 _restarter_get_channel_name(const char *fmri, int type) 210 { 211 char *name; 212 char *chan_name = malloc(MAX_CHNAME_LEN); 213 char prefix_name[3]; 214 int i; 215 216 if (chan_name == NULL) 217 return (NULL); 218 219 if (type == RESTARTER_CHANNEL_DELEGATE) 220 (void) strcpy(prefix_name, "d_"); 221 else if (type == RESTARTER_CHANNEL_MASTER) 222 (void) strcpy(prefix_name, "m_"); 223 else { 224 free(chan_name); 225 return (NULL); 226 } 227 228 /* 229 * Create a unique name 230 * 231 * Use the entire name, using a replacement of the / 232 * characters to get a better name. 233 * 234 * Remove the svc:/ from the beginning as this really 235 * isn't going to provide any uniqueness... 236 * 237 * An fmri name greater than MAX_CHNAME_LEN is going 238 * to be rejected as too long for the chan_name below 239 * in the snprintf call. 240 */ 241 if ((name = strdup(strchr(fmri, '/') + 1)) == NULL) { 242 free(chan_name); 243 return (NULL); 244 } 245 i = 0; 246 while (name[i]) { 247 if (name[i] == '/') { 248 name[i] = '_'; 249 } 250 251 i++; 252 } 253 254 /* 255 * Should check for [a-z],[A-Z],[0-9],.,_,-,: 256 */ 257 258 if (snprintf(chan_name, MAX_CHNAME_LEN, "com.sun:scf:%s%s", 259 prefix_name, name) > MAX_CHNAME_LEN) { 260 free(chan_name); 261 chan_name = NULL; 262 } 263 264 free(name); 265 return (chan_name); 266 } 267 268 int 269 cb(sysevent_t *syse, void *cookie) 270 { 271 restarter_event_handle_t *h = (restarter_event_handle_t *)cookie; 272 restarter_event_t *e; 273 nvlist_t *attr_list = NULL; 274 int ret = 0; 275 276 e = uu_zalloc(sizeof (restarter_event_t)); 277 if (e == NULL) 278 uu_die(allocfail); 279 e->re_event_handle = h; 280 e->re_sysevent = syse; 281 282 if (sysevent_get_attr_list(syse, &attr_list) != 0) 283 uu_die(allocfail); 284 285 if ((nvlist_lookup_uint32(attr_list, RESTARTER_NAME_TYPE, 286 &(e->re_type)) != 0) || 287 (nvlist_lookup_string(attr_list, 288 RESTARTER_NAME_INSTANCE, &(e->re_instance_name)) != 0)) { 289 uu_warn("%s: Can't decode nvlist for event %p\n", 290 h->reh_restarter_name, (void *)syse); 291 292 ret = 0; 293 } else { 294 ret = h->reh_handler(e); 295 } 296 297 uu_free(e); 298 nvlist_free(attr_list); 299 return (ret); 300 } 301 302 /* 303 * restarter_bind_handle(uint32_t, char *, int (*)(restarter_event_t *), int, 304 * restarter_event_handle_t **) 305 * 306 * Bind to a delegated restarter event channel. 307 * Each delegated restarter gets its own channel for resource management. 308 * 309 * Returns 0 on success or 310 * ENOTSUP version mismatch 311 * EINVAL restarter_name or event_handle is NULL 312 * ENOMEM out of memory, too many channels, or too many subscriptions 313 * EBUSY sysevent_evc_bind() could not establish binding 314 * EFAULT internal sysevent_evc_bind()/sysevent_evc_subscribe() error 315 * EMFILE out of file descriptors 316 * EPERM insufficient privilege for sysevent_evc_bind() 317 * EEXIST already subscribed 318 */ 319 int 320 restarter_bind_handle(uint32_t version, const char *restarter_name, 321 int (*event_handler)(restarter_event_t *), int flags, 322 restarter_event_handle_t **rehp) 323 { 324 restarter_event_handle_t *h; 325 size_t sz; 326 int err; 327 328 if (version != RESTARTER_EVENT_VERSION) 329 return (ENOTSUP); 330 331 if (restarter_name == NULL || event_handler == NULL) 332 return (EINVAL); 333 334 if (flags & RESTARTER_FLAG_DEBUG) 335 ndebug++; 336 337 if ((h = uu_zalloc(sizeof (restarter_event_handle_t))) == NULL) 338 return (ENOMEM); 339 340 h->reh_delegate_subscriber_id = malloc(MAX_SUBID_LEN); 341 h->reh_master_subscriber_id = malloc(MAX_SUBID_LEN); 342 h->reh_restarter_name = strdup(restarter_name); 343 if (h->reh_delegate_subscriber_id == NULL || 344 h->reh_master_subscriber_id == NULL || 345 h->reh_restarter_name == NULL) { 346 free_restarter_event_handle(h); 347 return (ENOMEM); 348 } 349 350 sz = strlcpy(h->reh_delegate_subscriber_id, "del", MAX_SUBID_LEN); 351 assert(sz < MAX_SUBID_LEN); 352 sz = strlcpy(h->reh_master_subscriber_id, "master", MAX_SUBID_LEN); 353 assert(sz < MAX_SUBID_LEN); 354 355 h->reh_delegate_channel_name = 356 _restarter_get_channel_name(restarter_name, 357 RESTARTER_CHANNEL_DELEGATE); 358 h->reh_master_channel_name = 359 _restarter_get_channel_name(restarter_name, 360 RESTARTER_CHANNEL_MASTER); 361 362 if (h->reh_delegate_channel_name == NULL || 363 h->reh_master_channel_name == NULL) { 364 free_restarter_event_handle(h); 365 return (ENOMEM); 366 } 367 368 if (sysevent_evc_bind(h->reh_delegate_channel_name, 369 &h->reh_delegate_channel, EVCH_CREAT|EVCH_HOLD_PEND) != 0) { 370 err = errno; 371 assert(err != EINVAL); 372 assert(err != ENOENT); 373 free_restarter_event_handle(h); 374 return (err); 375 } 376 377 if (sysevent_evc_bind(h->reh_master_channel_name, 378 &h->reh_master_channel, EVCH_CREAT|EVCH_HOLD_PEND) != 0) { 379 err = errno; 380 assert(err != EINVAL); 381 assert(err != ENOENT); 382 free_restarter_event_handle(h); 383 return (err); 384 } 385 386 h->reh_handler = event_handler; 387 388 assert(strlen(restarter_name) <= MAX_CLASS_LEN - 1); 389 assert(strlen(h->reh_delegate_subscriber_id) <= MAX_SUBID_LEN - 1); 390 assert(strlen(h->reh_master_subscriber_id) <= MAX_SUBID_LEN - 1); 391 392 if (sysevent_evc_subscribe(h->reh_delegate_channel, 393 h->reh_delegate_subscriber_id, EC_ALL, cb, h, EVCH_SUB_KEEP) != 0) { 394 err = errno; 395 assert(err != EINVAL); 396 free_restarter_event_handle(h); 397 return (err); 398 } 399 400 *rehp = h; 401 return (0); 402 } 403 404 restarter_event_handle_t * 405 restarter_event_get_handle(restarter_event_t *e) 406 { 407 assert(e != NULL && e->re_event_handle != NULL); 408 return (e->re_event_handle); 409 } 410 411 restarter_event_type_t 412 restarter_event_get_type(restarter_event_t *e) 413 { 414 assert(e != NULL); 415 return (e->re_type); 416 } 417 418 ssize_t 419 restarter_event_get_instance(restarter_event_t *e, char *inst, size_t sz) 420 { 421 assert(e != NULL && inst != NULL); 422 return ((ssize_t)strlcpy(inst, e->re_instance_name, sz)); 423 } 424 425 int 426 restarter_event_get_current_states(restarter_event_t *e, 427 restarter_instance_state_t *state, restarter_instance_state_t *next_state) 428 { 429 if (e == NULL) 430 return (-1); 431 *state = e->re_state; 432 *next_state = e->re_next_state; 433 return (0); 434 } 435 436 /* 437 * restarter_event_publish_retry() is a wrapper around sysevent_evc_publish(). 438 * In case, the event cannot be sent at the first attempt (sysevent_evc_publish 439 * returned EAGAIN - sysevent queue full), this function retries a few time 440 * and return ENOSPC if it reaches the retry limit. 441 * 442 * The arguments to this function map the arguments of sysevent_evc_publish(). 443 * 444 * On success, return 0. On error, return 445 * 446 * EFAULT - internal sysevent_evc_publish() error 447 * ENOMEM - internal sysevent_evc_publish() error 448 * EBADF - scp is invalid (sysevent_evc_publish() returned EINVAL) 449 * ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN) 450 */ 451 int 452 restarter_event_publish_retry(evchan_t *scp, const char *class, 453 const char *subclass, const char *vendor, const char *pub_name, 454 nvlist_t *attr_list, uint32_t flags) 455 { 456 int retries, ret; 457 useconds_t retry_int = INITIAL_COMMIT_RETRY_INT; 458 459 for (retries = 0; retries < MAX_COMMIT_RETRIES; retries++) { 460 ret = sysevent_evc_publish(scp, class, subclass, vendor, 461 pub_name, attr_list, flags); 462 if (ret == 0) 463 break; 464 465 switch (ret) { 466 case EAGAIN: 467 /* Queue is full */ 468 (void) usleep(retry_int); 469 470 retry_int = min(retry_int * 2, MAX_COMMIT_RETRY_INT); 471 break; 472 473 case EINVAL: 474 ret = EBADF; 475 /* FALLTHROUGH */ 476 477 case EFAULT: 478 case ENOMEM: 479 return (ret); 480 481 case EOVERFLOW: 482 default: 483 /* internal error - abort */ 484 bad_fail("sysevent_evc_publish", ret); 485 } 486 } 487 488 if (retries == MAX_COMMIT_RETRIES) 489 ret = ENOSPC; 490 491 return (ret); 492 } 493 494 /* 495 * Commit the state, next state, and auxiliary state into the repository. 496 * Let the graph engine know about the state change and error. On success, 497 * return 0. On error, return 498 * EINVAL - aux has spaces 499 * - inst is invalid or not an instance FMRI 500 * EPROTO - librestart compiled against different libscf 501 * ENOMEM - out of memory 502 * - repository server out of resources 503 * ENOTACTIVE - repository server not running 504 * ECONNABORTED - repository connection established, but then broken 505 * - unknown libscf error 506 * ENOENT - inst does not exist in the repository 507 * EPERM - insufficient permissions 508 * EACCESS - backend access denied 509 * EROFS - backend is readonly 510 * EFAULT - internal sysevent_evc_publish() error 511 * EBADF - h is invalid (sysevent_evc_publish() returned EINVAL) 512 * ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN) 513 */ 514 int 515 restarter_set_states(restarter_event_handle_t *h, const char *inst, 516 restarter_instance_state_t cur_state, 517 restarter_instance_state_t new_cur_state, 518 restarter_instance_state_t next_state, 519 restarter_instance_state_t new_next_state, restarter_error_t e, 520 const char *aux) 521 { 522 nvlist_t *attr; 523 scf_handle_t *scf_h; 524 instance_data_t id; 525 int ret = 0; 526 char *p = (char *)aux; 527 528 assert(h->reh_master_channel != NULL); 529 assert(h->reh_master_channel_name != NULL); 530 assert(h->reh_master_subscriber_id != NULL); 531 532 /* Validate format of auxiliary state: no spaces allowed */ 533 if (p != NULL) { 534 while (*p != '\0') { 535 if (isspace(*p)) 536 return (EINVAL); 537 p++; 538 } 539 } 540 541 if ((scf_h = scf_handle_create(SCF_VERSION)) == NULL) { 542 switch (scf_error()) { 543 case SCF_ERROR_VERSION_MISMATCH: 544 return (EPROTO); 545 546 case SCF_ERROR_NO_MEMORY: 547 return (ENOMEM); 548 549 default: 550 bad_fail("scf_handle_create", scf_error()); 551 } 552 } 553 554 if (scf_handle_bind(scf_h) == -1) { 555 scf_handle_destroy(scf_h); 556 switch (scf_error()) { 557 case SCF_ERROR_NO_SERVER: 558 return (ENOTACTIVE); 559 560 case SCF_ERROR_NO_RESOURCES: 561 return (ENOMEM); 562 563 case SCF_ERROR_INVALID_ARGUMENT: 564 case SCF_ERROR_IN_USE: 565 default: 566 bad_fail("scf_handle_bind", scf_error()); 567 } 568 } 569 570 if (nvlist_alloc(&attr, NV_UNIQUE_NAME, 0) != 0 || 571 nvlist_add_int32(attr, RESTARTER_NAME_STATE, new_cur_state) != 0 || 572 nvlist_add_int32(attr, RESTARTER_NAME_NEXT_STATE, new_next_state) 573 != 0 || 574 nvlist_add_int32(attr, RESTARTER_NAME_ERROR, e) != 0 || 575 nvlist_add_string(attr, RESTARTER_NAME_INSTANCE, inst) != 0) { 576 ret = ENOMEM; 577 } else { 578 id.i_fmri = inst; 579 id.i_state = cur_state; 580 id.i_next_state = next_state; 581 582 ret = _restarter_commit_states(scf_h, &id, new_cur_state, 583 new_next_state, aux); 584 585 if (ret == 0) { 586 ret = restarter_event_publish_retry( 587 h->reh_master_channel, "master", "state_change", 588 "com.sun", "librestart", attr, EVCH_NOSLEEP); 589 } 590 } 591 592 nvlist_free(attr); 593 (void) scf_handle_unbind(scf_h); 594 scf_handle_destroy(scf_h); 595 596 return (ret); 597 } 598 599 restarter_instance_state_t 600 restarter_string_to_state(char *string) 601 { 602 assert(string != NULL); 603 604 if (strcmp(string, SCF_STATE_STRING_NONE) == 0) 605 return (RESTARTER_STATE_NONE); 606 else if (strcmp(string, SCF_STATE_STRING_UNINIT) == 0) 607 return (RESTARTER_STATE_UNINIT); 608 else if (strcmp(string, SCF_STATE_STRING_MAINT) == 0) 609 return (RESTARTER_STATE_MAINT); 610 else if (strcmp(string, SCF_STATE_STRING_OFFLINE) == 0) 611 return (RESTARTER_STATE_OFFLINE); 612 else if (strcmp(string, SCF_STATE_STRING_DISABLED) == 0) 613 return (RESTARTER_STATE_DISABLED); 614 else if (strcmp(string, SCF_STATE_STRING_ONLINE) == 0) 615 return (RESTARTER_STATE_ONLINE); 616 else if (strcmp(string, SCF_STATE_STRING_DEGRADED) == 0) 617 return (RESTARTER_STATE_DEGRADED); 618 else { 619 return (RESTARTER_STATE_NONE); 620 } 621 } 622 623 ssize_t 624 restarter_state_to_string(restarter_instance_state_t state, char *string, 625 size_t len) 626 { 627 assert(string != NULL); 628 629 if (state == RESTARTER_STATE_NONE) 630 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_NONE, len)); 631 else if (state == RESTARTER_STATE_UNINIT) 632 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_UNINIT, len)); 633 else if (state == RESTARTER_STATE_MAINT) 634 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_MAINT, len)); 635 else if (state == RESTARTER_STATE_OFFLINE) 636 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_OFFLINE, 637 len)); 638 else if (state == RESTARTER_STATE_DISABLED) 639 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_DISABLED, 640 len)); 641 else if (state == RESTARTER_STATE_ONLINE) 642 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_ONLINE, len)); 643 else if (state == RESTARTER_STATE_DEGRADED) 644 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_DEGRADED, 645 len)); 646 else 647 return ((ssize_t)strlcpy(string, "unknown", len)); 648 } 649 650 /* 651 * Sets pg to the name property group of s_inst. If it doesn't exist, it is 652 * added. 653 * 654 * Fails with 655 * ECONNABORTED - repository disconnection or unknown libscf error 656 * EBADF - inst is not set 657 * ECANCELED - inst is deleted 658 * EPERM - permission is denied 659 * EACCES - backend denied access 660 * EROFS - backend readonly 661 */ 662 static int 663 instance_get_or_add_pg(scf_instance_t *inst, const char *name, 664 const char *type, uint32_t flags, scf_propertygroup_t *pg) 665 { 666 again: 667 if (scf_instance_get_pg(inst, name, pg) == 0) 668 return (0); 669 670 switch (scf_error()) { 671 case SCF_ERROR_CONNECTION_BROKEN: 672 default: 673 return (ECONNABORTED); 674 675 case SCF_ERROR_NOT_SET: 676 return (EBADF); 677 678 case SCF_ERROR_DELETED: 679 return (ECANCELED); 680 681 case SCF_ERROR_NOT_FOUND: 682 break; 683 684 case SCF_ERROR_HANDLE_MISMATCH: 685 case SCF_ERROR_INVALID_ARGUMENT: 686 bad_fail("scf_instance_get_pg", scf_error()); 687 } 688 689 if (scf_instance_add_pg(inst, name, type, flags, pg) == 0) 690 return (0); 691 692 switch (scf_error()) { 693 case SCF_ERROR_CONNECTION_BROKEN: 694 default: 695 return (ECONNABORTED); 696 697 case SCF_ERROR_DELETED: 698 return (ECANCELED); 699 700 case SCF_ERROR_EXISTS: 701 goto again; 702 703 case SCF_ERROR_PERMISSION_DENIED: 704 return (EPERM); 705 706 case SCF_ERROR_BACKEND_ACCESS: 707 return (EACCES); 708 709 case SCF_ERROR_BACKEND_READONLY: 710 return (EROFS); 711 712 case SCF_ERROR_HANDLE_MISMATCH: 713 case SCF_ERROR_INVALID_ARGUMENT: 714 case SCF_ERROR_NOT_SET: /* should be caught above */ 715 bad_fail("scf_instance_add_pg", scf_error()); 716 } 717 718 return (0); 719 } 720 721 /* 722 * Fails with 723 * ECONNABORTED 724 * ECANCELED - pg was deleted 725 */ 726 static int 727 tx_set_value(scf_transaction_t *tx, scf_transaction_entry_t *ent, 728 const char *pname, scf_type_t ty, scf_value_t *val) 729 { 730 int r; 731 732 for (;;) { 733 if (scf_transaction_property_change_type(tx, ent, pname, 734 ty) == 0) 735 break; 736 737 switch (scf_error()) { 738 case SCF_ERROR_CONNECTION_BROKEN: 739 default: 740 return (ECONNABORTED); 741 742 case SCF_ERROR_DELETED: 743 return (ECANCELED); 744 745 case SCF_ERROR_NOT_FOUND: 746 break; 747 748 case SCF_ERROR_HANDLE_MISMATCH: 749 case SCF_ERROR_INVALID_ARGUMENT: 750 case SCF_ERROR_IN_USE: 751 case SCF_ERROR_NOT_SET: 752 bad_fail("scf_transaction_property_change_type", 753 scf_error()); 754 } 755 756 if (scf_transaction_property_new(tx, ent, pname, ty) == 0) 757 break; 758 759 switch (scf_error()) { 760 case SCF_ERROR_CONNECTION_BROKEN: 761 default: 762 return (ECONNABORTED); 763 764 case SCF_ERROR_DELETED: 765 return (ECANCELED); 766 767 case SCF_ERROR_EXISTS: 768 break; 769 770 case SCF_ERROR_HANDLE_MISMATCH: 771 case SCF_ERROR_INVALID_ARGUMENT: 772 case SCF_ERROR_IN_USE: 773 case SCF_ERROR_NOT_SET: 774 bad_fail("scf_transaction_property_new", scf_error()); 775 } 776 } 777 778 r = scf_entry_add_value(ent, val); 779 assert(r == 0); 780 781 return (0); 782 } 783 784 /* 785 * Commit new_state, new_next_state, and aux to the repository for id. If 786 * successful, also set id's state and next-state as given, and return 0. 787 * Fails with 788 * ENOMEM - out of memory 789 * ECONNABORTED - repository connection broken 790 * - unknown libscf error 791 * EINVAL - id->i_fmri is invalid or not an instance FMRI 792 * ENOENT - id->i_fmri does not exist 793 * EPERM - insufficient permissions 794 * EACCES - backend access denied 795 * EROFS - backend is readonly 796 */ 797 int 798 _restarter_commit_states(scf_handle_t *h, instance_data_t *id, 799 restarter_instance_state_t new_state, 800 restarter_instance_state_t new_state_next, const char *aux) 801 { 802 char str_state[MAX_SCF_STATE_STRING_SZ]; 803 char str_new_state[MAX_SCF_STATE_STRING_SZ]; 804 char str_state_next[MAX_SCF_STATE_STRING_SZ]; 805 char str_new_state_next[MAX_SCF_STATE_STRING_SZ]; 806 int ret = 0, r; 807 struct timeval now; 808 ssize_t sz; 809 810 scf_transaction_t *t = NULL; 811 scf_transaction_entry_t *t_state = NULL, *t_state_next = NULL; 812 scf_transaction_entry_t *t_stime = NULL, *t_aux = NULL; 813 scf_value_t *v_state = NULL, *v_state_next = NULL, *v_stime = NULL; 814 scf_value_t *v_aux = NULL; 815 scf_instance_t *s_inst = NULL; 816 scf_propertygroup_t *pg = NULL; 817 818 assert(new_state != RESTARTER_STATE_NONE); 819 820 if ((s_inst = scf_instance_create(h)) == NULL || 821 (pg = scf_pg_create(h)) == NULL || 822 (t = scf_transaction_create(h)) == NULL || 823 (t_state = scf_entry_create(h)) == NULL || 824 (t_state_next = scf_entry_create(h)) == NULL || 825 (t_stime = scf_entry_create(h)) == NULL || 826 (t_aux = scf_entry_create(h)) == NULL || 827 (v_state = scf_value_create(h)) == NULL || 828 (v_state_next = scf_value_create(h)) == NULL || 829 (v_stime = scf_value_create(h)) == NULL || 830 (v_aux = scf_value_create(h)) == NULL) { 831 ret = ENOMEM; 832 goto out; 833 } 834 835 sz = restarter_state_to_string(new_state, str_new_state, 836 sizeof (str_new_state)); 837 assert(sz < sizeof (str_new_state)); 838 sz = restarter_state_to_string(new_state_next, str_new_state_next, 839 sizeof (str_new_state_next)); 840 assert(sz < sizeof (str_new_state_next)); 841 sz = restarter_state_to_string(id->i_state, str_state, 842 sizeof (str_state)); 843 assert(sz < sizeof (str_state)); 844 sz = restarter_state_to_string(id->i_next_state, str_state_next, 845 sizeof (str_state_next)); 846 assert(sz < sizeof (str_state_next)); 847 848 ret = gettimeofday(&now, NULL); 849 assert(ret != -1); 850 851 if (scf_handle_decode_fmri(h, id->i_fmri, NULL, NULL, s_inst, 852 NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) { 853 switch (scf_error()) { 854 case SCF_ERROR_CONNECTION_BROKEN: 855 default: 856 ret = ECONNABORTED; 857 break; 858 859 case SCF_ERROR_INVALID_ARGUMENT: 860 case SCF_ERROR_CONSTRAINT_VIOLATED: 861 ret = EINVAL; 862 break; 863 864 case SCF_ERROR_NOT_FOUND: 865 ret = ENOENT; 866 break; 867 868 case SCF_ERROR_HANDLE_MISMATCH: 869 bad_fail("scf_handle_decode_fmri", scf_error()); 870 } 871 goto out; 872 } 873 874 875 if (scf_value_set_astring(v_state, str_new_state) != 0 || 876 scf_value_set_astring(v_state_next, str_new_state_next) != 0) 877 bad_fail("scf_value_set_astring", scf_error()); 878 879 if (aux) { 880 if (scf_value_set_astring(v_aux, aux) != 0) 881 bad_fail("scf_value_set_astring", scf_error()); 882 } 883 884 if (scf_value_set_time(v_stime, now.tv_sec, now.tv_usec * 1000) != 0) 885 bad_fail("scf_value_set_time", scf_error()); 886 887 add_pg: 888 switch (r = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER, 889 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg)) { 890 case 0: 891 break; 892 893 case ECONNABORTED: 894 case EPERM: 895 case EACCES: 896 case EROFS: 897 ret = r; 898 goto out; 899 900 case ECANCELED: 901 ret = ENOENT; 902 goto out; 903 904 case EBADF: 905 default: 906 bad_fail("instance_get_or_add_pg", r); 907 } 908 909 for (;;) { 910 if (scf_transaction_start(t, pg) != 0) { 911 switch (scf_error()) { 912 case SCF_ERROR_CONNECTION_BROKEN: 913 default: 914 ret = ECONNABORTED; 915 goto out; 916 917 case SCF_ERROR_NOT_SET: 918 goto add_pg; 919 920 case SCF_ERROR_PERMISSION_DENIED: 921 ret = EPERM; 922 goto out; 923 924 case SCF_ERROR_BACKEND_ACCESS: 925 ret = EACCES; 926 goto out; 927 928 case SCF_ERROR_BACKEND_READONLY: 929 ret = EROFS; 930 goto out; 931 932 case SCF_ERROR_HANDLE_MISMATCH: 933 case SCF_ERROR_IN_USE: 934 bad_fail("scf_transaction_start", scf_error()); 935 } 936 } 937 938 if ((r = tx_set_value(t, t_state, SCF_PROPERTY_STATE, 939 SCF_TYPE_ASTRING, v_state)) != 0 || 940 (r = tx_set_value(t, t_state_next, SCF_PROPERTY_NEXT_STATE, 941 SCF_TYPE_ASTRING, v_state_next)) != 0 || 942 (r = tx_set_value(t, t_stime, SCF_PROPERTY_STATE_TIMESTAMP, 943 SCF_TYPE_TIME, v_stime)) != 0) { 944 switch (r) { 945 case ECONNABORTED: 946 ret = ECONNABORTED; 947 goto out; 948 949 case ECANCELED: 950 scf_transaction_reset(t); 951 goto add_pg; 952 953 default: 954 bad_fail("tx_set_value", r); 955 } 956 } 957 958 if (aux) { 959 if ((r = tx_set_value(t, t_aux, SCF_PROPERTY_AUX_STATE, 960 SCF_TYPE_ASTRING, v_aux)) != 0) { 961 switch (r) { 962 case ECONNABORTED: 963 ret = ECONNABORTED; 964 goto out; 965 966 case ECANCELED: 967 scf_transaction_reset(t); 968 goto add_pg; 969 970 default: 971 bad_fail("tx_set_value", r); 972 } 973 } 974 } 975 976 ret = scf_transaction_commit(t); 977 if (ret == 1) 978 break; 979 if (ret == -1) { 980 switch (scf_error()) { 981 case SCF_ERROR_CONNECTION_BROKEN: 982 default: 983 ret = ECONNABORTED; 984 goto out; 985 986 case SCF_ERROR_PERMISSION_DENIED: 987 ret = EPERM; 988 goto out; 989 990 case SCF_ERROR_BACKEND_ACCESS: 991 ret = EACCES; 992 goto out; 993 994 case SCF_ERROR_BACKEND_READONLY: 995 ret = EROFS; 996 goto out; 997 998 case SCF_ERROR_NOT_SET: 999 bad_fail("scf_transaction_commit", scf_error()); 1000 } 1001 } 1002 1003 scf_transaction_reset(t); 1004 if (scf_pg_update(pg) == -1) { 1005 switch (scf_error()) { 1006 case SCF_ERROR_CONNECTION_BROKEN: 1007 default: 1008 ret = ECONNABORTED; 1009 goto out; 1010 1011 case SCF_ERROR_NOT_SET: 1012 goto add_pg; 1013 } 1014 } 1015 } 1016 1017 id->i_state = new_state; 1018 id->i_next_state = new_state_next; 1019 ret = 0; 1020 1021 out: 1022 scf_transaction_destroy(t); 1023 scf_entry_destroy(t_state); 1024 scf_entry_destroy(t_state_next); 1025 scf_entry_destroy(t_stime); 1026 scf_entry_destroy(t_aux); 1027 scf_value_destroy(v_state); 1028 scf_value_destroy(v_state_next); 1029 scf_value_destroy(v_stime); 1030 scf_value_destroy(v_aux); 1031 scf_pg_destroy(pg); 1032 scf_instance_destroy(s_inst); 1033 1034 return (ret); 1035 } 1036 1037 /* 1038 * Fails with 1039 * EINVAL - type is invalid 1040 * ENOMEM 1041 * ECONNABORTED - repository connection broken 1042 * EBADF - s_inst is not set 1043 * ECANCELED - s_inst is deleted 1044 * EPERM - permission denied 1045 * EACCES - backend access denied 1046 * EROFS - backend readonly 1047 */ 1048 int 1049 restarter_remove_contract(scf_instance_t *s_inst, ctid_t contract_id, 1050 restarter_contract_type_t type) 1051 { 1052 scf_handle_t *h; 1053 scf_transaction_t *t = NULL; 1054 scf_transaction_entry_t *t_cid = NULL; 1055 scf_propertygroup_t *pg = NULL; 1056 scf_property_t *prop = NULL; 1057 scf_value_t *val; 1058 scf_iter_t *iter = NULL; 1059 const char *pname; 1060 int ret = 0, primary; 1061 uint64_t c; 1062 1063 switch (type) { 1064 case RESTARTER_CONTRACT_PRIMARY: 1065 primary = 1; 1066 break; 1067 case RESTARTER_CONTRACT_TRANSIENT: 1068 primary = 0; 1069 break; 1070 default: 1071 return (EINVAL); 1072 } 1073 1074 h = scf_instance_handle(s_inst); 1075 1076 pg = scf_pg_create(h); 1077 prop = scf_property_create(h); 1078 iter = scf_iter_create(h); 1079 t = scf_transaction_create(h); 1080 1081 if (pg == NULL || prop == NULL || iter == NULL || t == NULL) { 1082 ret = ENOMEM; 1083 goto remove_contract_cleanup; 1084 } 1085 1086 add: 1087 scf_transaction_destroy_children(t); 1088 ret = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER, 1089 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg); 1090 if (ret != 0) 1091 goto remove_contract_cleanup; 1092 1093 pname = primary? SCF_PROPERTY_CONTRACT : 1094 SCF_PROPERTY_TRANSIENT_CONTRACT; 1095 1096 for (;;) { 1097 if (scf_transaction_start(t, pg) != 0) { 1098 switch (scf_error()) { 1099 case SCF_ERROR_CONNECTION_BROKEN: 1100 default: 1101 ret = ECONNABORTED; 1102 goto remove_contract_cleanup; 1103 1104 case SCF_ERROR_DELETED: 1105 goto add; 1106 1107 case SCF_ERROR_PERMISSION_DENIED: 1108 ret = EPERM; 1109 goto remove_contract_cleanup; 1110 1111 case SCF_ERROR_BACKEND_ACCESS: 1112 ret = EACCES; 1113 goto remove_contract_cleanup; 1114 1115 case SCF_ERROR_BACKEND_READONLY: 1116 ret = EROFS; 1117 goto remove_contract_cleanup; 1118 1119 case SCF_ERROR_HANDLE_MISMATCH: 1120 case SCF_ERROR_IN_USE: 1121 case SCF_ERROR_NOT_SET: 1122 bad_fail("scf_transaction_start", scf_error()); 1123 } 1124 } 1125 1126 t_cid = scf_entry_create(h); 1127 1128 if (scf_pg_get_property(pg, pname, prop) == 0) { 1129 replace: 1130 if (scf_transaction_property_change_type(t, t_cid, 1131 pname, 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_NOT_FOUND: 1143 goto new; 1144 1145 case SCF_ERROR_HANDLE_MISMATCH: 1146 case SCF_ERROR_INVALID_ARGUMENT: 1147 case SCF_ERROR_IN_USE: 1148 case SCF_ERROR_NOT_SET: 1149 bad_fail( 1150 "scf_transaction_property_changetype", 1151 scf_error()); 1152 } 1153 } 1154 1155 if (scf_property_is_type(prop, SCF_TYPE_COUNT) == 0) { 1156 if (scf_iter_property_values(iter, prop) != 0) { 1157 switch (scf_error()) { 1158 case SCF_ERROR_CONNECTION_BROKEN: 1159 default: 1160 ret = ECONNABORTED; 1161 goto remove_contract_cleanup; 1162 1163 case SCF_ERROR_NOT_SET: 1164 case SCF_ERROR_HANDLE_MISMATCH: 1165 bad_fail( 1166 "scf_iter_property_values", 1167 scf_error()); 1168 } 1169 } 1170 1171 next_val: 1172 val = scf_value_create(h); 1173 if (val == NULL) { 1174 assert(scf_error() == 1175 SCF_ERROR_NO_MEMORY); 1176 ret = ENOMEM; 1177 goto remove_contract_cleanup; 1178 } 1179 1180 ret = scf_iter_next_value(iter, val); 1181 if (ret == -1) { 1182 switch (scf_error()) { 1183 case SCF_ERROR_CONNECTION_BROKEN: 1184 ret = ECONNABORTED; 1185 goto remove_contract_cleanup; 1186 1187 case SCF_ERROR_DELETED: 1188 scf_value_destroy(val); 1189 goto add; 1190 1191 case SCF_ERROR_HANDLE_MISMATCH: 1192 case SCF_ERROR_INVALID_ARGUMENT: 1193 case SCF_ERROR_PERMISSION_DENIED: 1194 default: 1195 bad_fail("scf_iter_next_value", 1196 scf_error()); 1197 } 1198 } 1199 1200 if (ret == 1) { 1201 ret = scf_value_get_count(val, &c); 1202 assert(ret == 0); 1203 1204 if (c != contract_id) { 1205 ret = scf_entry_add_value(t_cid, 1206 val); 1207 assert(ret == 0); 1208 } else { 1209 scf_value_destroy(val); 1210 } 1211 1212 goto next_val; 1213 } 1214 1215 scf_value_destroy(val); 1216 } else { 1217 switch (scf_error()) { 1218 case SCF_ERROR_CONNECTION_BROKEN: 1219 default: 1220 ret = ECONNABORTED; 1221 goto remove_contract_cleanup; 1222 1223 case SCF_ERROR_TYPE_MISMATCH: 1224 break; 1225 1226 case SCF_ERROR_INVALID_ARGUMENT: 1227 case SCF_ERROR_NOT_SET: 1228 bad_fail("scf_property_is_type", 1229 scf_error()); 1230 } 1231 } 1232 } else { 1233 switch (scf_error()) { 1234 case SCF_ERROR_CONNECTION_BROKEN: 1235 default: 1236 ret = ECONNABORTED; 1237 goto remove_contract_cleanup; 1238 1239 case SCF_ERROR_DELETED: 1240 scf_entry_destroy(t_cid); 1241 goto add; 1242 1243 case SCF_ERROR_NOT_FOUND: 1244 break; 1245 1246 case SCF_ERROR_HANDLE_MISMATCH: 1247 case SCF_ERROR_INVALID_ARGUMENT: 1248 case SCF_ERROR_NOT_SET: 1249 bad_fail("scf_pg_get_property", scf_error()); 1250 } 1251 1252 new: 1253 if (scf_transaction_property_new(t, t_cid, pname, 1254 SCF_TYPE_COUNT) != 0) { 1255 switch (scf_error()) { 1256 case SCF_ERROR_CONNECTION_BROKEN: 1257 default: 1258 ret = ECONNABORTED; 1259 goto remove_contract_cleanup; 1260 1261 case SCF_ERROR_DELETED: 1262 scf_entry_destroy(t_cid); 1263 goto add; 1264 1265 case SCF_ERROR_EXISTS: 1266 goto replace; 1267 1268 case SCF_ERROR_HANDLE_MISMATCH: 1269 case SCF_ERROR_INVALID_ARGUMENT: 1270 case SCF_ERROR_NOT_SET: 1271 bad_fail("scf_transaction_property_new", 1272 scf_error()); 1273 } 1274 } 1275 } 1276 1277 ret = scf_transaction_commit(t); 1278 if (ret == -1) { 1279 switch (scf_error()) { 1280 case SCF_ERROR_CONNECTION_BROKEN: 1281 default: 1282 ret = ECONNABORTED; 1283 goto remove_contract_cleanup; 1284 1285 case SCF_ERROR_DELETED: 1286 goto add; 1287 1288 case SCF_ERROR_PERMISSION_DENIED: 1289 ret = EPERM; 1290 goto remove_contract_cleanup; 1291 1292 case SCF_ERROR_BACKEND_ACCESS: 1293 ret = EACCES; 1294 goto remove_contract_cleanup; 1295 1296 case SCF_ERROR_BACKEND_READONLY: 1297 ret = EROFS; 1298 goto remove_contract_cleanup; 1299 1300 case SCF_ERROR_NOT_SET: 1301 bad_fail("scf_transaction_commit", scf_error()); 1302 } 1303 } 1304 if (ret == 1) { 1305 ret = 0; 1306 break; 1307 } 1308 1309 scf_transaction_destroy_children(t); 1310 if (scf_pg_update(pg) == -1) { 1311 switch (scf_error()) { 1312 case SCF_ERROR_CONNECTION_BROKEN: 1313 default: 1314 ret = ECONNABORTED; 1315 goto remove_contract_cleanup; 1316 1317 case SCF_ERROR_DELETED: 1318 goto add; 1319 1320 case SCF_ERROR_NOT_SET: 1321 bad_fail("scf_pg_update", scf_error()); 1322 } 1323 } 1324 } 1325 1326 remove_contract_cleanup: 1327 scf_transaction_destroy_children(t); 1328 scf_transaction_destroy(t); 1329 scf_iter_destroy(iter); 1330 scf_property_destroy(prop); 1331 scf_pg_destroy(pg); 1332 1333 return (ret); 1334 } 1335 1336 /* 1337 * Fails with 1338 * EINVAL - type is invalid 1339 * ENOMEM 1340 * ECONNABORTED - repository disconnection 1341 * EBADF - s_inst is not set 1342 * ECANCELED - s_inst is deleted 1343 * EPERM 1344 * EACCES 1345 * EROFS 1346 */ 1347 int 1348 restarter_store_contract(scf_instance_t *s_inst, ctid_t contract_id, 1349 restarter_contract_type_t type) 1350 { 1351 scf_handle_t *h; 1352 scf_transaction_t *t = NULL; 1353 scf_transaction_entry_t *t_cid = NULL; 1354 scf_value_t *val; 1355 scf_propertygroup_t *pg = NULL; 1356 scf_property_t *prop = NULL; 1357 scf_iter_t *iter = NULL; 1358 const char *pname; 1359 int ret = 0, primary; 1360 1361 if (type == RESTARTER_CONTRACT_PRIMARY) 1362 primary = 1; 1363 else if (type == RESTARTER_CONTRACT_TRANSIENT) 1364 primary = 0; 1365 else 1366 return (EINVAL); 1367 1368 h = scf_instance_handle(s_inst); 1369 1370 pg = scf_pg_create(h); 1371 prop = scf_property_create(h); 1372 iter = scf_iter_create(h); 1373 t = scf_transaction_create(h); 1374 1375 if (pg == NULL || prop == NULL || iter == NULL || t == NULL) { 1376 ret = ENOMEM; 1377 goto out; 1378 } 1379 1380 add: 1381 scf_transaction_destroy_children(t); 1382 ret = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER, 1383 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg); 1384 if (ret != 0) 1385 goto out; 1386 1387 pname = primary ? SCF_PROPERTY_CONTRACT : 1388 SCF_PROPERTY_TRANSIENT_CONTRACT; 1389 1390 for (;;) { 1391 if (scf_transaction_start(t, pg) != 0) { 1392 switch (scf_error()) { 1393 case SCF_ERROR_CONNECTION_BROKEN: 1394 default: 1395 ret = ECONNABORTED; 1396 goto out; 1397 1398 case SCF_ERROR_DELETED: 1399 goto add; 1400 1401 case SCF_ERROR_PERMISSION_DENIED: 1402 ret = EPERM; 1403 goto out; 1404 1405 case SCF_ERROR_BACKEND_ACCESS: 1406 ret = EACCES; 1407 goto out; 1408 1409 case SCF_ERROR_BACKEND_READONLY: 1410 ret = EROFS; 1411 goto out; 1412 1413 case SCF_ERROR_HANDLE_MISMATCH: 1414 case SCF_ERROR_IN_USE: 1415 case SCF_ERROR_NOT_SET: 1416 bad_fail("scf_transaction_start", scf_error()); 1417 } 1418 } 1419 1420 t_cid = scf_entry_create(h); 1421 if (t_cid == NULL) { 1422 ret = ENOMEM; 1423 goto out; 1424 } 1425 1426 if (scf_pg_get_property(pg, pname, prop) == 0) { 1427 replace: 1428 if (scf_transaction_property_change_type(t, t_cid, 1429 pname, SCF_TYPE_COUNT) != 0) { 1430 switch (scf_error()) { 1431 case SCF_ERROR_CONNECTION_BROKEN: 1432 default: 1433 ret = ECONNABORTED; 1434 goto out; 1435 1436 case SCF_ERROR_DELETED: 1437 scf_entry_destroy(t_cid); 1438 goto add; 1439 1440 case SCF_ERROR_NOT_FOUND: 1441 goto new; 1442 1443 case SCF_ERROR_HANDLE_MISMATCH: 1444 case SCF_ERROR_INVALID_ARGUMENT: 1445 case SCF_ERROR_IN_USE: 1446 case SCF_ERROR_NOT_SET: 1447 bad_fail( 1448 "scf_transaction_propert_change_type", 1449 scf_error()); 1450 } 1451 } 1452 1453 if (scf_property_is_type(prop, SCF_TYPE_COUNT) == 0) { 1454 if (scf_iter_property_values(iter, prop) != 0) { 1455 switch (scf_error()) { 1456 case SCF_ERROR_CONNECTION_BROKEN: 1457 default: 1458 ret = ECONNABORTED; 1459 goto out; 1460 1461 case SCF_ERROR_NOT_SET: 1462 case SCF_ERROR_HANDLE_MISMATCH: 1463 bad_fail( 1464 "scf_iter_property_values", 1465 scf_error()); 1466 } 1467 } 1468 1469 next_val: 1470 val = scf_value_create(h); 1471 if (val == NULL) { 1472 assert(scf_error() == 1473 SCF_ERROR_NO_MEMORY); 1474 ret = ENOMEM; 1475 goto out; 1476 } 1477 1478 ret = scf_iter_next_value(iter, val); 1479 if (ret == -1) { 1480 switch (scf_error()) { 1481 case SCF_ERROR_CONNECTION_BROKEN: 1482 default: 1483 ret = ECONNABORTED; 1484 goto out; 1485 1486 case SCF_ERROR_DELETED: 1487 scf_value_destroy(val); 1488 goto add; 1489 1490 case SCF_ERROR_HANDLE_MISMATCH: 1491 case SCF_ERROR_INVALID_ARGUMENT: 1492 case SCF_ERROR_PERMISSION_DENIED: 1493 bad_fail( 1494 "scf_iter_next_value", 1495 scf_error()); 1496 } 1497 } 1498 1499 if (ret == 1) { 1500 ret = scf_entry_add_value(t_cid, val); 1501 assert(ret == 0); 1502 1503 goto next_val; 1504 } 1505 1506 scf_value_destroy(val); 1507 } else { 1508 switch (scf_error()) { 1509 case SCF_ERROR_CONNECTION_BROKEN: 1510 default: 1511 ret = ECONNABORTED; 1512 goto out; 1513 1514 case SCF_ERROR_TYPE_MISMATCH: 1515 break; 1516 1517 case SCF_ERROR_INVALID_ARGUMENT: 1518 case SCF_ERROR_NOT_SET: 1519 bad_fail("scf_property_is_type", 1520 scf_error()); 1521 } 1522 } 1523 } else { 1524 switch (scf_error()) { 1525 case SCF_ERROR_CONNECTION_BROKEN: 1526 default: 1527 ret = ECONNABORTED; 1528 goto out; 1529 1530 case SCF_ERROR_DELETED: 1531 scf_entry_destroy(t_cid); 1532 goto add; 1533 1534 case SCF_ERROR_NOT_FOUND: 1535 break; 1536 1537 case SCF_ERROR_HANDLE_MISMATCH: 1538 case SCF_ERROR_INVALID_ARGUMENT: 1539 case SCF_ERROR_NOT_SET: 1540 bad_fail("scf_pg_get_property", scf_error()); 1541 } 1542 1543 new: 1544 if (scf_transaction_property_new(t, t_cid, pname, 1545 SCF_TYPE_COUNT) != 0) { 1546 switch (scf_error()) { 1547 case SCF_ERROR_CONNECTION_BROKEN: 1548 default: 1549 ret = ECONNABORTED; 1550 goto out; 1551 1552 case SCF_ERROR_DELETED: 1553 scf_entry_destroy(t_cid); 1554 goto add; 1555 1556 case SCF_ERROR_EXISTS: 1557 goto replace; 1558 1559 case SCF_ERROR_HANDLE_MISMATCH: 1560 case SCF_ERROR_INVALID_ARGUMENT: 1561 case SCF_ERROR_NOT_SET: 1562 bad_fail("scf_transaction_property_new", 1563 scf_error()); 1564 } 1565 } 1566 } 1567 1568 val = scf_value_create(h); 1569 if (val == NULL) { 1570 assert(scf_error() == SCF_ERROR_NO_MEMORY); 1571 ret = ENOMEM; 1572 goto out; 1573 } 1574 1575 scf_value_set_count(val, contract_id); 1576 ret = scf_entry_add_value(t_cid, val); 1577 assert(ret == 0); 1578 1579 ret = scf_transaction_commit(t); 1580 if (ret == -1) { 1581 switch (scf_error()) { 1582 case SCF_ERROR_CONNECTION_BROKEN: 1583 default: 1584 ret = ECONNABORTED; 1585 goto out; 1586 1587 case SCF_ERROR_DELETED: 1588 goto add; 1589 1590 case SCF_ERROR_PERMISSION_DENIED: 1591 ret = EPERM; 1592 goto out; 1593 1594 case SCF_ERROR_BACKEND_ACCESS: 1595 ret = EACCES; 1596 goto out; 1597 1598 case SCF_ERROR_BACKEND_READONLY: 1599 ret = EROFS; 1600 goto out; 1601 1602 case SCF_ERROR_NOT_SET: 1603 bad_fail("scf_transaction_commit", scf_error()); 1604 } 1605 } 1606 if (ret == 1) { 1607 ret = 0; 1608 break; 1609 } 1610 1611 scf_transaction_destroy_children(t); 1612 if (scf_pg_update(pg) == -1) { 1613 switch (scf_error()) { 1614 case SCF_ERROR_CONNECTION_BROKEN: 1615 default: 1616 ret = ECONNABORTED; 1617 goto out; 1618 1619 case SCF_ERROR_DELETED: 1620 goto add; 1621 1622 case SCF_ERROR_NOT_SET: 1623 bad_fail("scf_pg_update", scf_error()); 1624 } 1625 } 1626 } 1627 1628 out: 1629 scf_transaction_destroy_children(t); 1630 scf_transaction_destroy(t); 1631 scf_iter_destroy(iter); 1632 scf_property_destroy(prop); 1633 scf_pg_destroy(pg); 1634 1635 return (ret); 1636 } 1637 1638 int 1639 restarter_rm_libs_loadable() 1640 { 1641 void *libhndl; 1642 1643 if (method_context_safety) 1644 return (1); 1645 1646 if ((libhndl = dlopen("libpool.so", RTLD_LAZY | RTLD_LOCAL)) == NULL) 1647 return (0); 1648 1649 (void) dlclose(libhndl); 1650 1651 if ((libhndl = dlopen("libproject.so", RTLD_LAZY | RTLD_LOCAL)) == NULL) 1652 return (0); 1653 1654 (void) dlclose(libhndl); 1655 1656 method_context_safety = 1; 1657 1658 return (1); 1659 } 1660 1661 static int 1662 get_astring_val(scf_propertygroup_t *pg, const char *name, char *buf, 1663 size_t bufsz, scf_property_t *prop, scf_value_t *val) 1664 { 1665 ssize_t szret; 1666 1667 if (pg == NULL) 1668 return (-1); 1669 1670 if (scf_pg_get_property(pg, name, prop) != SCF_SUCCESS) { 1671 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN) 1672 uu_die(rcbroken); 1673 return (-1); 1674 } 1675 1676 if (scf_property_get_value(prop, val) != SCF_SUCCESS) { 1677 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN) 1678 uu_die(rcbroken); 1679 return (-1); 1680 } 1681 1682 szret = scf_value_get_astring(val, buf, bufsz); 1683 1684 return (szret >= 0 ? 0 : -1); 1685 } 1686 1687 static int 1688 get_boolean_val(scf_propertygroup_t *pg, const char *name, uint8_t *b, 1689 scf_property_t *prop, scf_value_t *val) 1690 { 1691 if (scf_pg_get_property(pg, name, prop) != SCF_SUCCESS) { 1692 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN) 1693 uu_die(rcbroken); 1694 return (-1); 1695 } 1696 1697 if (scf_property_get_value(prop, val) != SCF_SUCCESS) { 1698 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN) 1699 uu_die(rcbroken); 1700 return (-1); 1701 } 1702 1703 if (scf_value_get_boolean(val, b)) 1704 return (-1); 1705 1706 return (0); 1707 } 1708 1709 /* 1710 * Try to load mcp->pwd, if it isn't already. 1711 * Fails with 1712 * ENOMEM - malloc() failed 1713 * ENOENT - no entry found 1714 * EIO - I/O error 1715 * EMFILE - process out of file descriptors 1716 * ENFILE - system out of file handles 1717 */ 1718 static int 1719 lookup_pwd(struct method_context *mcp) 1720 { 1721 struct passwd *pwdp; 1722 1723 if (mcp->pwbuf != NULL && mcp->pwd.pw_uid == mcp->uid) 1724 return (0); 1725 1726 if (mcp->pwbuf == NULL) { 1727 mcp->pwbufsz = sysconf(_SC_GETPW_R_SIZE_MAX); 1728 assert(mcp->pwbufsz >= 0); 1729 mcp->pwbuf = malloc(mcp->pwbufsz); 1730 if (mcp->pwbuf == NULL) 1731 return (ENOMEM); 1732 } 1733 1734 do { 1735 errno = 0; 1736 pwdp = getpwuid_r(mcp->uid, &mcp->pwd, mcp->pwbuf, 1737 mcp->pwbufsz); 1738 } while (pwdp == NULL && errno == EINTR); 1739 if (pwdp != NULL) 1740 return (0); 1741 1742 free(mcp->pwbuf); 1743 mcp->pwbuf = NULL; 1744 1745 switch (errno) { 1746 case 0: 1747 default: 1748 /* 1749 * Until bug 5065780 is fixed, getpwuid_r() can fail with 1750 * ENOENT, particularly on the miniroot. Since the 1751 * documentation is inaccurate, we'll return ENOENT for unknown 1752 * errors. 1753 */ 1754 return (ENOENT); 1755 1756 case EIO: 1757 case EMFILE: 1758 case ENFILE: 1759 return (errno); 1760 1761 case ERANGE: 1762 bad_fail("getpwuid_r", errno); 1763 /* NOTREACHED */ 1764 } 1765 } 1766 1767 /* 1768 * Get the user id for str. Returns 0 on success or 1769 * ERANGE the uid is too big 1770 * EINVAL the string starts with a digit, but is not a valid uid 1771 * ENOMEM out of memory 1772 * ENOENT no passwd entry for str 1773 * EIO an I/O error has occurred 1774 * EMFILE/ENFILE out of file descriptors 1775 */ 1776 int 1777 get_uid(const char *str, struct method_context *ci, uid_t *uidp) 1778 { 1779 if (isdigit(str[0])) { 1780 uid_t uid; 1781 char *cp; 1782 1783 errno = 0; 1784 uid = strtol(str, &cp, 10); 1785 1786 if (uid == 0 && errno != 0) { 1787 assert(errno != EINVAL); 1788 return (errno); 1789 } 1790 1791 for (; *cp != '\0'; ++cp) 1792 if (*cp != ' ' || *cp != '\t') 1793 return (EINVAL); 1794 1795 if (uid > UID_MAX) 1796 return (EINVAL); 1797 1798 *uidp = uid; 1799 return (0); 1800 } else { 1801 struct passwd *pwdp; 1802 1803 if (ci->pwbuf == NULL) { 1804 ci->pwbufsz = sysconf(_SC_GETPW_R_SIZE_MAX); 1805 ci->pwbuf = malloc(ci->pwbufsz); 1806 if (ci->pwbuf == NULL) 1807 return (ENOMEM); 1808 } 1809 1810 do { 1811 errno = 0; 1812 pwdp = 1813 getpwnam_r(str, &ci->pwd, ci->pwbuf, ci->pwbufsz); 1814 } while (pwdp == NULL && errno == EINTR); 1815 1816 if (pwdp != NULL) { 1817 *uidp = ci->pwd.pw_uid; 1818 return (0); 1819 } else { 1820 free(ci->pwbuf); 1821 ci->pwbuf = NULL; 1822 switch (errno) { 1823 case 0: 1824 return (ENOENT); 1825 1826 case ENOENT: 1827 case EIO: 1828 case EMFILE: 1829 case ENFILE: 1830 return (errno); 1831 1832 case ERANGE: 1833 default: 1834 bad_fail("getpwnam_r", errno); 1835 /* NOTREACHED */ 1836 } 1837 } 1838 } 1839 } 1840 1841 gid_t 1842 get_gid(const char *str) 1843 { 1844 if (isdigit(str[0])) { 1845 gid_t gid; 1846 char *cp; 1847 1848 errno = 0; 1849 gid = strtol(str, &cp, 10); 1850 1851 if (gid == 0 && errno != 0) 1852 return ((gid_t)-1); 1853 1854 for (; *cp != '\0'; ++cp) 1855 if (*cp != ' ' || *cp != '\t') 1856 return ((gid_t)-1); 1857 1858 return (gid); 1859 } else { 1860 struct group grp, *ret; 1861 char *buffer; 1862 size_t buflen; 1863 1864 buflen = sysconf(_SC_GETGR_R_SIZE_MAX); 1865 buffer = malloc(buflen); 1866 if (buffer == NULL) 1867 uu_die(allocfail); 1868 1869 errno = 0; 1870 ret = getgrnam_r(str, &grp, buffer, buflen); 1871 free(buffer); 1872 1873 return (ret == NULL ? (gid_t)-1 : grp.gr_gid); 1874 } 1875 } 1876 1877 /* 1878 * Fails with 1879 * ENOMEM - out of memory 1880 * ENOENT - no passwd entry 1881 * no project entry 1882 * EIO - an I/O error occurred 1883 * EMFILE - the process is out of file descriptors 1884 * ENFILE - the system is out of file handles 1885 * ERANGE - the project id is out of range 1886 * EINVAL - str is invalid 1887 * E2BIG - the project entry was too big 1888 * -1 - the name service switch is misconfigured 1889 */ 1890 int 1891 get_projid(const char *str, struct method_context *cip) 1892 { 1893 int ret; 1894 void *buf; 1895 const size_t bufsz = PROJECT_BUFSZ; 1896 struct project proj, *pp; 1897 1898 if (strcmp(str, ":default") == 0) { 1899 if (cip->uid == 0) { 1900 /* Don't change project for root services */ 1901 cip->project = NULL; 1902 return (0); 1903 } 1904 1905 switch (ret = lookup_pwd(cip)) { 1906 case 0: 1907 break; 1908 1909 case ENOMEM: 1910 case ENOENT: 1911 case EIO: 1912 case EMFILE: 1913 case ENFILE: 1914 return (ret); 1915 1916 default: 1917 bad_fail("lookup_pwd", ret); 1918 } 1919 1920 buf = malloc(bufsz); 1921 if (buf == NULL) 1922 return (ENOMEM); 1923 1924 do { 1925 errno = 0; 1926 pp = getdefaultproj(cip->pwd.pw_name, &proj, buf, 1927 bufsz); 1928 } while (pp == NULL && errno == EINTR); 1929 1930 /* to be continued ... */ 1931 } else { 1932 projid_t projid; 1933 char *cp; 1934 1935 if (!isdigit(str[0])) { 1936 cip->project = strdup(str); 1937 return (cip->project != NULL ? 0 : ENOMEM); 1938 } 1939 1940 errno = 0; 1941 projid = strtol(str, &cp, 10); 1942 1943 if (projid == 0 && errno != 0) { 1944 assert(errno == ERANGE); 1945 return (errno); 1946 } 1947 1948 for (; *cp != '\0'; ++cp) 1949 if (*cp != ' ' || *cp != '\t') 1950 return (EINVAL); 1951 1952 if (projid > MAXPROJID) 1953 return (ERANGE); 1954 1955 buf = malloc(bufsz); 1956 if (buf == NULL) 1957 return (ENOMEM); 1958 1959 do { 1960 errno = 0; 1961 pp = getprojbyid(projid, &proj, buf, bufsz); 1962 } while (pp == NULL && errno == EINTR); 1963 } 1964 1965 if (pp) { 1966 cip->project = strdup(pp->pj_name); 1967 free(buf); 1968 return (cip->project != NULL ? 0 : ENOMEM); 1969 } 1970 1971 free(buf); 1972 1973 switch (errno) { 1974 case 0: 1975 return (ENOENT); 1976 1977 case EIO: 1978 case EMFILE: 1979 case ENFILE: 1980 return (errno); 1981 1982 case ERANGE: 1983 return (E2BIG); 1984 1985 default: 1986 return (-1); 1987 } 1988 } 1989 1990 /* 1991 * Parse the supp_groups property value and populate ci->groups. Returns 1992 * EINVAL (get_gid() failed for one of the components), E2BIG (the property has 1993 * more than NGROUPS_MAX-1 groups), or 0 on success. 1994 */ 1995 int 1996 get_groups(char *str, struct method_context *ci) 1997 { 1998 char *cp, *end, *next; 1999 uint_t i; 2000 2001 const char * const whitespace = " \t"; 2002 const char * const illegal = ", \t"; 2003 2004 if (str[0] == '\0') { 2005 ci->ngroups = 0; 2006 return (0); 2007 } 2008 2009 for (cp = str, i = 0; *cp != '\0'; ) { 2010 /* skip whitespace */ 2011 cp += strspn(cp, whitespace); 2012 2013 /* find the end */ 2014 end = cp + strcspn(cp, illegal); 2015 2016 /* skip whitespace after end */ 2017 next = end + strspn(end, whitespace); 2018 2019 /* if there's a comma, it separates the fields */ 2020 if (*next == ',') 2021 ++next; 2022 2023 *end = '\0'; 2024 2025 if ((ci->groups[i] = get_gid(cp)) == (gid_t)-1) { 2026 ci->ngroups = 0; 2027 return (EINVAL); 2028 } 2029 2030 ++i; 2031 if (i > NGROUPS_MAX - 1) { 2032 ci->ngroups = 0; 2033 return (E2BIG); 2034 } 2035 2036 cp = next; 2037 } 2038 2039 ci->ngroups = i; 2040 return (0); 2041 } 2042 2043 2044 /* 2045 * Return an error message structure containing the error message 2046 * with context, and the error so the caller can make a decision 2047 * on what to do next. 2048 * 2049 * Because get_ids uses the mc_error_create() function which can 2050 * reallocate the merr, this function must return the merr pointer 2051 * in case it was reallocated. 2052 */ 2053 static mc_error_t * 2054 get_profile(scf_propertygroup_t *methpg, scf_propertygroup_t *instpg, 2055 scf_property_t *prop, scf_value_t *val, const char *cmdline, 2056 struct method_context *ci, mc_error_t *merr) 2057 { 2058 char *buf = ci->vbuf; 2059 ssize_t buf_sz = ci->vbuf_sz; 2060 char cmd[PATH_MAX]; 2061 char *cp, *value; 2062 const char *cmdp; 2063 execattr_t *eap; 2064 mc_error_t *err = merr; 2065 int r; 2066 2067 if (!(get_astring_val(methpg, SCF_PROPERTY_PROFILE, buf, buf_sz, prop, 2068 val) == 0 || get_astring_val(instpg, SCF_PROPERTY_PROFILE, buf, 2069 buf_sz, prop, val) == 0)) 2070 return (mc_error_create(merr, scf_error(), 2071 "Method context requires a profile, but the \"%s\" " 2072 "property could not be read. scf_error is %s", 2073 SCF_PROPERTY_PROFILE, scf_strerror(scf_error()))); 2074 2075 /* Extract the command from the command line. */ 2076 cp = strpbrk(cmdline, " \t"); 2077 2078 if (cp == NULL) { 2079 cmdp = cmdline; 2080 } else { 2081 (void) strncpy(cmd, cmdline, cp - cmdline); 2082 cmd[cp - cmdline] = '\0'; 2083 cmdp = cmd; 2084 } 2085 2086 /* Require that cmdp[0] == '/'? */ 2087 2088 eap = getexecprof(buf, KV_COMMAND, cmdp, GET_ONE); 2089 if (eap == NULL) 2090 return (mc_error_create(merr, ENOENT, 2091 "Could not find the execution profile \"%s\", " 2092 "command %s.", buf, cmdp)); 2093 2094 /* Based on pfexec.c */ 2095 2096 /* Get the euid first so we don't override ci->pwd for the uid. */ 2097 if ((value = kva_match(eap->attr, EXECATTR_EUID_KW)) != NULL) { 2098 if ((r = get_uid(value, ci, &ci->euid)) != 0) { 2099 ci->euid = (uid_t)-1; 2100 err = mc_error_create(merr, r, 2101 "Could not interpret profile euid value \"%s\", " 2102 "from the execution profile \"%s\", error %d.", 2103 value, buf, r); 2104 goto out; 2105 } 2106 } 2107 2108 if ((value = kva_match(eap->attr, EXECATTR_UID_KW)) != NULL) { 2109 if ((r = get_uid(value, ci, &ci->uid)) != 0) { 2110 ci->euid = ci->uid = (uid_t)-1; 2111 err = mc_error_create(merr, r, 2112 "Could not interpret profile uid value \"%s\", " 2113 "from the execution profile \"%s\", error %d.", 2114 value, buf, r); 2115 goto out; 2116 } 2117 ci->euid = ci->uid; 2118 } 2119 2120 if ((value = kva_match(eap->attr, EXECATTR_GID_KW)) != NULL) { 2121 ci->egid = ci->gid = get_gid(value); 2122 if (ci->gid == (gid_t)-1) { 2123 err = mc_error_create(merr, EINVAL, 2124 "Could not interpret profile gid value \"%s\", " 2125 "from the execution profile \"%s\".", value, buf); 2126 goto out; 2127 } 2128 } 2129 2130 if ((value = kva_match(eap->attr, EXECATTR_EGID_KW)) != NULL) { 2131 ci->egid = get_gid(value); 2132 if (ci->egid == (gid_t)-1) { 2133 err = mc_error_create(merr, EINVAL, 2134 "Could not interpret profile egid value \"%s\", " 2135 "from the execution profile \"%s\".", value, buf); 2136 goto out; 2137 } 2138 } 2139 2140 if ((value = kva_match(eap->attr, EXECATTR_LPRIV_KW)) != NULL) { 2141 ci->lpriv_set = priv_str_to_set(value, ",", NULL); 2142 if (ci->lpriv_set == NULL) { 2143 if (errno != EINVAL) 2144 err = mc_error_create(merr, ENOMEM, 2145 ALLOCFAIL); 2146 else 2147 err = mc_error_create(merr, EINVAL, 2148 "Could not interpret profile " 2149 "limitprivs value \"%s\", from " 2150 "the execution profile \"%s\".", 2151 value, buf); 2152 goto out; 2153 } 2154 } 2155 2156 if ((value = kva_match(eap->attr, EXECATTR_IPRIV_KW)) != NULL) { 2157 ci->priv_set = priv_str_to_set(value, ",", NULL); 2158 if (ci->priv_set == NULL) { 2159 if (errno != EINVAL) 2160 err = mc_error_create(merr, ENOMEM, 2161 ALLOCFAIL); 2162 else 2163 err = mc_error_create(merr, EINVAL, 2164 "Could not interpret profile privs value " 2165 "\"%s\", from the execution profile " 2166 "\"%s\".", value, buf); 2167 goto out; 2168 } 2169 } 2170 2171 out: 2172 free_execattr(eap); 2173 2174 return (err); 2175 } 2176 2177 /* 2178 * Return an error message structure containing the error message 2179 * with context, and the error so the caller can make a decision 2180 * on what to do next. 2181 * 2182 * Because get_ids uses the mc_error_create() function which can 2183 * reallocate the merr, this function must return the merr pointer 2184 * in case it was reallocated. 2185 */ 2186 static mc_error_t * 2187 get_ids(scf_propertygroup_t *methpg, scf_propertygroup_t *instpg, 2188 scf_property_t *prop, scf_value_t *val, struct method_context *ci, 2189 mc_error_t *merr) 2190 { 2191 char *vbuf = ci->vbuf; 2192 ssize_t vbuf_sz = ci->vbuf_sz; 2193 int r; 2194 2195 /* 2196 * This should never happen because the caller should fall through 2197 * another path of just setting the ids to defaults, instead of 2198 * attempting to get the ids here. 2199 */ 2200 if (methpg == NULL && instpg == NULL) 2201 return (mc_error_create(merr, ENOENT, 2202 "No property groups to get ids from.")); 2203 2204 if (!(get_astring_val(methpg, SCF_PROPERTY_USER, 2205 vbuf, vbuf_sz, prop, val) == 0 || get_astring_val(instpg, 2206 SCF_PROPERTY_USER, vbuf, vbuf_sz, prop, 2207 val) == 0)) 2208 return (mc_error_create(merr, ENOENT, 2209 "Could not get \"%s\" property.", SCF_PROPERTY_USER)); 2210 2211 if ((r = get_uid(vbuf, ci, &ci->uid)) != 0) { 2212 ci->uid = (uid_t)-1; 2213 return (mc_error_create(merr, r, 2214 "Could not interpret \"%s\" property value \"%s\", " 2215 "error %d.", SCF_PROPERTY_USER, vbuf, r)); 2216 } 2217 2218 if (!(get_astring_val(methpg, SCF_PROPERTY_GROUP, vbuf, vbuf_sz, prop, 2219 val) == 0 || get_astring_val(instpg, SCF_PROPERTY_GROUP, vbuf, 2220 vbuf_sz, prop, val) == 0)) { 2221 if (scf_error() == SCF_ERROR_NOT_FOUND) { 2222 (void) strcpy(vbuf, ":default"); 2223 } else { 2224 return (mc_error_create(merr, ENOENT, 2225 "Could not get \"%s\" property.", 2226 SCF_PROPERTY_GROUP)); 2227 } 2228 } 2229 2230 if (strcmp(vbuf, ":default") != 0) { 2231 ci->gid = get_gid(vbuf); 2232 if (ci->gid == (gid_t)-1) { 2233 return (mc_error_create(merr, ENOENT, 2234 "Could not interpret \"%s\" property value \"%s\".", 2235 SCF_PROPERTY_GROUP, vbuf)); 2236 } 2237 } else { 2238 switch (r = lookup_pwd(ci)) { 2239 case 0: 2240 ci->gid = ci->pwd.pw_gid; 2241 break; 2242 2243 case ENOENT: 2244 ci->gid = (gid_t)-1; 2245 return (mc_error_create(merr, ENOENT, 2246 "No passwd entry for uid \"%d\".", ci->uid)); 2247 2248 case ENOMEM: 2249 return (mc_error_create(merr, ENOMEM, 2250 "Out of memory.")); 2251 2252 case EIO: 2253 case EMFILE: 2254 case ENFILE: 2255 return (mc_error_create(merr, ENFILE, 2256 "getpwuid_r() failed, error %d.", r)); 2257 2258 default: 2259 bad_fail("lookup_pwd", r); 2260 } 2261 } 2262 2263 if (!(get_astring_val(methpg, SCF_PROPERTY_SUPP_GROUPS, vbuf, vbuf_sz, 2264 prop, val) == 0 || get_astring_val(instpg, 2265 SCF_PROPERTY_SUPP_GROUPS, vbuf, vbuf_sz, prop, val) == 0)) { 2266 if (scf_error() == SCF_ERROR_NOT_FOUND) { 2267 (void) strcpy(vbuf, ":default"); 2268 } else { 2269 return (mc_error_create(merr, ENOENT, 2270 "Could not get supplemental groups (\"%s\") " 2271 "property.", SCF_PROPERTY_SUPP_GROUPS)); 2272 } 2273 } 2274 2275 if (strcmp(vbuf, ":default") != 0) { 2276 switch (r = get_groups(vbuf, ci)) { 2277 case 0: 2278 break; 2279 2280 case EINVAL: 2281 return (mc_error_create(merr, EINVAL, 2282 "Could not interpret supplemental groups (\"%s\") " 2283 "property value \"%s\".", SCF_PROPERTY_SUPP_GROUPS, 2284 vbuf)); 2285 2286 case E2BIG: 2287 return (mc_error_create(merr, E2BIG, 2288 "Too many supplemental groups values in \"%s\".", 2289 vbuf)); 2290 2291 default: 2292 bad_fail("get_groups", r); 2293 } 2294 } else { 2295 ci->ngroups = -1; 2296 } 2297 2298 if (!(get_astring_val(methpg, SCF_PROPERTY_PRIVILEGES, vbuf, vbuf_sz, 2299 prop, val) == 0 || get_astring_val(instpg, SCF_PROPERTY_PRIVILEGES, 2300 vbuf, vbuf_sz, prop, val) == 0)) { 2301 if (scf_error() == SCF_ERROR_NOT_FOUND) { 2302 (void) strcpy(vbuf, ":default"); 2303 } else { 2304 return (mc_error_create(merr, ENOENT, 2305 "Could not get \"%s\" property.", 2306 SCF_PROPERTY_PRIVILEGES)); 2307 } 2308 } 2309 2310 /* 2311 * For default privs, we need to keep priv_set == NULL, as 2312 * we use this test elsewhere. 2313 */ 2314 if (strcmp(vbuf, ":default") != 0) { 2315 ci->priv_set = priv_str_to_set(vbuf, ",", NULL); 2316 if (ci->priv_set == NULL) { 2317 if (errno != EINVAL) { 2318 return (mc_error_create(merr, ENOMEM, 2319 ALLOCFAIL)); 2320 } else { 2321 return (mc_error_create(merr, EINVAL, 2322 "Could not interpret \"%s\" " 2323 "property value \"%s\".", 2324 SCF_PROPERTY_PRIVILEGES, vbuf)); 2325 } 2326 } 2327 } 2328 2329 if (!(get_astring_val(methpg, SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf, 2330 vbuf_sz, prop, val) == 0 || get_astring_val(instpg, 2331 SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf, vbuf_sz, prop, val) == 0)) { 2332 if (scf_error() == SCF_ERROR_NOT_FOUND) { 2333 (void) strcpy(vbuf, ":default"); 2334 } else { 2335 return (mc_error_create(merr, ENOENT, 2336 "Could not get \"%s\" property.", 2337 SCF_PROPERTY_LIMIT_PRIVILEGES)); 2338 } 2339 } 2340 2341 if (strcmp(vbuf, ":default") == 0) 2342 /* 2343 * L must default to all privileges so root NPA services see 2344 * iE = all. "zone" is all privileges available in the current 2345 * zone, equivalent to "all" in the global zone. 2346 */ 2347 (void) strcpy(vbuf, "zone"); 2348 2349 ci->lpriv_set = priv_str_to_set(vbuf, ",", NULL); 2350 if (ci->lpriv_set == NULL) { 2351 if (errno != EINVAL) { 2352 return (mc_error_create(merr, ENOMEM, ALLOCFAIL)); 2353 } else { 2354 return (mc_error_create(merr, EINVAL, 2355 "Could not interpret \"%s\" property value \"%s\".", 2356 SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf)); 2357 } 2358 } 2359 2360 return (merr); 2361 } 2362 2363 static int 2364 get_environment(scf_handle_t *h, scf_propertygroup_t *pg, 2365 struct method_context *mcp, scf_property_t *prop, scf_value_t *val) 2366 { 2367 scf_iter_t *iter; 2368 scf_type_t type; 2369 size_t i = 0; 2370 int ret; 2371 2372 if (scf_pg_get_property(pg, SCF_PROPERTY_ENVIRONMENT, prop) != 0) { 2373 if (scf_error() == SCF_ERROR_NOT_FOUND) 2374 return (ENOENT); 2375 return (scf_error()); 2376 } 2377 if (scf_property_type(prop, &type) != 0) 2378 return (scf_error()); 2379 if (type != SCF_TYPE_ASTRING) 2380 return (EINVAL); 2381 if ((iter = scf_iter_create(h)) == NULL) 2382 return (scf_error()); 2383 2384 if (scf_iter_property_values(iter, prop) != 0) { 2385 ret = scf_error(); 2386 scf_iter_destroy(iter); 2387 return (ret); 2388 } 2389 2390 mcp->env_sz = 10; 2391 2392 if ((mcp->env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz)) == NULL) { 2393 ret = ENOMEM; 2394 goto out; 2395 } 2396 2397 while ((ret = scf_iter_next_value(iter, val)) == 1) { 2398 ret = scf_value_get_as_string(val, mcp->vbuf, mcp->vbuf_sz); 2399 if (ret == -1) { 2400 ret = scf_error(); 2401 goto out; 2402 } 2403 2404 if ((mcp->env[i] = strdup(mcp->vbuf)) == NULL) { 2405 ret = ENOMEM; 2406 goto out; 2407 } 2408 2409 if (++i == mcp->env_sz) { 2410 char **env; 2411 mcp->env_sz *= 2; 2412 env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz); 2413 if (env == NULL) { 2414 ret = ENOMEM; 2415 goto out; 2416 } 2417 (void) memcpy(env, mcp->env, 2418 sizeof (*mcp->env) * (mcp->env_sz / 2)); 2419 free(mcp->env); 2420 mcp->env = env; 2421 } 2422 } 2423 2424 if (ret == -1) 2425 ret = scf_error(); 2426 2427 out: 2428 scf_iter_destroy(iter); 2429 return (ret); 2430 } 2431 2432 /* 2433 * Fetch method context information from the repository, allocate and fill 2434 * a method_context structure, return it in *mcpp, and return NULL. 2435 * 2436 * If no method_context is defined, original init context is provided, where 2437 * the working directory is '/', and uid/gid are 0/0. But if a method_context 2438 * is defined at any level the smf_method(5) method_context defaults are used. 2439 * 2440 * Return an error message structure containing the error message 2441 * with context, and the error so the caller can make a decision 2442 * on what to do next. 2443 * 2444 * Error Types : 2445 * E2BIG Too many values or entry is too big 2446 * EINVAL Invalid value 2447 * EIO an I/O error has occured 2448 * ENOENT no entry for value 2449 * ENOMEM out of memory 2450 * ENOTSUP Version mismatch 2451 * ERANGE value is out of range 2452 * EMFILE/ENFILE out of file descriptors 2453 * 2454 * SCF_ERROR_BACKEND_ACCESS 2455 * SCF_ERROR_CONNECTION_BROKEN 2456 * SCF_ERROR_DELETED 2457 * SCF_ERROR_CONSTRAINT_VIOLATED 2458 * SCF_ERROR_HANDLE_DESTROYED 2459 * SCF_ERROR_INTERNAL 2460 * SCF_ERROR_INVALID_ARGUMENT 2461 * SCF_ERROR_NO_MEMORY 2462 * SCF_ERROR_NO_RESOURCES 2463 * SCF_ERROR_NOT_BOUND 2464 * SCF_ERROR_NOT_FOUND 2465 * SCF_ERROR_NOT_SET 2466 * SCF_ERROR_TYPE_MISMATCH 2467 * 2468 */ 2469 mc_error_t * 2470 restarter_get_method_context(uint_t version, scf_instance_t *inst, 2471 scf_snapshot_t *snap, const char *mname, const char *cmdline, 2472 struct method_context **mcpp) 2473 { 2474 scf_handle_t *h; 2475 scf_propertygroup_t *methpg = NULL; 2476 scf_propertygroup_t *instpg = NULL; 2477 scf_propertygroup_t *pg = NULL; 2478 scf_property_t *prop = NULL; 2479 scf_value_t *val = NULL; 2480 scf_type_t ty; 2481 uint8_t use_profile; 2482 int ret = 0; 2483 int mc_used = 0; 2484 mc_error_t *err = NULL; 2485 struct method_context *cip; 2486 2487 if ((err = malloc(sizeof (mc_error_t))) == NULL) 2488 return (mc_error_create(NULL, ENOMEM, NULL)); 2489 2490 /* Set the type to zero to track if an error occured. */ 2491 err->type = 0; 2492 2493 if (version != RESTARTER_METHOD_CONTEXT_VERSION) 2494 return (mc_error_create(err, ENOTSUP, 2495 "Invalid client version %d. (Expected %d)", 2496 version, RESTARTER_METHOD_CONTEXT_VERSION)); 2497 2498 /* Get the handle before we allocate anything. */ 2499 h = scf_instance_handle(inst); 2500 if (h == NULL) 2501 return (mc_error_create(err, scf_error(), 2502 scf_strerror(scf_error()))); 2503 2504 cip = malloc(sizeof (*cip)); 2505 if (cip == NULL) 2506 return (mc_error_create(err, ENOMEM, ALLOCFAIL)); 2507 2508 (void) memset(cip, 0, sizeof (*cip)); 2509 cip->uid = (uid_t)-1; 2510 cip->euid = (uid_t)-1; 2511 cip->gid = (gid_t)-1; 2512 cip->egid = (gid_t)-1; 2513 2514 cip->vbuf_sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 2515 assert(cip->vbuf_sz >= 0); 2516 cip->vbuf = malloc(cip->vbuf_sz); 2517 if (cip->vbuf == NULL) { 2518 free(cip); 2519 return (mc_error_create(err, ENOMEM, ALLOCFAIL)); 2520 } 2521 2522 if ((instpg = scf_pg_create(h)) == NULL || 2523 (methpg = scf_pg_create(h)) == NULL || 2524 (prop = scf_property_create(h)) == NULL || 2525 (val = scf_value_create(h)) == NULL) { 2526 err = mc_error_create(err, scf_error(), 2527 "Failed to create repository object: %s\n", 2528 scf_strerror(scf_error())); 2529 goto out; 2530 } 2531 2532 /* 2533 * The method environment, and the credentials/profile data, 2534 * may be found either in the pg for the method (methpg), 2535 * or in the instance/service SCF_PG_METHOD_CONTEXT pg (named 2536 * instpg below). 2537 */ 2538 2539 if (scf_instance_get_pg_composed(inst, snap, mname, methpg) != 2540 SCF_SUCCESS) { 2541 err = mc_error_create(err, scf_error(), "Unable to get the " 2542 "\"%s\" method, %s", mname, scf_strerror(scf_error())); 2543 goto out; 2544 } 2545 2546 if (scf_instance_get_pg_composed(inst, snap, SCF_PG_METHOD_CONTEXT, 2547 instpg) != SCF_SUCCESS) { 2548 if (scf_error() != SCF_ERROR_NOT_FOUND) { 2549 err = mc_error_create(err, scf_error(), 2550 "Unable to retrieve the \"%s\" property group, %s", 2551 SCF_PG_METHOD_CONTEXT, scf_strerror(scf_error())); 2552 goto out; 2553 } 2554 scf_pg_destroy(instpg); 2555 instpg = NULL; 2556 } else { 2557 mc_used++; 2558 } 2559 2560 ret = get_environment(h, methpg, cip, prop, val); 2561 if (ret == ENOENT && instpg != NULL) { 2562 ret = get_environment(h, instpg, cip, prop, val); 2563 } 2564 2565 switch (ret) { 2566 case 0: 2567 mc_used++; 2568 break; 2569 case ENOENT: 2570 break; 2571 case ENOMEM: 2572 err = mc_error_create(err, ret, "Out of memory."); 2573 goto out; 2574 case EINVAL: 2575 err = mc_error_create(err, ret, "Invalid method environment."); 2576 goto out; 2577 default: 2578 err = mc_error_create(err, ret, 2579 "Get method environment failed : %s\n", scf_strerror(ret)); 2580 goto out; 2581 } 2582 2583 pg = methpg; 2584 2585 ret = scf_pg_get_property(pg, SCF_PROPERTY_USE_PROFILE, prop); 2586 if (ret && scf_error() == SCF_ERROR_NOT_FOUND && instpg != NULL) { 2587 pg = NULL; 2588 ret = scf_pg_get_property(instpg, SCF_PROPERTY_USE_PROFILE, 2589 prop); 2590 } 2591 2592 if (ret) { 2593 switch (scf_error()) { 2594 case SCF_ERROR_NOT_FOUND: 2595 /* No profile context: use default credentials */ 2596 cip->uid = 0; 2597 cip->gid = 0; 2598 break; 2599 2600 case SCF_ERROR_CONNECTION_BROKEN: 2601 err = mc_error_create(err, SCF_ERROR_CONNECTION_BROKEN, 2602 RCBROKEN); 2603 goto out; 2604 2605 case SCF_ERROR_DELETED: 2606 err = mc_error_create(err, SCF_ERROR_NOT_FOUND, 2607 "Could not find property group \"%s\"", 2608 pg == NULL ? SCF_PG_METHOD_CONTEXT : mname); 2609 goto out; 2610 2611 case SCF_ERROR_HANDLE_MISMATCH: 2612 case SCF_ERROR_INVALID_ARGUMENT: 2613 case SCF_ERROR_NOT_SET: 2614 default: 2615 bad_fail("scf_pg_get_property", scf_error()); 2616 } 2617 } else { 2618 if (scf_property_type(prop, &ty) != SCF_SUCCESS) { 2619 ret = scf_error(); 2620 switch (ret) { 2621 case SCF_ERROR_CONNECTION_BROKEN: 2622 err = mc_error_create(err, 2623 SCF_ERROR_CONNECTION_BROKEN, RCBROKEN); 2624 break; 2625 2626 case SCF_ERROR_DELETED: 2627 err = mc_error_create(err, 2628 SCF_ERROR_NOT_FOUND, 2629 "Could not find property group \"%s\"", 2630 pg == NULL ? SCF_PG_METHOD_CONTEXT : mname); 2631 break; 2632 2633 case SCF_ERROR_NOT_SET: 2634 default: 2635 bad_fail("scf_property_type", ret); 2636 } 2637 2638 goto out; 2639 } 2640 2641 if (ty != SCF_TYPE_BOOLEAN) { 2642 err = mc_error_create(err, 2643 SCF_ERROR_TYPE_MISMATCH, 2644 "\"%s\" property is not boolean in property group " 2645 "\"%s\".", SCF_PROPERTY_USE_PROFILE, 2646 pg == NULL ? SCF_PG_METHOD_CONTEXT : mname); 2647 goto out; 2648 } 2649 2650 if (scf_property_get_value(prop, val) != SCF_SUCCESS) { 2651 ret = scf_error(); 2652 switch (ret) { 2653 case SCF_ERROR_CONNECTION_BROKEN: 2654 err = mc_error_create(err, 2655 SCF_ERROR_CONNECTION_BROKEN, RCBROKEN); 2656 break; 2657 2658 case SCF_ERROR_CONSTRAINT_VIOLATED: 2659 err = mc_error_create(err, 2660 SCF_ERROR_CONSTRAINT_VIOLATED, 2661 "\"%s\" property has multiple values.", 2662 SCF_PROPERTY_USE_PROFILE); 2663 break; 2664 2665 case SCF_ERROR_NOT_FOUND: 2666 err = mc_error_create(err, 2667 SCF_ERROR_NOT_FOUND, 2668 "\"%s\" property has no values.", 2669 SCF_PROPERTY_USE_PROFILE); 2670 break; 2671 default: 2672 bad_fail("scf_property_get_value", ret); 2673 } 2674 2675 goto out; 2676 } 2677 2678 mc_used++; 2679 ret = scf_value_get_boolean(val, &use_profile); 2680 assert(ret == SCF_SUCCESS); 2681 2682 /* get ids & privileges */ 2683 if (use_profile) 2684 err = get_profile(pg, instpg, prop, val, cmdline, 2685 cip, err); 2686 else 2687 err = get_ids(pg, instpg, prop, val, cip, err); 2688 2689 if (err->type != 0) 2690 goto out; 2691 } 2692 2693 /* get working directory */ 2694 if ((methpg != NULL && scf_pg_get_property(methpg, 2695 SCF_PROPERTY_WORKING_DIRECTORY, prop) == SCF_SUCCESS) || 2696 (instpg != NULL && scf_pg_get_property(instpg, 2697 SCF_PROPERTY_WORKING_DIRECTORY, prop) == SCF_SUCCESS)) { 2698 if (scf_property_get_value(prop, val) != SCF_SUCCESS) { 2699 ret = scf_error(); 2700 switch (ret) { 2701 case SCF_ERROR_CONNECTION_BROKEN: 2702 err = mc_error_create(err, ret, RCBROKEN); 2703 break; 2704 2705 case SCF_ERROR_CONSTRAINT_VIOLATED: 2706 err = mc_error_create(err, ret, 2707 "\"%s\" property has multiple values.", 2708 SCF_PROPERTY_WORKING_DIRECTORY); 2709 break; 2710 2711 case SCF_ERROR_NOT_FOUND: 2712 err = mc_error_create(err, ret, 2713 "\"%s\" property has no values.", 2714 SCF_PROPERTY_WORKING_DIRECTORY); 2715 break; 2716 2717 default: 2718 bad_fail("scf_property_get_value", ret); 2719 } 2720 2721 goto out; 2722 } 2723 2724 mc_used++; 2725 ret = scf_value_get_astring(val, cip->vbuf, cip->vbuf_sz); 2726 assert(ret != -1); 2727 } else { 2728 ret = scf_error(); 2729 switch (ret) { 2730 case SCF_ERROR_NOT_FOUND: 2731 /* okay if missing. */ 2732 (void) strcpy(cip->vbuf, ":default"); 2733 break; 2734 2735 case SCF_ERROR_CONNECTION_BROKEN: 2736 err = mc_error_create(err, ret, RCBROKEN); 2737 goto out; 2738 2739 case SCF_ERROR_DELETED: 2740 err = mc_error_create(err, ret, 2741 "Property group could not be found"); 2742 goto out; 2743 2744 case SCF_ERROR_HANDLE_MISMATCH: 2745 case SCF_ERROR_INVALID_ARGUMENT: 2746 case SCF_ERROR_NOT_SET: 2747 default: 2748 bad_fail("scf_pg_get_property", ret); 2749 } 2750 } 2751 2752 if (strcmp(cip->vbuf, ":default") == 0 || 2753 strcmp(cip->vbuf, ":home") == 0) { 2754 switch (ret = lookup_pwd(cip)) { 2755 case 0: 2756 break; 2757 2758 case ENOMEM: 2759 err = mc_error_create(err, ret, "Out of memory."); 2760 goto out; 2761 2762 case ENOENT: 2763 case EIO: 2764 case EMFILE: 2765 case ENFILE: 2766 err = mc_error_create(err, ret, 2767 "Could not get passwd entry."); 2768 goto out; 2769 2770 default: 2771 bad_fail("lookup_pwd", ret); 2772 } 2773 2774 cip->working_dir = strdup(cip->pwd.pw_dir); 2775 if (cip->working_dir == NULL) { 2776 err = mc_error_create(err, ENOMEM, ALLOCFAIL); 2777 goto out; 2778 } 2779 } else { 2780 cip->working_dir = strdup(cip->vbuf); 2781 if (cip->working_dir == NULL) { 2782 err = mc_error_create(err, ENOMEM, ALLOCFAIL); 2783 goto out; 2784 } 2785 } 2786 2787 /* get (optional) corefile pattern */ 2788 if ((methpg != NULL && scf_pg_get_property(methpg, 2789 SCF_PROPERTY_COREFILE_PATTERN, prop) == SCF_SUCCESS) || 2790 (instpg != NULL && scf_pg_get_property(instpg, 2791 SCF_PROPERTY_COREFILE_PATTERN, prop) == SCF_SUCCESS)) { 2792 if (scf_property_get_value(prop, val) != SCF_SUCCESS) { 2793 ret = scf_error(); 2794 switch (ret) { 2795 case SCF_ERROR_CONNECTION_BROKEN: 2796 err = mc_error_create(err, ret, RCBROKEN); 2797 break; 2798 2799 case SCF_ERROR_CONSTRAINT_VIOLATED: 2800 err = mc_error_create(err, ret, 2801 "\"%s\" property has multiple values.", 2802 SCF_PROPERTY_COREFILE_PATTERN); 2803 break; 2804 2805 case SCF_ERROR_NOT_FOUND: 2806 err = mc_error_create(err, ret, 2807 "\"%s\" property has no values.", 2808 SCF_PROPERTY_COREFILE_PATTERN); 2809 break; 2810 2811 default: 2812 bad_fail("scf_property_get_value", ret); 2813 } 2814 2815 } else { 2816 2817 ret = scf_value_get_astring(val, cip->vbuf, 2818 cip->vbuf_sz); 2819 assert(ret != -1); 2820 2821 cip->corefile_pattern = strdup(cip->vbuf); 2822 if (cip->corefile_pattern == NULL) { 2823 err = mc_error_create(err, ENOMEM, ALLOCFAIL); 2824 goto out; 2825 } 2826 } 2827 2828 mc_used++; 2829 } else { 2830 ret = scf_error(); 2831 switch (ret) { 2832 case SCF_ERROR_NOT_FOUND: 2833 /* okay if missing. */ 2834 break; 2835 2836 case SCF_ERROR_CONNECTION_BROKEN: 2837 err = mc_error_create(err, ret, RCBROKEN); 2838 goto out; 2839 2840 case SCF_ERROR_DELETED: 2841 err = mc_error_create(err, ret, 2842 "Property group could not be found"); 2843 goto out; 2844 2845 case SCF_ERROR_HANDLE_MISMATCH: 2846 case SCF_ERROR_INVALID_ARGUMENT: 2847 case SCF_ERROR_NOT_SET: 2848 default: 2849 bad_fail("scf_pg_get_property", ret); 2850 } 2851 } 2852 2853 if (restarter_rm_libs_loadable()) { 2854 /* get project */ 2855 if ((methpg != NULL && scf_pg_get_property(methpg, 2856 SCF_PROPERTY_PROJECT, prop) == SCF_SUCCESS) || 2857 (instpg != NULL && scf_pg_get_property(instpg, 2858 SCF_PROPERTY_PROJECT, prop) == SCF_SUCCESS)) { 2859 if (scf_property_get_value(prop, val) != SCF_SUCCESS) { 2860 ret = scf_error(); 2861 switch (ret) { 2862 case SCF_ERROR_CONNECTION_BROKEN: 2863 err = mc_error_create(err, ret, 2864 RCBROKEN); 2865 break; 2866 2867 case SCF_ERROR_CONSTRAINT_VIOLATED: 2868 err = mc_error_create(err, ret, 2869 "\"%s\" property has multiple " 2870 "values.", SCF_PROPERTY_PROJECT); 2871 break; 2872 2873 case SCF_ERROR_NOT_FOUND: 2874 err = mc_error_create(err, ret, 2875 "\"%s\" property has no values.", 2876 SCF_PROPERTY_PROJECT); 2877 break; 2878 2879 default: 2880 bad_fail("scf_property_get_value", ret); 2881 } 2882 2883 (void) strcpy(cip->vbuf, ":default"); 2884 } else { 2885 ret = scf_value_get_astring(val, cip->vbuf, 2886 cip->vbuf_sz); 2887 assert(ret != -1); 2888 } 2889 2890 mc_used++; 2891 } else { 2892 (void) strcpy(cip->vbuf, ":default"); 2893 } 2894 2895 switch (ret = get_projid(cip->vbuf, cip)) { 2896 case 0: 2897 break; 2898 2899 case ENOMEM: 2900 err = mc_error_create(err, ret, "Out of memory."); 2901 goto out; 2902 2903 case ENOENT: 2904 err = mc_error_create(err, ret, 2905 "Missing passwd or project entry for \"%s\".", 2906 cip->vbuf); 2907 goto out; 2908 2909 case EIO: 2910 err = mc_error_create(err, ret, "I/O error."); 2911 goto out; 2912 2913 case EMFILE: 2914 case ENFILE: 2915 err = mc_error_create(err, ret, 2916 "Out of file descriptors."); 2917 goto out; 2918 2919 case -1: 2920 err = mc_error_create(err, ret, 2921 "Name service switch is misconfigured."); 2922 goto out; 2923 2924 case ERANGE: 2925 case E2BIG: 2926 err = mc_error_create(err, ret, 2927 "Project ID \"%s\" too big.", cip->vbuf); 2928 goto out; 2929 2930 case EINVAL: 2931 err = mc_error_create(err, ret, 2932 "Project ID \"%s\" is invalid.", cip->vbuf); 2933 goto out; 2934 2935 default: 2936 bad_fail("get_projid", ret); 2937 } 2938 2939 /* get resource pool */ 2940 if ((methpg != NULL && scf_pg_get_property(methpg, 2941 SCF_PROPERTY_RESOURCE_POOL, prop) == SCF_SUCCESS) || 2942 (instpg != NULL && scf_pg_get_property(instpg, 2943 SCF_PROPERTY_RESOURCE_POOL, prop) == SCF_SUCCESS)) { 2944 if (scf_property_get_value(prop, val) != SCF_SUCCESS) { 2945 ret = scf_error(); 2946 switch (ret) { 2947 case SCF_ERROR_CONNECTION_BROKEN: 2948 err = mc_error_create(err, ret, 2949 RCBROKEN); 2950 break; 2951 2952 case SCF_ERROR_CONSTRAINT_VIOLATED: 2953 err = mc_error_create(err, ret, 2954 "\"%s\" property has multiple " 2955 "values.", 2956 SCF_PROPERTY_RESOURCE_POOL); 2957 break; 2958 2959 case SCF_ERROR_NOT_FOUND: 2960 err = mc_error_create(err, ret, 2961 "\"%s\" property has no " 2962 "values.", 2963 SCF_PROPERTY_RESOURCE_POOL); 2964 break; 2965 2966 default: 2967 bad_fail("scf_property_get_value", ret); 2968 } 2969 2970 (void) strcpy(cip->vbuf, ":default"); 2971 } else { 2972 ret = scf_value_get_astring(val, cip->vbuf, 2973 cip->vbuf_sz); 2974 assert(ret != -1); 2975 } 2976 2977 mc_used++; 2978 } else { 2979 ret = scf_error(); 2980 switch (ret) { 2981 case SCF_ERROR_NOT_FOUND: 2982 /* okay if missing. */ 2983 (void) strcpy(cip->vbuf, ":default"); 2984 break; 2985 2986 case SCF_ERROR_CONNECTION_BROKEN: 2987 err = mc_error_create(err, ret, RCBROKEN); 2988 goto out; 2989 2990 case SCF_ERROR_DELETED: 2991 err = mc_error_create(err, ret, 2992 "property group could not be found."); 2993 goto out; 2994 2995 case SCF_ERROR_HANDLE_MISMATCH: 2996 case SCF_ERROR_INVALID_ARGUMENT: 2997 case SCF_ERROR_NOT_SET: 2998 default: 2999 bad_fail("scf_pg_get_property", ret); 3000 } 3001 } 3002 3003 if (strcmp(cip->vbuf, ":default") != 0) { 3004 cip->resource_pool = strdup(cip->vbuf); 3005 if (cip->resource_pool == NULL) { 3006 err = mc_error_create(err, ENOMEM, ALLOCFAIL); 3007 goto out; 3008 } 3009 } 3010 } 3011 3012 /* 3013 * A method_context was not used for any configurable 3014 * elements or attributes, so reset and use the simple 3015 * defaults that provide historic init behavior. 3016 */ 3017 if (mc_used == 0) { 3018 (void) memset(cip, 0, sizeof (*cip)); 3019 cip->uid = 0; 3020 cip->gid = 0; 3021 cip->euid = (uid_t)-1; 3022 cip->egid = (gid_t)-1; 3023 } 3024 3025 *mcpp = cip; 3026 3027 out: 3028 (void) scf_value_destroy(val); 3029 scf_property_destroy(prop); 3030 scf_pg_destroy(instpg); 3031 scf_pg_destroy(methpg); 3032 3033 if (cip->pwbuf != NULL) 3034 free(cip->pwbuf); 3035 free(cip->vbuf); 3036 3037 if (err->type != 0) { 3038 restarter_free_method_context(cip); 3039 } else { 3040 restarter_mc_error_destroy(err); 3041 err = NULL; 3042 } 3043 3044 return (err); 3045 } 3046 3047 /* 3048 * Modify the current process per the given method_context. On success, returns 3049 * 0. Note that the environment is not modified by this function to include the 3050 * environment variables in cip->env. 3051 * 3052 * On failure, sets *fp to NULL or the name of the function which failed, 3053 * and returns one of the following error codes. The words in parentheses are 3054 * the values to which *fp may be set for the error case. 3055 * ENOMEM - malloc() failed 3056 * EIO - an I/O error occurred (getpwuid_r, chdir) 3057 * EMFILE - process is out of file descriptors (getpwuid_r) 3058 * ENFILE - system is out of file handles (getpwuid_r) 3059 * EINVAL - gid or egid is out of range (setregid) 3060 * ngroups is too big (setgroups) 3061 * project's project id is bad (setproject) 3062 * uid or euid is out of range (setreuid) 3063 * poolname is invalid (pool_set_binding) 3064 * EPERM - insufficient privilege (setregid, initgroups, setgroups, setppriv, 3065 * setproject, setreuid, settaskid) 3066 * ENOENT - uid has a passwd entry but no shadow entry 3067 * working_dir does not exist (chdir) 3068 * uid has no passwd entry 3069 * the pool could not be found (pool_set_binding) 3070 * EFAULT - lpriv_set or priv_set has a bad address (setppriv) 3071 * working_dir has a bad address (chdir) 3072 * EACCES - could not access working_dir (chdir) 3073 * in a TASK_FINAL task (setproject, settaskid) 3074 * no resource pool accepting default binding exists (setproject) 3075 * ELOOP - too many symbolic links in working_dir (chdir) 3076 * ENAMETOOLONG - working_dir is too long (chdir) 3077 * ENOLINK - working_dir is on an inaccessible remote machine (chdir) 3078 * ENOTDIR - working_dir is not a directory (chdir) 3079 * ESRCH - uid is not a user of project (setproject) 3080 * project is invalid (setproject) 3081 * the resource pool specified for project is unknown (setproject) 3082 * EBADF - the configuration for the pool is invalid (pool_set_binding) 3083 * -1 - core_set_process_path() failed (core_set_process_path) 3084 * a resource control assignment failed (setproject) 3085 * a system error occurred during pool_set_binding (pool_set_binding) 3086 */ 3087 int 3088 restarter_set_method_context(struct method_context *cip, const char **fp) 3089 { 3090 pid_t mypid = -1; 3091 int r, ret; 3092 3093 cip->pwbuf = NULL; 3094 *fp = NULL; 3095 3096 if (cip->gid != (gid_t)-1) { 3097 if (setregid(cip->gid, 3098 cip->egid != (gid_t)-1 ? cip->egid : cip->gid) != 0) { 3099 *fp = "setregid"; 3100 3101 ret = errno; 3102 assert(ret == EINVAL || ret == EPERM); 3103 goto out; 3104 } 3105 } else { 3106 if (cip->pwbuf == NULL) { 3107 switch (ret = lookup_pwd(cip)) { 3108 case 0: 3109 break; 3110 3111 case ENOMEM: 3112 case ENOENT: 3113 *fp = NULL; 3114 goto out; 3115 3116 case EIO: 3117 case EMFILE: 3118 case ENFILE: 3119 *fp = "getpwuid_r"; 3120 goto out; 3121 3122 default: 3123 bad_fail("lookup_pwd", ret); 3124 } 3125 } 3126 3127 if (setregid(cip->pwd.pw_gid, 3128 cip->egid != (gid_t)-1 ? 3129 cip->egid : cip->pwd.pw_gid) != 0) { 3130 *fp = "setregid"; 3131 3132 ret = errno; 3133 assert(ret == EINVAL || ret == EPERM); 3134 goto out; 3135 } 3136 } 3137 3138 if (cip->ngroups == -1) { 3139 if (cip->pwbuf == NULL) { 3140 switch (ret = lookup_pwd(cip)) { 3141 case 0: 3142 break; 3143 3144 case ENOMEM: 3145 case ENOENT: 3146 *fp = NULL; 3147 goto out; 3148 3149 case EIO: 3150 case EMFILE: 3151 case ENFILE: 3152 *fp = "getpwuid_r"; 3153 goto out; 3154 3155 default: 3156 bad_fail("lookup_pwd", ret); 3157 } 3158 } 3159 3160 /* Ok if cip->gid == -1 */ 3161 if (initgroups(cip->pwd.pw_name, cip->gid) != 0) { 3162 *fp = "initgroups"; 3163 ret = errno; 3164 assert(ret == EPERM); 3165 goto out; 3166 } 3167 } else if (cip->ngroups > 0 && 3168 setgroups(cip->ngroups, cip->groups) != 0) { 3169 *fp = "setgroups"; 3170 3171 ret = errno; 3172 assert(ret == EINVAL || ret == EPERM); 3173 goto out; 3174 } 3175 3176 if (cip->corefile_pattern != NULL) { 3177 mypid = getpid(); 3178 3179 if (core_set_process_path(cip->corefile_pattern, 3180 strlen(cip->corefile_pattern) + 1, mypid) != 0) { 3181 *fp = "core_set_process_path"; 3182 ret = -1; 3183 goto out; 3184 } 3185 } 3186 3187 if (restarter_rm_libs_loadable()) { 3188 if (cip->project == NULL) { 3189 if (settaskid(getprojid(), TASK_NORMAL) == -1) { 3190 switch (errno) { 3191 case EACCES: 3192 case EPERM: 3193 *fp = "settaskid"; 3194 ret = errno; 3195 goto out; 3196 3197 case EINVAL: 3198 default: 3199 bad_fail("settaskid", errno); 3200 } 3201 } 3202 } else { 3203 switch (ret = lookup_pwd(cip)) { 3204 case 0: 3205 break; 3206 3207 case ENOMEM: 3208 case ENOENT: 3209 *fp = NULL; 3210 goto out; 3211 3212 case EIO: 3213 case EMFILE: 3214 case ENFILE: 3215 *fp = "getpwuid_r"; 3216 goto out; 3217 3218 default: 3219 bad_fail("lookup_pwd", ret); 3220 } 3221 3222 *fp = "setproject"; 3223 3224 switch (setproject(cip->project, cip->pwd.pw_name, 3225 TASK_NORMAL)) { 3226 case 0: 3227 break; 3228 3229 case SETPROJ_ERR_TASK: 3230 case SETPROJ_ERR_POOL: 3231 ret = errno; 3232 goto out; 3233 3234 default: 3235 ret = -1; 3236 goto out; 3237 } 3238 } 3239 3240 if (cip->resource_pool != NULL) { 3241 if (mypid == -1) 3242 mypid = getpid(); 3243 3244 *fp = "pool_set_binding"; 3245 3246 if (pool_set_binding(cip->resource_pool, P_PID, 3247 mypid) != PO_SUCCESS) { 3248 switch (pool_error()) { 3249 case POE_INVALID_SEARCH: 3250 ret = ENOENT; 3251 break; 3252 3253 case POE_BADPARAM: 3254 ret = EINVAL; 3255 break; 3256 3257 case POE_INVALID_CONF: 3258 ret = EBADF; 3259 break; 3260 3261 case POE_SYSTEM: 3262 ret = -1; 3263 break; 3264 3265 default: 3266 bad_fail("pool_set_binding", 3267 pool_error()); 3268 } 3269 3270 goto out; 3271 } 3272 } 3273 } 3274 3275 /* 3276 * Now, we have to assume our ID. If the UID is 0, we want it to be 3277 * privilege-aware, otherwise the limit set gets used instead of E/P. 3278 * We can do this by setting P as well, which keeps 3279 * PA status (see priv_can_clear_PA()). 3280 */ 3281 3282 *fp = "setppriv"; 3283 3284 if (cip->lpriv_set != NULL) { 3285 if (setppriv(PRIV_SET, PRIV_LIMIT, cip->lpriv_set) != 0) { 3286 ret = errno; 3287 assert(ret == EFAULT || ret == EPERM); 3288 goto out; 3289 } 3290 } 3291 if (cip->priv_set != NULL) { 3292 if (setppriv(PRIV_SET, PRIV_INHERITABLE, cip->priv_set) != 0) { 3293 ret = errno; 3294 assert(ret == EFAULT || ret == EPERM); 3295 goto out; 3296 } 3297 } 3298 3299 /* 3300 * If the limit privset is already set, then must be privilege 3301 * aware. Otherwise, don't assume anything, and force privilege 3302 * aware status. 3303 */ 3304 3305 if (cip->lpriv_set == NULL && cip->priv_set != NULL) { 3306 ret = setpflags(PRIV_AWARE, 1); 3307 assert(ret == 0); 3308 } 3309 3310 *fp = "setreuid"; 3311 if (setreuid(cip->uid, 3312 cip->euid != (uid_t)-1 ? cip->euid : cip->uid) != 0) { 3313 ret = errno; 3314 assert(ret == EINVAL || ret == EPERM); 3315 goto out; 3316 } 3317 3318 *fp = "setppriv"; 3319 if (cip->priv_set != NULL) { 3320 if (setppriv(PRIV_SET, PRIV_PERMITTED, cip->priv_set) != 0) { 3321 ret = errno; 3322 assert(ret == EFAULT || ret == EPERM); 3323 goto out; 3324 } 3325 } 3326 3327 /* 3328 * The last thing to do is chdir to the specified working directory. 3329 * This should come after the uid switching as only the user might 3330 * have access to the specified directory. 3331 */ 3332 if (cip->working_dir != NULL) { 3333 do { 3334 r = chdir(cip->working_dir); 3335 } while (r != 0 && errno == EINTR); 3336 if (r != 0) { 3337 *fp = "chdir"; 3338 ret = errno; 3339 goto out; 3340 } 3341 } 3342 3343 ret = 0; 3344 out: 3345 free(cip->pwbuf); 3346 cip->pwbuf = NULL; 3347 return (ret); 3348 } 3349 3350 void 3351 restarter_free_method_context(struct method_context *mcp) 3352 { 3353 size_t i; 3354 3355 if (mcp->lpriv_set != NULL) 3356 priv_freeset(mcp->lpriv_set); 3357 if (mcp->priv_set != NULL) 3358 priv_freeset(mcp->priv_set); 3359 3360 if (mcp->env != NULL) { 3361 for (i = 0; i < mcp->env_sz; i++) 3362 free(mcp->env[i]); 3363 free(mcp->env); 3364 } 3365 3366 free(mcp->working_dir); 3367 free(mcp->corefile_pattern); 3368 free(mcp->project); 3369 free(mcp->resource_pool); 3370 free(mcp); 3371 } 3372 3373 /* 3374 * Method keyword functions 3375 */ 3376 3377 int 3378 restarter_is_null_method(const char *meth) 3379 { 3380 return (strcmp(meth, MKW_TRUE) == 0); 3381 } 3382 3383 static int 3384 is_kill_method(const char *method, const char *kill_str, 3385 size_t kill_str_len) 3386 { 3387 const char *cp; 3388 int sig; 3389 3390 if (strncmp(method, kill_str, kill_str_len) != 0 || 3391 (method[kill_str_len] != '\0' && 3392 !isspace(method[kill_str_len]))) 3393 return (-1); 3394 3395 cp = method + kill_str_len; 3396 while (*cp != '\0' && isspace(*cp)) 3397 ++cp; 3398 3399 if (*cp == '\0') 3400 return (SIGTERM); 3401 3402 if (*cp != '-') 3403 return (-1); 3404 3405 return (str2sig(cp + 1, &sig) == 0 ? sig : -1); 3406 } 3407 3408 int 3409 restarter_is_kill_proc_method(const char *method) 3410 { 3411 return (is_kill_method(method, MKW_KILL_PROC, 3412 sizeof (MKW_KILL_PROC) - 1)); 3413 } 3414 3415 int 3416 restarter_is_kill_method(const char *method) 3417 { 3418 return (is_kill_method(method, MKW_KILL, sizeof (MKW_KILL) - 1)); 3419 } 3420 3421 /* 3422 * Stubs for now. 3423 */ 3424 3425 /* ARGSUSED */ 3426 int 3427 restarter_event_get_enabled(restarter_event_t *e) 3428 { 3429 return (-1); 3430 } 3431 3432 /* ARGSUSED */ 3433 uint64_t 3434 restarter_event_get_seq(restarter_event_t *e) 3435 { 3436 return (-1); 3437 } 3438 3439 /* ARGSUSED */ 3440 void 3441 restarter_event_get_time(restarter_event_t *e, hrtime_t *time) 3442 { 3443 } 3444 3445 /* 3446 * Check for and validate fmri specified in restarter_actions/auxiliary_fmri 3447 * 0 - Success 3448 * 1 - Failure 3449 */ 3450 int 3451 restarter_inst_validate_ractions_aux_fmri(scf_instance_t *inst) 3452 { 3453 scf_handle_t *h; 3454 scf_propertygroup_t *pg; 3455 scf_property_t *prop; 3456 scf_value_t *val; 3457 char *aux_fmri; 3458 size_t size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 3459 int ret = 1; 3460 3461 if ((aux_fmri = malloc(size)) == NULL) 3462 return (1); 3463 3464 h = scf_instance_handle(inst); 3465 3466 pg = scf_pg_create(h); 3467 prop = scf_property_create(h); 3468 val = scf_value_create(h); 3469 if (pg == NULL || prop == NULL || val == NULL) 3470 goto out; 3471 3472 if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS, 3473 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS, 3474 pg) != SCF_SUCCESS) 3475 goto out; 3476 3477 if (get_astring_val(pg, SCF_PROPERTY_AUX_FMRI, aux_fmri, size, 3478 prop, val) != SCF_SUCCESS) 3479 goto out; 3480 3481 if (scf_parse_fmri(aux_fmri, NULL, NULL, NULL, NULL, NULL, 3482 NULL) != SCF_SUCCESS) 3483 goto out; 3484 3485 ret = 0; 3486 3487 out: 3488 free(aux_fmri); 3489 scf_value_destroy(val); 3490 scf_property_destroy(prop); 3491 scf_pg_destroy(pg); 3492 return (ret); 3493 } 3494 3495 /* 3496 * Get instance's boolean value in restarter_actions/auxiliary_tty 3497 * Return -1 on failure 3498 */ 3499 int 3500 restarter_inst_ractions_from_tty(scf_instance_t *inst) 3501 { 3502 scf_handle_t *h; 3503 scf_propertygroup_t *pg; 3504 scf_property_t *prop; 3505 scf_value_t *val; 3506 uint8_t has_tty; 3507 int ret = -1; 3508 3509 h = scf_instance_handle(inst); 3510 pg = scf_pg_create(h); 3511 prop = scf_property_create(h); 3512 val = scf_value_create(h); 3513 if (pg == NULL || prop == NULL || val == NULL) 3514 goto out; 3515 3516 if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS, 3517 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS, 3518 pg) != SCF_SUCCESS) 3519 goto out; 3520 3521 if (get_boolean_val(pg, SCF_PROPERTY_AUX_TTY, &has_tty, prop, 3522 val) != SCF_SUCCESS) 3523 goto out; 3524 3525 ret = has_tty; 3526 3527 out: 3528 scf_value_destroy(val); 3529 scf_property_destroy(prop); 3530 scf_pg_destroy(pg); 3531 return (ret); 3532 } 3533 3534 static int 3535 restarter_inst_set_astring_prop(scf_instance_t *inst, const char *pgname, 3536 const char *pgtype, uint32_t pgflags, const char *pname, const char *str) 3537 { 3538 scf_handle_t *h; 3539 scf_propertygroup_t *pg; 3540 scf_transaction_t *t; 3541 scf_transaction_entry_t *e; 3542 scf_value_t *v; 3543 int ret = 1, r; 3544 3545 h = scf_instance_handle(inst); 3546 3547 pg = scf_pg_create(h); 3548 t = scf_transaction_create(h); 3549 e = scf_entry_create(h); 3550 v = scf_value_create(h); 3551 if (pg == NULL || t == NULL || e == NULL || v == NULL) 3552 goto out; 3553 3554 if (instance_get_or_add_pg(inst, pgname, pgtype, pgflags, pg)) 3555 goto out; 3556 3557 if (scf_value_set_astring(v, str) != SCF_SUCCESS) 3558 goto out; 3559 3560 for (;;) { 3561 if (scf_transaction_start(t, pg) != 0) 3562 goto out; 3563 3564 if (tx_set_value(t, e, pname, SCF_TYPE_ASTRING, v) != 0) 3565 goto out; 3566 3567 if ((r = scf_transaction_commit(t)) == 1) 3568 break; 3569 3570 if (r == -1) 3571 goto out; 3572 3573 scf_transaction_reset(t); 3574 if (scf_pg_update(pg) == -1) 3575 goto out; 3576 } 3577 ret = 0; 3578 3579 out: 3580 scf_transaction_destroy(t); 3581 scf_entry_destroy(e); 3582 scf_value_destroy(v); 3583 scf_pg_destroy(pg); 3584 3585 return (ret); 3586 } 3587 3588 int 3589 restarter_inst_set_aux_fmri(scf_instance_t *inst) 3590 { 3591 scf_handle_t *h; 3592 scf_propertygroup_t *pg; 3593 scf_property_t *prop; 3594 scf_value_t *val; 3595 char *aux_fmri; 3596 size_t size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 3597 int ret = 1; 3598 3599 if ((aux_fmri = malloc(size)) == NULL) 3600 return (1); 3601 3602 h = scf_instance_handle(inst); 3603 3604 pg = scf_pg_create(h); 3605 prop = scf_property_create(h); 3606 val = scf_value_create(h); 3607 if (pg == NULL || prop == NULL || val == NULL) 3608 goto out; 3609 3610 /* 3611 * Get auxiliary_fmri value from restarter_actions pg 3612 */ 3613 if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS, 3614 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS, 3615 pg) != SCF_SUCCESS) 3616 goto out; 3617 3618 if (get_astring_val(pg, SCF_PROPERTY_AUX_FMRI, aux_fmri, size, 3619 prop, val) != SCF_SUCCESS) 3620 goto out; 3621 3622 /* 3623 * Populate restarter/auxiliary_fmri with the obtained fmri. 3624 */ 3625 ret = restarter_inst_set_astring_prop(inst, SCF_PG_RESTARTER, 3626 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, 3627 SCF_PROPERTY_AUX_FMRI, aux_fmri); 3628 3629 out: 3630 free(aux_fmri); 3631 scf_value_destroy(val); 3632 scf_property_destroy(prop); 3633 scf_pg_destroy(pg); 3634 return (ret); 3635 } 3636 3637 int 3638 restarter_inst_reset_aux_fmri(scf_instance_t *inst) 3639 { 3640 return (scf_instance_delete_prop(inst, 3641 SCF_PG_RESTARTER, SCF_PROPERTY_AUX_FMRI)); 3642 } 3643 3644 int 3645 restarter_inst_reset_ractions_aux_fmri(scf_instance_t *inst) 3646 { 3647 return (scf_instance_delete_prop(inst, 3648 SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_FMRI)); 3649 } 3650