xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.lib/inetd/repval.c (revision 628e3cbed6489fa1db545d8524a06cd6535af456)
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