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