1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 *
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * This file contains routines to manipulate lists of repository values that
29 * are used to store process ids and the internal state. There are routines
30 * to read/write the lists from/to the repository and routines to modify or
31 * inspect the lists. It also contains routines that deal with the
32 * repository side of contract ids.
33 */
34
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <libintl.h>
38 #include <unistd.h>
39 #include <string.h>
40 #include <signal.h>
41 #include <sys/param.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <libscf_priv.h>
45 #include "inetd_impl.h"
46
47 /*
48 * Number of consecutive repository bind retries performed by bind_to_rep()
49 * before failing.
50 */
51 #define BIND_TO_REP_RETRIES 10
52
53 /* Name of property group where inetd's state for a service is stored. */
54 #define PG_NAME_INSTANCE_STATE (const char *) "inetd_state"
55
56 /* uu_list repval list pool */
57 static uu_list_pool_t *rep_val_pool = NULL;
58
59 /*
60 * Repository object pointers that get set-up in repval_init() and closed down
61 * in repval_fini(). They're used in _retrieve_rep_vals(), _store_rep_vals(),
62 * add_remove_contract_norebind(), and adopt_repository_contracts(). They're
63 * global so they can be initialized once on inetd startup, and re-used
64 * there-after in the referenced functions.
65 */
66 static scf_handle_t *rep_handle = NULL;
67 static scf_propertygroup_t *pg = NULL;
68 static scf_instance_t *inst = NULL;
69 static scf_transaction_t *trans = NULL;
70 static scf_transaction_entry_t *entry = NULL;
71 static scf_property_t *prop = NULL;
72
73 /*
74 * Pathname storage for paths generated from the fmri.
75 * Used when updating the ctid and (start) pid files for an inetd service.
76 */
77 static char genfmri_filename[MAXPATHLEN] = "";
78 static char genfmri_temp_filename[MAXPATHLEN] = "";
79
80 /*
81 * Try and make the given handle bind be bound to the repository. If
82 * it's already bound, or we succeed a new bind return 0; else return
83 * -1 on failure, with the SCF error set to one of the following:
84 * SCF_ERROR_NO_SERVER
85 * SCF_ERROR_NO_RESOURCES
86 */
87 int
make_handle_bound(scf_handle_t * hdl)88 make_handle_bound(scf_handle_t *hdl)
89 {
90 uint_t retries;
91
92 for (retries = 0; retries <= BIND_TO_REP_RETRIES; retries++) {
93 if ((scf_handle_bind(hdl) == 0) ||
94 (scf_error() == SCF_ERROR_IN_USE))
95 return (0);
96
97 assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
98 }
99
100 return (-1);
101 }
102
103 int
repval_init(void)104 repval_init(void)
105 {
106 /*
107 * Create the repval list pool.
108 */
109 rep_val_pool = uu_list_pool_create("rep_val_pool", sizeof (rep_val_t),
110 offsetof(rep_val_t, link), NULL, UU_LIST_POOL_DEBUG);
111 if (rep_val_pool == NULL) {
112 error_msg("%s: %s", gettext("Failed to create rep_val pool"),
113 uu_strerror(uu_error()));
114 return (-1);
115 }
116
117 /*
118 * Create and bind a repository handle, and create all repository
119 * objects that we'll use later that are associated with it. On any
120 * errors we simply return -1 and let repval_fini() clean-up after
121 * us.
122 */
123 if ((rep_handle = scf_handle_create(SCF_VERSION)) == NULL) {
124 error_msg("%s: %s",
125 gettext("Failed to create repository handle"),
126 scf_strerror(scf_error()));
127 goto cleanup;
128 } else if (make_handle_bound(rep_handle) == -1) {
129 goto cleanup;
130 } else if (((pg = scf_pg_create(rep_handle)) == NULL) ||
131 ((inst = scf_instance_create(rep_handle)) == NULL) ||
132 ((trans = scf_transaction_create(rep_handle)) == NULL) ||
133 ((entry = scf_entry_create(rep_handle)) == NULL) ||
134 ((prop = scf_property_create(rep_handle)) == NULL)) {
135 error_msg("%s: %s",
136 gettext("Failed to create repository object"),
137 scf_strerror(scf_error()));
138 goto cleanup;
139 }
140
141 return (0);
142 cleanup:
143 repval_fini();
144 return (-1);
145 }
146
147 void
repval_fini(void)148 repval_fini(void)
149 {
150 if (rep_handle != NULL) {
151 /*
152 * We unbind from the repository before we free the repository
153 * objects for efficiency reasons.
154 */
155 (void) scf_handle_unbind(rep_handle);
156
157 scf_pg_destroy(pg);
158 pg = NULL;
159 scf_instance_destroy(inst);
160 inst = NULL;
161 scf_transaction_destroy(trans);
162 trans = NULL;
163 scf_entry_destroy(entry);
164 entry = NULL;
165 scf_property_destroy(prop);
166 prop = NULL;
167
168 scf_handle_destroy(rep_handle);
169 rep_handle = NULL;
170 }
171
172 if (rep_val_pool != NULL) {
173 uu_list_pool_destroy(rep_val_pool);
174 rep_val_pool = NULL;
175 }
176 }
177
178 uu_list_t *
create_rep_val_list(void)179 create_rep_val_list(void)
180 {
181 uu_list_t *ret;
182
183 if ((ret = uu_list_create(rep_val_pool, NULL, 0)) == NULL)
184 assert(uu_error() == UU_ERROR_NO_MEMORY);
185
186 return (ret);
187 }
188
189 void
destroy_rep_val_list(uu_list_t * list)190 destroy_rep_val_list(uu_list_t *list)
191 {
192 if (list != NULL) {
193 empty_rep_val_list(list);
194 uu_list_destroy(list);
195 }
196 }
197
198 rep_val_t *
find_rep_val(uu_list_t * list,int64_t val)199 find_rep_val(uu_list_t *list, int64_t val)
200 {
201 rep_val_t *rv;
202
203 for (rv = uu_list_first(list); rv != NULL;
204 rv = uu_list_next(list, rv)) {
205 if (rv->val == val)
206 break;
207 }
208 return (rv);
209 }
210
211 int
add_rep_val(uu_list_t * list,int64_t val)212 add_rep_val(uu_list_t *list, int64_t val)
213 {
214 rep_val_t *rv;
215
216 if ((rv = malloc(sizeof (rep_val_t))) == NULL)
217 return (-1);
218
219 uu_list_node_init(rv, &rv->link, rep_val_pool);
220 rv->val = val;
221 rv->scf_val = NULL;
222 (void) uu_list_insert_after(list, NULL, rv);
223
224 return (0);
225 }
226
227 void
remove_rep_val(uu_list_t * list,int64_t val)228 remove_rep_val(uu_list_t *list, int64_t val)
229 {
230 rep_val_t *rv;
231
232 if ((rv = find_rep_val(list, val)) != NULL) {
233 uu_list_remove(list, rv);
234 assert(rv->scf_val == NULL);
235 free(rv);
236 }
237 }
238
239 void
empty_rep_val_list(uu_list_t * list)240 empty_rep_val_list(uu_list_t *list)
241 {
242 void *cookie = NULL;
243 rep_val_t *rv;
244
245 while ((rv = uu_list_teardown(list, &cookie)) != NULL) {
246 if (rv->scf_val != NULL)
247 scf_value_destroy(rv->scf_val);
248 free(rv);
249 }
250 }
251
252 int64_t
get_single_rep_val(uu_list_t * list)253 get_single_rep_val(uu_list_t *list)
254 {
255 rep_val_t *rv = uu_list_first(list);
256
257 assert(rv != NULL);
258 return (rv->val);
259 }
260
261 int
set_single_rep_val(uu_list_t * list,int64_t val)262 set_single_rep_val(uu_list_t *list, int64_t val)
263 {
264 rep_val_t *rv = uu_list_first(list);
265
266 if (rv == NULL) {
267 if (add_rep_val(list, val) == -1)
268 return (-1);
269 } else {
270 rv->val = val;
271 }
272
273 return (0);
274 }
275
276 /*
277 * Partner to add_tr_entry_values. This function frees the scf_values created
278 * in add_tr_entry_values() in the list 'vals'.
279 */
280 static void
remove_tr_entry_values(uu_list_t * vals)281 remove_tr_entry_values(uu_list_t *vals)
282 {
283 rep_val_t *rval;
284
285 for (rval = uu_list_first(vals); rval != NULL;
286 rval = uu_list_next(vals, rval)) {
287 if (rval->scf_val != NULL) {
288 scf_value_destroy(rval->scf_val);
289 rval->scf_val = NULL;
290 }
291 }
292 }
293
294 /*
295 * This function creates and associates with transaction entry 'entry' an
296 * scf value for each value in 'vals'. The pointers to the scf values
297 * are stored in the list for later cleanup by remove_tr_entry_values.
298 * Returns 0 on success, else -1 on error with scf_error() set to:
299 * SCF_ERROR_NO_MEMORY if memory allocation failed.
300 * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
301 */
302 static int
add_tr_entry_values(scf_handle_t * hdl,scf_transaction_entry_t * entry,uu_list_t * vals)303 add_tr_entry_values(scf_handle_t *hdl, scf_transaction_entry_t *entry,
304 uu_list_t *vals)
305 {
306 rep_val_t *rval;
307
308 for (rval = uu_list_first(vals); rval != NULL;
309 rval = uu_list_next(vals, rval)) {
310
311 assert(rval->scf_val == NULL);
312 if ((rval->scf_val = scf_value_create(hdl)) == NULL) {
313 remove_tr_entry_values(vals);
314 return (-1);
315 }
316
317 scf_value_set_integer(rval->scf_val, rval->val);
318
319 if (scf_entry_add_value(entry, rval->scf_val) < 0) {
320 remove_tr_entry_values(vals);
321 return (-1);
322 }
323 }
324
325 return (0);
326 }
327
328 /*
329 * Stores the values contained in the list 'vals' into the property 'prop_name'
330 * of the instance with fmri 'inst_fmri', within the instance's instance
331 * state property group.
332 *
333 * Returns 0 on success, else one of the following on failure:
334 * SCF_ERROR_NO_MEMORY if memory allocation failed.
335 * SCF_ERROR_NO_RESOURCES if the server doesn't have required resources.
336 * SCF_ERROR_VERSION_MISMATCH if program compiled against a newer libscf
337 * than on system.
338 * SCF_ERROR_PERMISSION_DENIED if insufficient privileges to modify pg.
339 * SCF_ERROR_BACKEND_ACCESS if the repository back-end refused the pg modify.
340 * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
341 */
342 static scf_error_t
_store_rep_vals(uu_list_t * vals,const char * inst_fmri,const char * prop_name)343 _store_rep_vals(uu_list_t *vals, const char *inst_fmri, const char *prop_name)
344 {
345 int cret;
346 int ret;
347
348 if (scf_handle_decode_fmri(rep_handle, inst_fmri, NULL, NULL, inst,
349 NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1)
350 return (scf_error());
351
352 /*
353 * Fetch the instance state pg, and if it doesn't exist try and
354 * create it.
355 */
356 if (scf_instance_get_pg(inst, PG_NAME_INSTANCE_STATE, pg) < 0) {
357 if (scf_error() != SCF_ERROR_NOT_FOUND)
358 return (scf_error());
359 if (scf_instance_add_pg(inst, PG_NAME_INSTANCE_STATE,
360 SCF_GROUP_FRAMEWORK, SCF_PG_FLAG_NONPERSISTENT, pg) < 0)
361 return (scf_error());
362 }
363
364 /*
365 * Perform a transaction to write the values to the requested property.
366 * If someone got there before us, loop and retry.
367 */
368 do {
369 if (scf_transaction_start(trans, pg) < 0)
370 return (scf_error());
371
372 if ((scf_transaction_property_new(trans, entry,
373 prop_name, SCF_TYPE_INTEGER) < 0) &&
374 (scf_transaction_property_change_type(trans, entry,
375 prop_name, SCF_TYPE_INTEGER) < 0)) {
376 ret = scf_error();
377 goto cleanup;
378 }
379
380 if (add_tr_entry_values(rep_handle, entry, vals) < 0) {
381 ret = scf_error();
382 goto cleanup;
383 }
384
385 if ((cret = scf_transaction_commit(trans)) < 0) {
386 ret = scf_error();
387 goto cleanup;
388 } else if (cret == 0) {
389 scf_transaction_reset(trans);
390 scf_entry_reset(entry);
391 remove_tr_entry_values(vals);
392 if (scf_pg_update(pg) < 0) {
393 ret = scf_error();
394 goto cleanup;
395 }
396 }
397 } while (cret == 0);
398
399 ret = 0;
400 cleanup:
401 scf_transaction_reset(trans);
402 scf_entry_reset(entry);
403 remove_tr_entry_values(vals);
404 return (ret);
405 }
406
407 /*
408 * Retrieves the repository values of property 'prop_name', of the instance
409 * with fmri 'fmri', from within the instance's instance state property
410 * group and adds them to the value list 'list'.
411 *
412 * Returns 0 on success, else one of the following values on error:
413 * SCF_ERROR_NOT_FOUND if the property doesn't exist.
414 * SCF_ERROR_NO_MEMORY if memory allocation failed.
415 * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
416 * SCF_ERROR_TYPE_MISMATCH if the property was of an unexpected type.
417 *
418 */
419 static scf_error_t
_retrieve_rep_vals(uu_list_t * list,const char * fmri,const char * prop_name)420 _retrieve_rep_vals(uu_list_t *list, const char *fmri, const char *prop_name)
421 {
422 scf_simple_prop_t *sp;
423 int64_t *ip;
424
425 if ((sp = scf_simple_prop_get(rep_handle, fmri, PG_NAME_INSTANCE_STATE,
426 prop_name)) == NULL)
427 return (scf_error());
428
429 while ((ip = scf_simple_prop_next_integer(sp)) != NULL) {
430 if (add_rep_val(list, *ip) == -1) {
431 empty_rep_val_list(list);
432 scf_simple_prop_free(sp);
433 return (SCF_ERROR_NO_MEMORY);
434 }
435 }
436 if (scf_error() != SCF_ERROR_NONE) {
437 assert(scf_error() == SCF_ERROR_TYPE_MISMATCH);
438 empty_rep_val_list(list);
439 scf_simple_prop_free(sp);
440 return (scf_error());
441 }
442
443 scf_simple_prop_free(sp);
444 return (0);
445 }
446
447 /*
448 * Writes the repository values in the vals list to
449 * a file that is generated based on the passed in fmri and name.
450 * Returns 0 on success,
451 * ENAMETOOLONG if unable to generate filename from fmri (including
452 * the inability to create the directory for the generated filename) and
453 * ENOENT on all other failures.
454 */
455 static int
repvals_to_file(const char * fmri,const char * name,uu_list_t * vals)456 repvals_to_file(const char *fmri, const char *name, uu_list_t *vals)
457 {
458 int tfd;
459 FILE *tfp; /* temp fp */
460 rep_val_t *spval; /* Contains a start_pid or ctid */
461 int ret = 0;
462
463 if (gen_filenms_from_fmri(fmri, name, genfmri_filename,
464 genfmri_temp_filename) != 0) {
465 /* Failure either from fmri too long or mkdir failure */
466 return (ENAMETOOLONG);
467 }
468
469 if ((tfd = mkstemp(genfmri_temp_filename)) == -1) {
470 return (ENOENT);
471 }
472
473 if (fchmod(tfd, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) {
474 (void) close(tfd);
475 ret = ENOENT;
476 goto unlink_out;
477 }
478
479 if ((tfp = fdopen(tfd, "w")) == NULL) {
480 (void) close(tfd);
481 ret = ENOENT;
482 goto unlink_out;
483 }
484
485 for (spval = uu_list_first(vals); spval != NULL;
486 spval = uu_list_next(vals, spval)) {
487 if (fprintf(tfp, "%lld\n", spval->val) <= 0) {
488 (void) fclose(tfp);
489 ret = ENOENT;
490 goto unlink_out;
491 }
492 }
493 if (fclose(tfp) != 0) {
494 ret = ENOENT;
495 goto unlink_out;
496 }
497 if (rename(genfmri_temp_filename, genfmri_filename) != 0) {
498 ret = ENOENT;
499 goto unlink_out;
500 }
501 return (0);
502
503 unlink_out:
504 if (unlink(genfmri_temp_filename) != 0) {
505 warn_msg(gettext("Removal of temp file "
506 "%s failed. Please remove manually."),
507 genfmri_temp_filename);
508 }
509 return (ret);
510 }
511
512 /*
513 * A routine that loops trying to read/write values until either success,
514 * an error other than a broken repository connection or
515 * the number of retries reaches REP_OP_RETRIES.
516 * This action is used to read/write the values:
517 * reads/writes to a file for the START_PIDS property due to scalability
518 * problems with libscf
519 * reads/writes to the repository for all other properties.
520 * Returns 0 on success, else the error value from either _store_rep_vals or
521 * _retrieve_rep_vals (based on whether 'store' was set or not), or one of the
522 * following:
523 * SCF_ERROR_NO_RESOURCES if the server doesn't have adequate resources
524 * SCF_ERROR_NO_MEMORY if a memory allocation failure
525 * SCF_ERROR_NO_SERVER if the server isn't running.
526 * SCF_ERROR_CONSTRAINT_VIOLATED if an error in dealing with the speedy files
527 */
528 static scf_error_t
store_retrieve_rep_vals(uu_list_t * vals,const char * fmri,const char * prop,boolean_t store)529 store_retrieve_rep_vals(uu_list_t *vals, const char *fmri,
530 const char *prop, boolean_t store)
531 {
532 scf_error_t ret = 0;
533 uint_t retries;
534 FILE *tfp; /* temp fp */
535 int64_t tval; /* temp val holder */
536 int fscanf_ret;
537 int fopen_retry_cnt = 2;
538
539 /* inetd specific action for START_PIDS property */
540 if (strcmp(prop, PR_NAME_START_PIDS) == 0) {
541 /*
542 * Storage performance of START_PIDS is important,
543 * so each instance has its own file and all start_pids
544 * in the list are written to a temp file and then
545 * moved (renamed).
546 */
547 if (store) {
548 /* Write all values in list to file */
549 if (repvals_to_file(fmri, "pid", vals)) {
550 return (SCF_ERROR_CONSTRAINT_VIOLATED);
551 }
552 } else {
553 /* no temp name needed */
554 if (gen_filenms_from_fmri(fmri, "pid", genfmri_filename,
555 NULL) != 0)
556 return (SCF_ERROR_CONSTRAINT_VIOLATED);
557
558 retry_fopen:
559 /* It's ok if no file, there are just no pids */
560 if ((tfp = fopen(genfmri_filename, "r")) == NULL) {
561 if ((errno == EINTR) && (fopen_retry_cnt > 0)) {
562 fopen_retry_cnt--;
563 goto retry_fopen;
564 }
565 return (0);
566 }
567 /* fscanf may not set errno, so clear it first */
568 errno = 0;
569 while ((fscanf_ret = fscanf(tfp, "%lld", &tval)) == 1) {
570 /* If tval isn't a valid pid, then fail. */
571 if ((tval > MAXPID) || (tval <= 0)) {
572 empty_rep_val_list(vals);
573 return (SCF_ERROR_CONSTRAINT_VIOLATED);
574 }
575 if (add_rep_val(vals, tval) == -1) {
576 empty_rep_val_list(vals);
577 return (SCF_ERROR_NO_MEMORY);
578 }
579 errno = 0;
580 }
581 /* EOF is ok when no errno */
582 if ((fscanf_ret != EOF) || (errno != 0)) {
583 empty_rep_val_list(vals);
584 return (SCF_ERROR_CONSTRAINT_VIOLATED);
585 }
586 if (fclose(tfp) != 0) {
587 /* for close failure just log a message */
588 warn_msg(gettext("Close of file %s failed."),
589 genfmri_filename);
590 }
591 }
592 } else {
593 for (retries = 0; retries <= REP_OP_RETRIES; retries++) {
594 if (make_handle_bound(rep_handle) == -1) {
595 ret = scf_error();
596 break;
597 }
598
599 if ((ret = (store ? _store_rep_vals(vals, fmri, prop) :
600 _retrieve_rep_vals(vals, fmri, prop))) !=
601 SCF_ERROR_CONNECTION_BROKEN)
602 break;
603
604 (void) scf_handle_unbind(rep_handle);
605 }
606 }
607
608 out:
609 return (ret);
610 }
611
612 scf_error_t
store_rep_vals(uu_list_t * vals,const char * fmri,const char * prop)613 store_rep_vals(uu_list_t *vals, const char *fmri, const char *prop)
614 {
615 return (store_retrieve_rep_vals(vals, fmri, prop, B_TRUE));
616 }
617
618 scf_error_t
retrieve_rep_vals(uu_list_t * vals,const char * fmri,const char * prop)619 retrieve_rep_vals(uu_list_t *vals, const char *fmri, const char *prop)
620 {
621 return (store_retrieve_rep_vals(vals, fmri, prop, B_FALSE));
622 }
623
624 /*
625 * Adds/removes a contract id to/from the cached list kept in the instance.
626 * Then the cached list is written to a file named "ctid" in a directory
627 * based on the fmri. Cached list is written to a file due to scalability
628 * problems in libscf. The file "ctid" is used when inetd is restarted
629 * so that inetd can adopt the contracts that it had previously.
630 * Returns:
631 * 0 on success
632 * ENAMETOOLONG if unable to generate filename from fmri (including
633 * the inability to create the directory for the generated filename)
634 * ENOENT - failure accessing file
635 * ENOMEM - memory allocation failure
636 */
637 int
add_remove_contract(instance_t * inst,boolean_t add,ctid_t ctid)638 add_remove_contract(instance_t *inst, boolean_t add, ctid_t ctid)
639 {
640 FILE *tfp; /* temp fp */
641 int ret = 0;
642 int repval_ret = 0;
643 int fopen_retry_cnt = 2;
644
645 /*
646 * Storage performance of contract ids is important,
647 * so each instance has its own file. An add of a
648 * ctid will be appended to the ctid file.
649 * The removal of a ctid will result in the remaining
650 * ctids in the list being written to a temp file and then
651 * moved (renamed).
652 */
653 if (add) {
654 if (gen_filenms_from_fmri(inst->fmri, "ctid", genfmri_filename,
655 NULL) != 0) {
656 /* Failure either from fmri too long or mkdir failure */
657 return (ENAMETOOLONG);
658 }
659
660 retry_fopen:
661 if ((tfp = fopen(genfmri_filename, "a")) == NULL) {
662 if ((errno == EINTR) && (fopen_retry_cnt > 0)) {
663 fopen_retry_cnt--;
664 goto retry_fopen;
665 }
666 ret = ENOENT;
667 goto out;
668 }
669
670 /* Always store ctids as long long */
671 if (fprintf(tfp, "%llu\n", (uint64_t)ctid) <= 0) {
672 (void) fclose(tfp);
673 ret = ENOENT;
674 goto out;
675 }
676
677 if (fclose(tfp) != 0) {
678 ret = ENOENT;
679 goto out;
680 }
681
682 if (add_rep_val(inst->start_ctids, ctid) != 0) {
683 ret = ENOMEM;
684 goto out;
685 }
686 } else {
687 remove_rep_val(inst->start_ctids, ctid);
688
689 /* Write all values in list to file */
690 if ((repval_ret = repvals_to_file(inst->fmri, "ctid",
691 inst->start_ctids)) != 0) {
692 ret = repval_ret;
693 goto out;
694 }
695 }
696
697 out:
698 return (ret);
699 }
700
701 /*
702 * If sig !=0, iterate over all contracts in the cached list of contract
703 * ids kept in the instance. Send each contract the specified signal.
704 * If sig == 0, read in the contract ids that were last associated
705 * with this instance (reload the cache) and call adopt_contract()
706 * to take ownership.
707 *
708 * Returns 0 on success;
709 * ENAMETOOLONG if unable to generate filename from fmri (including
710 * the inability to create the directory for the generated filename) and
711 * ENXIO if a failure accessing the file
712 * ENOMEM if there was a memory allocation failure
713 * ENOENT if the instance, its restarter property group, or its
714 * contract property don't exist
715 * EIO if invalid data read from the file
716 */
717 int
iterate_repository_contracts(instance_t * inst,int sig)718 iterate_repository_contracts(instance_t *inst, int sig)
719 {
720 int ret = 0;
721 FILE *fp;
722 rep_val_t *spval = NULL; /* Contains a start_pid */
723 uint64_t tval; /* temp val holder */
724 uu_list_t *uup = NULL;
725 int fscanf_ret;
726 int fopen_retry_cnt = 2;
727
728 if (sig != 0) {
729 /*
730 * Send a signal to all in the contract; ESRCH just
731 * means they all exited before we could kill them
732 */
733 for (spval = uu_list_first(inst->start_ctids); spval != NULL;
734 spval = uu_list_next(inst->start_ctids, spval)) {
735 if (sigsend(P_CTID, (ctid_t)spval->val, sig) == -1 &&
736 errno != ESRCH) {
737 warn_msg(gettext("Unable to signal all "
738 "contract members of instance %s: %s"),
739 inst->fmri, strerror(errno));
740 }
741 }
742 return (0);
743 }
744
745 /*
746 * sig == 0 case.
747 * Attempt to adopt the contract for each ctid.
748 */
749 if (gen_filenms_from_fmri(inst->fmri, "ctid", genfmri_filename,
750 NULL) != 0) {
751 /* Failure either from fmri too long or mkdir failure */
752 return (ENAMETOOLONG);
753 }
754
755 retry_fopen:
756 /* It's ok if no file, there are no ctids to adopt */
757 if ((fp = fopen(genfmri_filename, "r")) == NULL) {
758 if ((errno == EINTR) && (fopen_retry_cnt > 0)) {
759 fopen_retry_cnt--;
760 goto retry_fopen;
761 }
762 return (0);
763 }
764
765 /*
766 * Read ctids from file into 2 lists:
767 * - temporary list to be traversed (uup)
768 * - cached list that can be modified if adoption of
769 * contract fails (inst->start_ctids).
770 * Always treat ctids as long longs.
771 */
772 uup = create_rep_val_list();
773 /* fscanf may not set errno, so clear it first */
774 errno = 0;
775 while ((fscanf_ret = fscanf(fp, "%llu", &tval)) == 1) {
776 /* If tval isn't a valid ctid, then fail. */
777 if (tval == 0) {
778 (void) fclose(fp);
779 ret = EIO;
780 goto out;
781 }
782 if ((add_rep_val(uup, tval) == -1) ||
783 (add_rep_val(inst->start_ctids, tval) == -1)) {
784 (void) fclose(fp);
785 ret = ENOMEM;
786 goto out;
787 }
788 errno = 0;
789 }
790 /* EOF is not a failure when no errno */
791 if ((fscanf_ret != EOF) || (errno != 0)) {
792 ret = EIO;
793 goto out;
794 }
795
796 if (fclose(fp) != 0) {
797 ret = ENXIO;
798 goto out;
799 }
800
801 for (spval = uu_list_first(uup); spval != NULL;
802 spval = uu_list_next(uup, spval)) {
803 /* Try to adopt the contract */
804 if (adopt_contract((ctid_t)spval->val,
805 inst->fmri) != 0) {
806 /*
807 * Adoption failed. No reason to think it'll
808 * work later, so remove the id from our list
809 * in the instance.
810 */
811 remove_rep_val(inst->start_ctids, spval->val);
812 }
813 }
814 out:
815 if (uup) {
816 empty_rep_val_list(uup);
817 destroy_rep_val_list(uup);
818 }
819
820 if (ret != 0)
821 empty_rep_val_list(inst->start_ctids);
822
823 return (ret);
824 }
825