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