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
make_handle_bound(scf_handle_t * hdl)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
repval_init(void)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
repval_fini(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 *
create_rep_val_list(void)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
destroy_rep_val_list(uu_list_t * list)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 *
find_rep_val(uu_list_t * list,int64_t val)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
add_rep_val(uu_list_t * list,int64_t val)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
remove_rep_val(uu_list_t * list,int64_t val)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
empty_rep_val_list(uu_list_t * list)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
get_single_rep_val(uu_list_t * list)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
set_single_rep_val(uu_list_t * list,int64_t val)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
remove_tr_entry_values(uu_list_t * vals)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
add_tr_entry_values(scf_handle_t * hdl,scf_transaction_entry_t * entry,uu_list_t * vals)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
_store_rep_vals(uu_list_t * vals,const char * inst_fmri,const char * prop_name)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
_retrieve_rep_vals(uu_list_t * list,const char * fmri,const char * prop_name)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
repvals_to_file(const char * fmri,const char * name,uu_list_t * vals)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
store_retrieve_rep_vals(uu_list_t * vals,const char * fmri,const char * prop,boolean_t store)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
store_rep_vals(uu_list_t * vals,const char * fmri,const char * prop)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
retrieve_rep_vals(uu_list_t * vals,const char * fmri,const char * prop)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
add_remove_contract(instance_t * inst,boolean_t add,ctid_t ctid)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
iterate_repository_contracts(instance_t * inst,int sig)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