kill _FILE_OFFSET_BITS
[unleashed.git] / usr / src / cmd / svc / startd / contract.c
blob8206a55e8f6cdd18a2fab523c6a1dc9b8def0a3f
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/contract/process.h>
28 #include <sys/ctfs.h>
29 #include <sys/types.h>
30 #include <assert.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <libcontract.h>
34 #include <libcontract_priv.h>
35 #include <libuutil.h>
36 #include <limits.h>
37 #include <procfs.h>
38 #include <signal.h>
39 #include <string.h>
40 #include <unistd.h>
42 #include "startd.h"
44 void
45 contract_abandon(ctid_t ctid)
47 int err;
49 assert(ctid != 0);
51 err = contract_abandon_id(ctid);
53 if (err)
54 log_framework(LOG_NOTICE,
55 "failed to abandon contract %ld: %s\n", ctid,
56 strerror(err));
59 int
60 contract_kill(ctid_t ctid, int sig, const char *fmri)
62 if (sigsend(P_CTID, ctid, sig) == -1 && errno != ESRCH) {
63 log_error(LOG_WARNING,
64 "%s: Could not signal all contract members: %s\n", fmri,
65 strerror(errno));
66 return (-1);
69 return (0);
72 ctid_t
73 contract_init()
75 int psfd, csfd;
76 ctid_t ctid, configd_ctid = -1;
77 psinfo_t psi;
78 ct_stathdl_t s;
79 ctid_t *ctids;
80 uint_t nctids;
81 uint_t n;
82 int err;
85 * 2. Acquire any contracts we should have inherited. First, find the
86 * contract we belong to, then get its status.
88 if ((psfd = open("/proc/self/psinfo", O_RDONLY)) < 0) {
89 log_error(LOG_WARNING, "Can not open /proc/self/psinfo; unable "
90 "to check to adopt contracts: %s\n", strerror(errno));
91 return (-1);
94 if (read(psfd, &psi, sizeof (psinfo_t)) != sizeof (psinfo_t)) {
95 log_error(LOG_WARNING, "Can not read from /proc/self/psinfo; "
96 "unable to adopt contracts: %s\n",
97 strerror(errno));
98 startd_close(psfd);
99 return (-1);
102 ctid = psi.pr_contract;
104 startd_close(psfd);
106 if ((csfd = contract_open(ctid, "process", "status", O_RDONLY)) < 0) {
107 log_error(LOG_WARNING, "Can not open containing contract "
108 "status; unable to adopt contracts: %s\n", strerror(errno));
109 return (-1);
112 /* 3. Go about adopting our member list. */
114 err = ct_status_read(csfd, CTD_ALL, &s);
115 startd_close(csfd);
116 if (err) {
117 log_error(LOG_WARNING, "Can not read containing contract "
118 "status; unable to adopt: %s\n", strerror(err));
119 return (-1);
122 if (err = ct_pr_status_get_contracts(s, &ctids, &nctids)) {
123 log_error(LOG_WARNING, "Can not get my inherited contracts; "
124 "unable to adopt: %s\n", strerror(err));
125 ct_status_free(s);
126 return (-1);
129 if (nctids == 0) {
131 * We're booting, as a svc.startd which managed to fork a
132 * child will always have a svc.configd contract to adopt.
134 st->st_initial = 1;
135 ct_status_free(s);
136 return (-1);
140 * We're restarting after an interruption of some kind.
142 log_framework(LOG_NOTICE, "restarting after interruption\n");
143 st->st_initial = 0;
146 * 3'. Loop through the array, adopting them all where possible, and
147 * noting which one contains svc.configd (via a cookie vlaue of
148 * CONFIGD_COOKIE).
150 for (n = 0; n < nctids; n++) {
151 int ccfd;
152 ct_stathdl_t cs;
154 if ((ccfd = contract_open(ctids[n], "process", "ctl",
155 O_WRONLY)) < 0) {
156 log_error(LOG_WARNING, "Can not open contract %ld ctl "
157 "for adoption: %s\n", ctids[n], strerror(err));
159 continue;
162 if ((csfd = contract_open(ctids[n], "process", "status",
163 O_RDONLY)) < 0) {
164 log_error(LOG_WARNING, "Can not open contract %ld "
165 "status for cookie: %s\n", ctids[n], strerror(err));
166 startd_close(ccfd);
168 continue;
171 if (err = ct_ctl_adopt(ccfd)) {
172 log_error(LOG_WARNING, "Can not adopt contract %ld: "
173 "%s\n", ctids[n], strerror(err));
174 startd_close(ccfd);
175 startd_close(csfd);
177 continue;
180 startd_close(ccfd);
182 if (err = ct_status_read(csfd, CTD_COMMON, &cs)) {
183 log_error(LOG_WARNING, "Can not read contract %ld"
184 "status; unable to fetch cookie: %s\n", ctids[n],
185 strerror(err));
187 ct_status_free(cs);
188 startd_close(csfd);
190 continue;
193 if (ct_status_get_cookie(cs) == CONFIGD_COOKIE)
194 configd_ctid = ctids[n];
196 ct_status_free(cs);
198 startd_close(csfd);
201 ct_status_free(s);
203 return (configd_ctid);
207 contract_is_empty(ctid_t ctid)
209 int fd;
210 ct_stathdl_t ctstat;
211 pid_t *members;
212 uint_t num;
213 int ret;
215 fd = contract_open(ctid, "process", "status", O_RDONLY);
216 if (fd < 0)
217 return (1);
219 ret = ct_status_read(fd, CTD_ALL, &ctstat);
220 (void) close(fd);
221 if (ret != 0)
222 return (1);
224 ret = ct_pr_status_get_members(ctstat, &members, &num);
225 ct_status_free(ctstat);
226 if (ret != 0)
227 return (1);
229 if (num == 0)
230 return (1);
231 else
232 return (0);
235 typedef struct contract_bucket {
236 pthread_mutex_t cb_lock;
237 uu_list_t *cb_list;
238 } contract_bucket_t;
240 #define CI_HASH_SIZE 64
241 #define CI_HASH_MASK (CI_HASH_SIZE - 1);
244 * contract_hash is a hash table of contract ids to restarter instance
245 * IDs. It can be used for quick lookups when processing contract events,
246 * because the restarter instance lock doesn't need to be held to access
247 * its entries.
249 static contract_bucket_t contract_hash[CI_HASH_SIZE];
251 static contract_bucket_t *
252 contract_hold_bucket(ctid_t ctid)
254 contract_bucket_t *bp;
255 int hash;
257 hash = ctid & CI_HASH_MASK;
259 bp = &contract_hash[hash];
260 MUTEX_LOCK(&bp->cb_lock);
261 return (bp);
264 static void
265 contract_release_bucket(contract_bucket_t *bp)
267 assert(MUTEX_HELD(&bp->cb_lock));
268 MUTEX_UNLOCK(&bp->cb_lock);
271 static contract_entry_t *
272 contract_lookup(contract_bucket_t *bp, ctid_t ctid)
274 contract_entry_t *ce;
276 assert(MUTEX_HELD(&bp->cb_lock));
278 if (bp->cb_list == NULL)
279 return (NULL);
281 for (ce = uu_list_first(bp->cb_list); ce != NULL;
282 ce = uu_list_next(bp->cb_list, ce)) {
283 if (ce->ce_ctid == ctid)
284 return (ce);
287 return (NULL);
290 static void
291 contract_insert(contract_bucket_t *bp, contract_entry_t *ce)
293 int r;
295 if (bp->cb_list == NULL)
296 bp->cb_list = startd_list_create(contract_list_pool, bp, 0);
298 uu_list_node_init(ce, &ce->ce_link, contract_list_pool);
299 r = uu_list_insert_before(bp->cb_list, NULL, ce);
300 assert(r == 0);
303 void
304 contract_hash_init()
306 int i;
308 for (i = 0; i < CI_HASH_SIZE; i++)
309 (void) pthread_mutex_init(&contract_hash[i].cb_lock,
310 &mutex_attrs);
313 void
314 contract_hash_store(ctid_t ctid, int instid)
316 contract_bucket_t *bp;
317 contract_entry_t *ce;
319 bp = contract_hold_bucket(ctid);
320 assert(contract_lookup(bp, ctid) == NULL);
321 ce = startd_alloc(sizeof (contract_entry_t));
322 ce->ce_ctid = ctid;
323 ce->ce_instid = instid;
325 contract_insert(bp, ce);
327 contract_release_bucket(bp);
330 void
331 contract_hash_remove(ctid_t ctid)
333 contract_bucket_t *bp;
334 contract_entry_t *ce;
336 bp = contract_hold_bucket(ctid);
338 ce = contract_lookup(bp, ctid);
339 if (ce != NULL) {
340 uu_list_remove(bp->cb_list, ce);
341 startd_free(ce, sizeof (contract_entry_t));
344 contract_release_bucket(bp);
348 * int lookup_inst_by_contract()
349 * Lookup the instance id in the hash table by the contract id.
350 * Returns instid if found, -1 if not. Doesn't do a hold on the
351 * instance, so a check for continued existence is required.
354 lookup_inst_by_contract(ctid_t ctid)
356 contract_bucket_t *bp;
357 contract_entry_t *ce;
358 int id = -1;
360 bp = contract_hold_bucket(ctid);
361 ce = contract_lookup(bp, ctid);
362 if (ce != NULL)
363 id = ce->ce_instid;
364 contract_release_bucket(bp);
366 return (id);