xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.lib/inetd/repval.c (revision ddb365bfc9e868ad24ccdcb0dc91af18b10df082)
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
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
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
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 *
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
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 *
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
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
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
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
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
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
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
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
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
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
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
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 out:
609 	return (ret);
610 }
611 
612 scf_error_t
613 store_rep_vals(uu_list_t *vals, const char *fmri, const char *prop)
614 {
615 	return (store_retrieve_rep_vals(vals, fmri, prop, B_TRUE));
616 }
617 
618 scf_error_t
619 retrieve_rep_vals(uu_list_t *vals, const char *fmri, const char *prop)
620 {
621 	return (store_retrieve_rep_vals(vals, fmri, prop, B_FALSE));
622 }
623 
624 /*
625  * Adds/removes a contract id to/from the cached list kept in the instance.
626  * Then the cached list is written to a file named "ctid" in a directory
627  * based on the fmri.  Cached list is written to a file due to scalability
628  * problems in libscf.  The file "ctid" is used when inetd is restarted
629  * so that inetd can adopt the contracts that it had previously.
630  * Returns:
631  *   0 on success
632  *   ENAMETOOLONG if unable to generate filename from fmri (including
633  *   the inability to create the directory for the generated filename)
634  *   ENOENT - failure accessing file
635  *   ENOMEM - memory allocation failure
636  */
637 int
638 add_remove_contract(instance_t *inst, boolean_t add, ctid_t ctid)
639 {
640 	FILE		*tfp;		/* temp fp */
641 	int		ret = 0;
642 	int		repval_ret = 0;
643 	int		fopen_retry_cnt = 2;
644 
645 	/*
646 	 * Storage performance of contract ids is important,
647 	 * so each instance has its own file.  An add of a
648 	 * ctid will be appended to the ctid file.
649 	 * The removal of a ctid will result in the remaining
650 	 * ctids in the list being written to a temp file and then
651 	 * moved (renamed).
652 	 */
653 	if (add) {
654 		if (gen_filenms_from_fmri(inst->fmri, "ctid", genfmri_filename,
655 		    NULL) != 0) {
656 			/* Failure either from fmri too long or mkdir failure */
657 			return (ENAMETOOLONG);
658 		}
659 
660 retry_fopen:
661 		if ((tfp = fopen(genfmri_filename, "a")) == NULL) {
662 			if ((errno == EINTR) && (fopen_retry_cnt > 0)) {
663 				fopen_retry_cnt--;
664 				goto retry_fopen;
665 			}
666 			ret = ENOENT;
667 			goto out;
668 		}
669 
670 		/* Always store ctids as long long */
671 		if (fprintf(tfp, "%llu\n", (uint64_t)ctid) <= 0) {
672 			(void) fclose(tfp);
673 			ret = ENOENT;
674 			goto out;
675 		}
676 
677 		if (fclose(tfp) != 0) {
678 			ret = ENOENT;
679 			goto out;
680 		}
681 
682 		if (add_rep_val(inst->start_ctids, ctid) != 0) {
683 			ret = ENOMEM;
684 			goto out;
685 		}
686 	} else {
687 		remove_rep_val(inst->start_ctids, ctid);
688 
689 		/* Write all values in list to file */
690 		if ((repval_ret = repvals_to_file(inst->fmri, "ctid",
691 		    inst->start_ctids)) != 0) {
692 			ret = repval_ret;
693 			goto out;
694 		}
695 	}
696 
697 out:
698 	return (ret);
699 }
700 
701 /*
702  * If sig !=0, iterate over all contracts in the cached list of contract
703  * ids kept in the instance.  Send each contract the specified signal.
704  * If sig == 0, read in the contract ids that were last associated
705  * with this instance (reload the cache) and call adopt_contract()
706  * to take ownership.
707  *
708  * Returns 0 on success;
709  * ENAMETOOLONG if unable to generate filename from fmri (including
710  * the inability to create the directory for the generated filename) and
711  * ENXIO if a failure accessing the file
712  * ENOMEM if there was a memory allocation failure
713  * ENOENT if the instance, its restarter property group, or its
714  *   contract property don't exist
715  * EIO if invalid data read from the file
716  */
717 int
718 iterate_repository_contracts(instance_t *inst, int sig)
719 {
720 	int		ret = 0;
721 	FILE		*fp;
722 	rep_val_t	*spval = NULL;	/* Contains a start_pid */
723 	uint64_t	tval;		/* temp val holder */
724 	uu_list_t	*uup = NULL;
725 	int		fscanf_ret;
726 	int		fopen_retry_cnt = 2;
727 
728 	if (sig != 0) {
729 		/*
730 		 * Send a signal to all in the contract; ESRCH just
731 		 * means they all exited before we could kill them
732 		 */
733 		for (spval = uu_list_first(inst->start_ctids); spval != NULL;
734 		    spval = uu_list_next(inst->start_ctids, spval)) {
735 			if (sigsend(P_CTID, (ctid_t)spval->val, sig) == -1 &&
736 			    errno != ESRCH) {
737 				warn_msg(gettext("Unable to signal all "
738 				    "contract members of instance %s: %s"),
739 				    inst->fmri, strerror(errno));
740 			}
741 		}
742 		return (0);
743 	}
744 
745 	/*
746 	 * sig == 0 case.
747 	 * Attempt to adopt the contract for each ctid.
748 	 */
749 	if (gen_filenms_from_fmri(inst->fmri, "ctid", genfmri_filename,
750 	    NULL) != 0) {
751 		/* Failure either from fmri too long or mkdir failure */
752 		return (ENAMETOOLONG);
753 	}
754 
755 retry_fopen:
756 	/* It's ok if no file, there are no ctids to adopt */
757 	if ((fp = fopen(genfmri_filename, "r")) == NULL) {
758 		if ((errno == EINTR) && (fopen_retry_cnt > 0)) {
759 			fopen_retry_cnt--;
760 			goto retry_fopen;
761 		}
762 		return (0);
763 	}
764 
765 	/*
766 	 * Read ctids from file into 2 lists:
767 	 * - temporary list to be traversed (uup)
768 	 * - cached list that can be modified if adoption of
769 	 *   contract fails (inst->start_ctids).
770 	 * Always treat ctids as long longs.
771 	 */
772 	uup = create_rep_val_list();
773 	/* fscanf may not set errno, so clear it first */
774 	errno = 0;
775 	while ((fscanf_ret = fscanf(fp, "%llu", &tval)) == 1) {
776 		/* If tval isn't a valid ctid, then fail. */
777 		if (tval == 0) {
778 			(void) fclose(fp);
779 			ret = EIO;
780 			goto out;
781 		}
782 		if ((add_rep_val(uup, tval) == -1) ||
783 		    (add_rep_val(inst->start_ctids, tval) == -1)) {
784 			(void) fclose(fp);
785 			ret = ENOMEM;
786 			goto out;
787 		}
788 		errno = 0;
789 	}
790 	/* EOF is not a failure when no errno */
791 	if ((fscanf_ret != EOF) || (errno != 0)) {
792 		ret = EIO;
793 		goto out;
794 	}
795 
796 	if (fclose(fp) != 0) {
797 		ret = ENXIO;
798 		goto out;
799 	}
800 
801 	for (spval = uu_list_first(uup); spval != NULL;
802 	    spval = uu_list_next(uup, spval)) {
803 		/* Try to adopt the contract */
804 		if (adopt_contract((ctid_t)spval->val,
805 		    inst->fmri) != 0) {
806 			/*
807 			 * Adoption failed.  No reason to think it'll
808 			 * work later, so remove the id from our list
809 			 * in the instance.
810 			 */
811 			remove_rep_val(inst->start_ctids, spval->val);
812 		}
813 	}
814 out:
815 	if (uup) {
816 		empty_rep_val_list(uup);
817 		destroy_rep_val_list(uup);
818 	}
819 
820 	if (ret != 0)
821 		empty_rep_val_list(inst->start_ctids);
822 
823 	return (ret);
824 }
825