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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * This file contains routines to manipulate lists of repository values that 31 * are used to store process ids and the internal state. There are routines 32 * to read/write the lists from/to the repository and routines to modify or 33 * inspect the lists. It also contains routines that deal with the 34 * repository side of contract ids. 35 */ 36 37 #include <errno.h> 38 #include <stdlib.h> 39 #include <libintl.h> 40 #include <unistd.h> 41 #include <string.h> 42 #include <signal.h> 43 #include <sys/param.h> 44 #include <sys/types.h> 45 #include <sys/stat.h> 46 #include <libscf_priv.h> 47 #include "inetd_impl.h" 48 49 /* 50 * Number of consecutive repository bind retries performed by bind_to_rep() 51 * before failing. 52 */ 53 #define BIND_TO_REP_RETRIES 10 54 55 /* Name of property group where inetd's state for a service is stored. */ 56 #define PG_NAME_INSTANCE_STATE (const char *) "inetd_state" 57 58 /* uu_list repval list pool */ 59 static uu_list_pool_t *rep_val_pool = NULL; 60 61 /* 62 * Repository object pointers that get set-up in repval_init() and closed down 63 * in repval_fini(). They're used in _retrieve_rep_vals(), _store_rep_vals(), 64 * add_remove_contract_norebind(), and adopt_repository_contracts(). They're 65 * global so they can be initialized once on inetd startup, and re-used 66 * there-after in the referenced functions. 67 */ 68 static scf_handle_t *rep_handle = NULL; 69 static scf_propertygroup_t *pg = NULL; 70 static scf_instance_t *inst = NULL; 71 static scf_transaction_t *trans = NULL; 72 static scf_transaction_entry_t *entry = NULL; 73 static scf_property_t *prop = NULL; 74 75 /* 76 * Pathname storage for paths generated from the fmri. 77 * Used when updating the ctid and (start) pid files for an inetd service. 78 */ 79 static char genfmri_filename[MAXPATHLEN] = ""; 80 static char genfmri_temp_filename[MAXPATHLEN] = ""; 81 82 /* 83 * Try and make the given handle bind be bound to the repository. If 84 * it's already bound, or we succeed a new bind return 0; else return 85 * -1 on failure, with the SCF error set to one of the following: 86 * SCF_ERROR_NO_SERVER 87 * SCF_ERROR_NO_RESOURCES 88 */ 89 int 90 make_handle_bound(scf_handle_t *hdl) 91 { 92 uint_t retries; 93 94 for (retries = 0; retries <= BIND_TO_REP_RETRIES; retries++) { 95 if ((scf_handle_bind(hdl) == 0) || 96 (scf_error() == SCF_ERROR_IN_USE)) 97 return (0); 98 99 assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT); 100 } 101 102 return (-1); 103 } 104 105 int 106 repval_init(void) 107 { 108 /* 109 * Create the repval list pool. 110 */ 111 rep_val_pool = uu_list_pool_create("rep_val_pool", sizeof (rep_val_t), 112 offsetof(rep_val_t, link), NULL, UU_LIST_POOL_DEBUG); 113 if (rep_val_pool == NULL) { 114 error_msg("%s: %s", gettext("Failed to create rep_val pool"), 115 uu_strerror(uu_error())); 116 return (-1); 117 } 118 119 /* 120 * Create and bind a repository handle, and create all repository 121 * objects that we'll use later that are associated with it. On any 122 * errors we simply return -1 and let repval_fini() clean-up after 123 * us. 124 */ 125 if ((rep_handle = scf_handle_create(SCF_VERSION)) == NULL) { 126 error_msg("%s: %s", 127 gettext("Failed to create repository handle"), 128 scf_strerror(scf_error())); 129 goto cleanup; 130 } else if (make_handle_bound(rep_handle) == -1) { 131 goto cleanup; 132 } else if (((pg = scf_pg_create(rep_handle)) == NULL) || 133 ((inst = scf_instance_create(rep_handle)) == NULL) || 134 ((trans = scf_transaction_create(rep_handle)) == NULL) || 135 ((entry = scf_entry_create(rep_handle)) == NULL) || 136 ((prop = scf_property_create(rep_handle)) == NULL)) { 137 error_msg("%s: %s", 138 gettext("Failed to create repository object"), 139 scf_strerror(scf_error())); 140 goto cleanup; 141 } 142 143 return (0); 144 cleanup: 145 repval_fini(); 146 return (-1); 147 } 148 149 void 150 repval_fini(void) 151 { 152 if (rep_handle != NULL) { 153 /* 154 * We unbind from the repository before we free the repository 155 * objects for efficiency reasons. 156 */ 157 (void) scf_handle_unbind(rep_handle); 158 159 scf_pg_destroy(pg); 160 pg = NULL; 161 scf_instance_destroy(inst); 162 inst = NULL; 163 scf_transaction_destroy(trans); 164 trans = NULL; 165 scf_entry_destroy(entry); 166 entry = NULL; 167 scf_property_destroy(prop); 168 prop = NULL; 169 170 scf_handle_destroy(rep_handle); 171 rep_handle = NULL; 172 } 173 174 if (rep_val_pool != NULL) { 175 uu_list_pool_destroy(rep_val_pool); 176 rep_val_pool = NULL; 177 } 178 } 179 180 uu_list_t * 181 create_rep_val_list(void) 182 { 183 uu_list_t *ret; 184 185 if ((ret = uu_list_create(rep_val_pool, NULL, 0)) == NULL) 186 assert(uu_error() == UU_ERROR_NO_MEMORY); 187 188 return (ret); 189 } 190 191 void 192 destroy_rep_val_list(uu_list_t *list) 193 { 194 if (list != NULL) { 195 empty_rep_val_list(list); 196 uu_list_destroy(list); 197 } 198 } 199 200 rep_val_t * 201 find_rep_val(uu_list_t *list, int64_t val) 202 { 203 rep_val_t *rv; 204 205 for (rv = uu_list_first(list); rv != NULL; 206 rv = uu_list_next(list, rv)) { 207 if (rv->val == val) 208 break; 209 } 210 return (rv); 211 } 212 213 int 214 add_rep_val(uu_list_t *list, int64_t val) 215 { 216 rep_val_t *rv; 217 218 if ((rv = malloc(sizeof (rep_val_t))) == NULL) 219 return (-1); 220 221 uu_list_node_init(rv, &rv->link, rep_val_pool); 222 rv->val = val; 223 rv->scf_val = NULL; 224 (void) uu_list_insert_after(list, NULL, rv); 225 226 return (0); 227 } 228 229 void 230 remove_rep_val(uu_list_t *list, int64_t val) 231 { 232 rep_val_t *rv; 233 234 if ((rv = find_rep_val(list, val)) != NULL) { 235 uu_list_remove(list, rv); 236 assert(rv->scf_val == NULL); 237 free(rv); 238 } 239 } 240 241 void 242 empty_rep_val_list(uu_list_t *list) 243 { 244 void *cookie = NULL; 245 rep_val_t *rv; 246 247 while ((rv = uu_list_teardown(list, &cookie)) != NULL) { 248 if (rv->scf_val != NULL) 249 scf_value_destroy(rv->scf_val); 250 free(rv); 251 } 252 } 253 254 int64_t 255 get_single_rep_val(uu_list_t *list) 256 { 257 rep_val_t *rv = uu_list_first(list); 258 259 assert(rv != NULL); 260 return (rv->val); 261 } 262 263 int 264 set_single_rep_val(uu_list_t *list, int64_t val) 265 { 266 rep_val_t *rv = uu_list_first(list); 267 268 if (rv == NULL) { 269 if (add_rep_val(list, val) == -1) 270 return (-1); 271 } else { 272 rv->val = val; 273 } 274 275 return (0); 276 } 277 278 /* 279 * Partner to add_tr_entry_values. This function frees the scf_values created 280 * in add_tr_entry_values() in the list 'vals'. 281 */ 282 static void 283 remove_tr_entry_values(uu_list_t *vals) 284 { 285 rep_val_t *rval; 286 287 for (rval = uu_list_first(vals); rval != NULL; 288 rval = uu_list_next(vals, rval)) { 289 if (rval->scf_val != NULL) { 290 scf_value_destroy(rval->scf_val); 291 rval->scf_val = NULL; 292 } 293 } 294 } 295 296 /* 297 * This function creates and associates with transaction entry 'entry' an 298 * scf value for each value in 'vals'. The pointers to the scf values 299 * are stored in the list for later cleanup by remove_tr_entry_values. 300 * Returns 0 on success, else -1 on error with scf_error() set to: 301 * SCF_ERROR_NO_MEMORY if memory allocation failed. 302 * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken. 303 */ 304 static int 305 add_tr_entry_values(scf_handle_t *hdl, scf_transaction_entry_t *entry, 306 uu_list_t *vals) 307 { 308 rep_val_t *rval; 309 310 for (rval = uu_list_first(vals); rval != NULL; 311 rval = uu_list_next(vals, rval)) { 312 313 assert(rval->scf_val == NULL); 314 if ((rval->scf_val = scf_value_create(hdl)) == NULL) { 315 remove_tr_entry_values(vals); 316 return (-1); 317 } 318 319 scf_value_set_integer(rval->scf_val, rval->val); 320 321 if (scf_entry_add_value(entry, rval->scf_val) < 0) { 322 remove_tr_entry_values(vals); 323 return (-1); 324 } 325 } 326 327 return (0); 328 } 329 330 /* 331 * Stores the values contained in the list 'vals' into the property 'prop_name' 332 * of the instance with fmri 'inst_fmri', within the instance's instance 333 * state property group. 334 * 335 * Returns 0 on success, else one of the following on failure: 336 * SCF_ERROR_NO_MEMORY if memory allocation failed. 337 * SCF_ERROR_NO_RESOURCES if the server doesn't have required resources. 338 * SCF_ERROR_VERSION_MISMATCH if program compiled against a newer libscf 339 * than on system. 340 * SCF_ERROR_PERMISSION_DENIED if insufficient privileges to modify pg. 341 * SCF_ERROR_BACKEND_ACCESS if the repository back-end refused the pg modify. 342 * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken. 343 */ 344 static scf_error_t 345 _store_rep_vals(uu_list_t *vals, const char *inst_fmri, const char *prop_name) 346 { 347 int cret; 348 int ret; 349 350 if (scf_handle_decode_fmri(rep_handle, inst_fmri, NULL, NULL, inst, 351 NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) 352 return (scf_error()); 353 354 /* 355 * Fetch the instance state pg, and if it doesn't exist try and 356 * create it. 357 */ 358 if (scf_instance_get_pg(inst, PG_NAME_INSTANCE_STATE, pg) < 0) { 359 if (scf_error() != SCF_ERROR_NOT_FOUND) 360 return (scf_error()); 361 if (scf_instance_add_pg(inst, PG_NAME_INSTANCE_STATE, 362 SCF_GROUP_FRAMEWORK, SCF_PG_FLAG_NONPERSISTENT, pg) < 0) 363 return (scf_error()); 364 } 365 366 /* 367 * Perform a transaction to write the values to the requested property. 368 * If someone got there before us, loop and retry. 369 */ 370 do { 371 if (scf_transaction_start(trans, pg) < 0) 372 return (scf_error()); 373 374 if ((scf_transaction_property_new(trans, entry, 375 prop_name, SCF_TYPE_INTEGER) < 0) && 376 (scf_transaction_property_change_type(trans, entry, 377 prop_name, SCF_TYPE_INTEGER) < 0)) { 378 ret = scf_error(); 379 goto cleanup; 380 } 381 382 if (add_tr_entry_values(rep_handle, entry, vals) < 0) { 383 ret = scf_error(); 384 goto cleanup; 385 } 386 387 if ((cret = scf_transaction_commit(trans)) < 0) { 388 ret = scf_error(); 389 goto cleanup; 390 } else if (cret == 0) { 391 scf_transaction_reset(trans); 392 scf_entry_reset(entry); 393 remove_tr_entry_values(vals); 394 if (scf_pg_update(pg) < 0) { 395 ret = scf_error(); 396 goto cleanup; 397 } 398 } 399 } while (cret == 0); 400 401 ret = 0; 402 cleanup: 403 scf_transaction_reset(trans); 404 scf_entry_reset(entry); 405 remove_tr_entry_values(vals); 406 return (ret); 407 } 408 409 /* 410 * Retrieves the repository values of property 'prop_name', of the instance 411 * with fmri 'fmri', from within the instance's instance state property 412 * group and adds them to the value list 'list'. 413 * 414 * Returns 0 on success, else one of the following values on error: 415 * SCF_ERROR_NOT_FOUND if the property doesn't exist. 416 * SCF_ERROR_NO_MEMORY if memory allocation failed. 417 * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken. 418 * SCF_ERROR_TYPE_MISMATCH if the property was of an unexpected type. 419 * 420 */ 421 static scf_error_t 422 _retrieve_rep_vals(uu_list_t *list, const char *fmri, const char *prop_name) 423 { 424 scf_simple_prop_t *sp; 425 int64_t *ip; 426 427 if ((sp = scf_simple_prop_get(rep_handle, fmri, PG_NAME_INSTANCE_STATE, 428 prop_name)) == NULL) 429 return (scf_error()); 430 431 while ((ip = scf_simple_prop_next_integer(sp)) != NULL) { 432 if (add_rep_val(list, *ip) == -1) { 433 empty_rep_val_list(list); 434 scf_simple_prop_free(sp); 435 return (SCF_ERROR_NO_MEMORY); 436 } 437 } 438 if (scf_error() != SCF_ERROR_NONE) { 439 assert(scf_error() == SCF_ERROR_TYPE_MISMATCH); 440 empty_rep_val_list(list); 441 scf_simple_prop_free(sp); 442 return (scf_error()); 443 } 444 445 scf_simple_prop_free(sp); 446 return (0); 447 } 448 449 /* 450 * Writes the repository values in the vals list to 451 * a file that is generated based on the passed in fmri and name. 452 * Returns 0 on success, 453 * ENAMETOOLONG if unable to generate filename from fmri (including 454 * the inability to create the directory for the generated filename) and 455 * ENOENT on all other failures. 456 */ 457 static int 458 repvals_to_file(const char *fmri, const char *name, uu_list_t *vals) 459 { 460 int tfd; 461 FILE *tfp; /* temp fp */ 462 rep_val_t *spval; /* Contains a start_pid or ctid */ 463 int ret = 0; 464 465 if (gen_filenms_from_fmri(fmri, name, genfmri_filename, 466 genfmri_temp_filename) != 0) { 467 /* Failure either from fmri too long or mkdir failure */ 468 return (ENAMETOOLONG); 469 } 470 471 if ((tfd = mkstemp(genfmri_temp_filename)) == -1) { 472 return (ENOENT); 473 } 474 475 if (fchmod(tfd, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) { 476 (void) close(tfd); 477 ret = ENOENT; 478 goto unlink_out; 479 } 480 481 if ((tfp = fdopen(tfd, "w")) == NULL) { 482 (void) close(tfd); 483 ret = ENOENT; 484 goto unlink_out; 485 } 486 487 for (spval = uu_list_first(vals); spval != NULL; 488 spval = uu_list_next(vals, spval)) { 489 if (fprintf(tfp, "%lld\n", spval->val) <= 0) { 490 (void) fclose(tfp); 491 ret = ENOENT; 492 goto unlink_out; 493 } 494 } 495 if (fclose(tfp) != 0) { 496 ret = ENOENT; 497 goto unlink_out; 498 } 499 if (rename(genfmri_temp_filename, genfmri_filename) != 0) { 500 ret = ENOENT; 501 goto unlink_out; 502 } 503 return (0); 504 505 unlink_out: 506 if (unlink(genfmri_temp_filename) != 0) { 507 warn_msg(gettext("Removal of temp file " 508 "%s failed. Please remove manually."), 509 genfmri_temp_filename); 510 } 511 return (ret); 512 } 513 514 /* 515 * A routine that loops trying to read/write values until either success, 516 * an error other than a broken repository connection or 517 * the number of retries reaches REP_OP_RETRIES. 518 * This action is used to read/write the values: 519 * reads/writes to a file for the START_PIDS property due to scalability 520 * problems with libscf 521 * reads/writes to the repository for all other properties. 522 * Returns 0 on success, else the error value from either _store_rep_vals or 523 * _retrieve_rep_vals (based on whether 'store' was set or not), or one of the 524 * following: 525 * SCF_ERROR_NO_RESOURCES if the server doesn't have adequate resources 526 * SCF_ERROR_NO_MEMORY if a memory allocation failure 527 * SCF_ERROR_NO_SERVER if the server isn't running. 528 * SCF_ERROR_CONSTRAINT_VIOLATED if an error in dealing with the speedy files 529 */ 530 static scf_error_t 531 store_retrieve_rep_vals(uu_list_t *vals, const char *fmri, 532 const char *prop, boolean_t store) 533 { 534 scf_error_t ret = 0; 535 uint_t retries; 536 FILE *tfp; /* temp fp */ 537 int64_t tval; /* temp val holder */ 538 int fscanf_ret; 539 int fopen_retry_cnt = 2; 540 541 /* inetd specific action for START_PIDS property */ 542 if (strcmp(prop, PR_NAME_START_PIDS) == 0) { 543 /* 544 * Storage performance of START_PIDS is important, 545 * so each instance has its own file and all start_pids 546 * in the list are written to a temp file and then 547 * moved (renamed). 548 */ 549 if (store) { 550 /* Write all values in list to file */ 551 if (repvals_to_file(fmri, "pid", vals)) { 552 return (SCF_ERROR_CONSTRAINT_VIOLATED); 553 } 554 } else { 555 /* no temp name needed */ 556 if (gen_filenms_from_fmri(fmri, "pid", genfmri_filename, 557 NULL) != 0) 558 return (SCF_ERROR_CONSTRAINT_VIOLATED); 559 560 retry_fopen: 561 /* It's ok if no file, there are just no pids */ 562 if ((tfp = fopen(genfmri_filename, "r")) == NULL) { 563 if ((errno == EINTR) && (fopen_retry_cnt > 0)) { 564 fopen_retry_cnt--; 565 goto retry_fopen; 566 } 567 return (0); 568 } 569 /* fscanf may not set errno, so clear it first */ 570 errno = 0; 571 while ((fscanf_ret = fscanf(tfp, "%lld", &tval)) == 1) { 572 /* If tval isn't a valid pid, then fail. */ 573 if ((tval > MAXPID) || (tval <= 0)) { 574 empty_rep_val_list(vals); 575 return (SCF_ERROR_CONSTRAINT_VIOLATED); 576 } 577 if (add_rep_val(vals, tval) == -1) { 578 empty_rep_val_list(vals); 579 return (SCF_ERROR_NO_MEMORY); 580 } 581 errno = 0; 582 } 583 /* EOF is ok when no errno */ 584 if ((fscanf_ret != EOF) || (errno != 0)) { 585 empty_rep_val_list(vals); 586 return (SCF_ERROR_CONSTRAINT_VIOLATED); 587 } 588 if (fclose(tfp) != 0) { 589 /* for close failure just log a message */ 590 warn_msg(gettext("Close of file %s failed."), 591 genfmri_filename); 592 } 593 } 594 } else { 595 for (retries = 0; retries <= REP_OP_RETRIES; retries++) { 596 if (make_handle_bound(rep_handle) == -1) { 597 ret = scf_error(); 598 break; 599 } 600 601 if ((ret = (store ? _store_rep_vals(vals, fmri, prop) : 602 _retrieve_rep_vals(vals, fmri, prop))) != 603 SCF_ERROR_CONNECTION_BROKEN) 604 break; 605 606 (void) scf_handle_unbind(rep_handle); 607 } 608 } 609 610 out: 611 return (ret); 612 } 613 614 scf_error_t 615 store_rep_vals(uu_list_t *vals, const char *fmri, const char *prop) 616 { 617 return (store_retrieve_rep_vals(vals, fmri, prop, B_TRUE)); 618 } 619 620 scf_error_t 621 retrieve_rep_vals(uu_list_t *vals, const char *fmri, const char *prop) 622 { 623 return (store_retrieve_rep_vals(vals, fmri, prop, B_FALSE)); 624 } 625 626 /* 627 * Adds/removes a contract id to/from the cached list kept in the instance. 628 * Then the cached list is written to a file named "ctid" in a directory 629 * based on the fmri. Cached list is written to a file due to scalability 630 * problems in libscf. The file "ctid" is used when inetd is restarted 631 * so that inetd can adopt the contracts that it had previously. 632 * Returns: 633 * 0 on success 634 * ENAMETOOLONG if unable to generate filename from fmri (including 635 * the inability to create the directory for the generated filename) 636 * ENOENT - failure accessing file 637 * ENOMEM - memory allocation failure 638 */ 639 int 640 add_remove_contract(instance_t *inst, boolean_t add, ctid_t ctid) 641 { 642 FILE *tfp; /* temp fp */ 643 int ret = 0; 644 int repval_ret = 0; 645 int fopen_retry_cnt = 2; 646 647 /* 648 * Storage performance of contract ids is important, 649 * so each instance has its own file. An add of a 650 * ctid will be appended to the ctid file. 651 * The removal of a ctid will result in the remaining 652 * ctids in the list being written to a temp file and then 653 * moved (renamed). 654 */ 655 if (add) { 656 if (gen_filenms_from_fmri(inst->fmri, "ctid", genfmri_filename, 657 NULL) != 0) { 658 /* Failure either from fmri too long or mkdir failure */ 659 return (ENAMETOOLONG); 660 } 661 662 retry_fopen: 663 if ((tfp = fopen(genfmri_filename, "a")) == NULL) { 664 if ((errno == EINTR) && (fopen_retry_cnt > 0)) { 665 fopen_retry_cnt--; 666 goto retry_fopen; 667 } 668 ret = ENOENT; 669 goto out; 670 } 671 672 /* Always store ctids as long long */ 673 if (fprintf(tfp, "%llu\n", (uint64_t)ctid) <= 0) { 674 (void) fclose(tfp); 675 ret = ENOENT; 676 goto out; 677 } 678 679 if (fclose(tfp) != 0) { 680 ret = ENOENT; 681 goto out; 682 } 683 684 if (add_rep_val(inst->start_ctids, ctid) != 0) { 685 ret = ENOMEM; 686 goto out; 687 } 688 } else { 689 remove_rep_val(inst->start_ctids, ctid); 690 691 /* Write all values in list to file */ 692 if ((repval_ret = repvals_to_file(inst->fmri, "ctid", 693 inst->start_ctids)) != 0) { 694 ret = repval_ret; 695 goto out; 696 } 697 } 698 699 out: 700 return (ret); 701 } 702 703 /* 704 * If sig !=0, iterate over all contracts in the cached list of contract 705 * ids kept in the instance. Send each contract the specified signal. 706 * If sig == 0, read in the contract ids that were last associated 707 * with this instance (reload the cache) and call adopt_contract() 708 * to take ownership. 709 * 710 * Returns 0 on success; 711 * ENAMETOOLONG if unable to generate filename from fmri (including 712 * the inability to create the directory for the generated filename) and 713 * ENXIO if a failure accessing the file 714 * ENOMEM if there was a memory allocation failure 715 * ENOENT if the instance, its restarter property group, or its 716 * contract property don't exist 717 * EIO if invalid data read from the file 718 */ 719 int 720 iterate_repository_contracts(instance_t *inst, int sig) 721 { 722 int ret = 0; 723 FILE *fp; 724 rep_val_t *spval = NULL; /* Contains a start_pid */ 725 uint64_t tval; /* temp val holder */ 726 uu_list_t *uup = NULL; 727 int fscanf_ret; 728 int fopen_retry_cnt = 2; 729 730 if (sig != 0) { 731 /* 732 * Send a signal to all in the contract; ESRCH just 733 * means they all exited before we could kill them 734 */ 735 for (spval = uu_list_first(inst->start_ctids); spval != NULL; 736 spval = uu_list_next(inst->start_ctids, spval)) { 737 if (sigsend(P_CTID, (ctid_t)spval->val, sig) == -1 && 738 errno != ESRCH) { 739 warn_msg(gettext("Unable to signal all " 740 "contract members of instance %s: %s"), 741 inst->fmri, strerror(errno)); 742 } 743 } 744 return (0); 745 } 746 747 /* 748 * sig == 0 case. 749 * Attempt to adopt the contract for each ctid. 750 */ 751 if (gen_filenms_from_fmri(inst->fmri, "ctid", genfmri_filename, 752 NULL) != 0) { 753 /* Failure either from fmri too long or mkdir failure */ 754 return (ENAMETOOLONG); 755 } 756 757 retry_fopen: 758 /* It's ok if no file, there are no ctids to adopt */ 759 if ((fp = fopen(genfmri_filename, "r")) == NULL) { 760 if ((errno == EINTR) && (fopen_retry_cnt > 0)) { 761 fopen_retry_cnt--; 762 goto retry_fopen; 763 } 764 return (0); 765 } 766 767 /* 768 * Read ctids from file into 2 lists: 769 * - temporary list to be traversed (uup) 770 * - cached list that can be modified if adoption of 771 * contract fails (inst->start_ctids). 772 * Always treat ctids as long longs. 773 */ 774 uup = create_rep_val_list(); 775 /* fscanf may not set errno, so clear it first */ 776 errno = 0; 777 while ((fscanf_ret = fscanf(fp, "%llu", &tval)) == 1) { 778 /* If tval isn't a valid ctid, then fail. */ 779 if (tval == 0) { 780 (void) fclose(fp); 781 ret = EIO; 782 goto out; 783 } 784 if ((add_rep_val(uup, tval) == -1) || 785 (add_rep_val(inst->start_ctids, tval) == -1)) { 786 (void) fclose(fp); 787 ret = ENOMEM; 788 goto out; 789 } 790 errno = 0; 791 } 792 /* EOF is not a failure when no errno */ 793 if ((fscanf_ret != EOF) || (errno != 0)) { 794 ret = EIO; 795 goto out; 796 } 797 798 if (fclose(fp) != 0) { 799 ret = ENXIO; 800 goto out; 801 } 802 803 for (spval = uu_list_first(uup); spval != NULL; 804 spval = uu_list_next(uup, spval)) { 805 /* Try to adopt the contract */ 806 if (adopt_contract((ctid_t)spval->val, 807 inst->fmri) != 0) { 808 /* 809 * Adoption failed. No reason to think it'll 810 * work later, so remove the id from our list 811 * in the instance. 812 */ 813 remove_rep_val(inst->start_ctids, spval->val); 814 } 815 } 816 out: 817 if (uup) { 818 empty_rep_val_list(uup); 819 destroy_rep_val_list(uup); 820 } 821 822 if (ret != 0) 823 empty_rep_val_list(inst->start_ctids); 824 825 return (ret); 826 } 827