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