repval.c revision 3175:5903f61aa150
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 */
59static 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 */
68static scf_handle_t		*rep_handle = NULL;
69static scf_propertygroup_t	*pg = NULL;
70static scf_instance_t		*inst = NULL;
71static scf_transaction_t	*trans = NULL;
72static scf_transaction_entry_t	*entry = NULL;
73static 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 */
79static char genfmri_filename[MAXPATHLEN] = "";
80static 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 */
89int
90make_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
105int
106repval_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);
146cleanup:
147	repval_fini();
148	return (-1);
149}
150
151void
152repval_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
184uu_list_t *
185create_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
197void
198destroy_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
208rep_val_t *
209find_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
223int
224add_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
241void
242remove_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
255void
256empty_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
270int64_t
271get_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
281int
282set_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 */
302static void
303remove_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 */
326static int
327add_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 */
368static 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;
429cleanup:
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 */
448static 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 */
487static int
488repvals_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
538unlink_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 */
563static scf_error_t
564store_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
595retry_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
645out:
646	return (ret);
647}
648
649scf_error_t
650store_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
655scf_error_t
656retrieve_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 */
674int
675add_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
699retry_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
736out:
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 */
756int
757iterate_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
796retry_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	}
855out:
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