xref: /titanic_50/usr/src/cmd/cmd-inet/usr.lib/inetd/repval.c (revision c77a61a72b5ecdc507d6cf104142edd371a16c84)
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