xref: /illumos-gate/usr/src/cmd/svc/startd/contract.c (revision 355b4669e025ff377602b6fc7caaf30dbc218371)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 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 #ifdef _FILE_OFFSET_BITS
30 #undef _FILE_OFFSET_BITS
31 #endif /* _FILE_OFFSET_BITS */
32 
33 #include <sys/contract/process.h>
34 #include <sys/ctfs.h>
35 #include <sys/types.h>
36 #include <assert.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <libcontract.h>
40 #include <libcontract_priv.h>
41 #include <libuutil.h>
42 #include <limits.h>
43 #include <procfs.h>
44 #include <signal.h>
45 #include <string.h>
46 #include <unistd.h>
47 
48 #include "startd.h"
49 
50 void
51 contract_abandon(ctid_t ctid)
52 {
53 	int err;
54 
55 	assert(ctid != 0);
56 
57 	err = contract_abandon_id(ctid);
58 
59 	if (err)
60 		log_framework(LOG_NOTICE,
61 		    "failed to abandon contract %ld: %s\n", ctid,
62 		    strerror(err));
63 }
64 
65 int
66 contract_kill(ctid_t ctid, int sig, const char *fmri)
67 {
68 	if (sigsend(P_CTID, ctid, sig) == -1 && errno != ESRCH) {
69 		log_error(LOG_WARNING,
70 		    "%s: Could not signal all contract members: %s\n", fmri,
71 		    strerror(errno));
72 		return (-1);
73 	}
74 
75 	return (0);
76 }
77 
78 ctid_t
79 contract_init()
80 {
81 	int psfd, csfd;
82 	ctid_t ctid, configd_ctid = -1;
83 	psinfo_t psi;
84 	ct_stathdl_t s;
85 	ctid_t *ctids;
86 	uint_t nctids;
87 	uint_t n;
88 	int err;
89 
90 	/*
91 	 * 2.  Acquire any contracts we should have inherited.  First, find the
92 	 * contract we belong to, then get its status.
93 	 */
94 	if ((psfd = open("/proc/self/psinfo", O_RDONLY)) < 0) {
95 		log_error(LOG_WARNING, "Can not open /proc/self/psinfo; unable "
96 		    "to check to adopt contracts: %s\n", strerror(errno));
97 		return (-1);
98 	}
99 
100 	if (read(psfd, &psi, sizeof (psinfo_t)) != sizeof (psinfo_t)) {
101 		log_error(LOG_WARNING, "Can not read from /proc/self/psinfo; "
102 		    "unable to adopt contracts: %s\n",
103 		    strerror(errno));
104 		startd_close(psfd);
105 		return (-1);
106 	}
107 
108 	ctid = psi.pr_contract;
109 
110 	startd_close(psfd);
111 
112 	if ((csfd = contract_open(ctid, "process", "status", O_RDONLY)) < 0) {
113 		log_error(LOG_WARNING, "Can not open containing contract "
114 		    "status; unable to adopt contracts: %s\n", strerror(errno));
115 		return (-1);
116 	}
117 
118 	/* 3.  Go about adopting our member list. */
119 
120 	err = ct_status_read(csfd, CTD_ALL, &s);
121 	startd_close(csfd);
122 	if (err) {
123 		log_error(LOG_WARNING, "Can not read containing contract "
124 		    "status; unable to adopt: %s\n", strerror(err));
125 		return (-1);
126 	}
127 
128 	if (err = ct_pr_status_get_contracts(s, &ctids, &nctids)) {
129 		log_error(LOG_WARNING, "Can not get my inherited contracts; "
130 		    "unable to adopt: %s\n", strerror(err));
131 		ct_status_free(s);
132 		return (-1);
133 	}
134 
135 	if (nctids == 0) {
136 		/*
137 		 * We're booting, as a svc.startd which managed to fork a
138 		 * child will always have a svc.configd contract to adopt.
139 		 */
140 		st->st_initial = 1;
141 		ct_status_free(s);
142 		return (-1);
143 	}
144 
145 	/*
146 	 * We're restarting after an interruption of some kind.
147 	 */
148 	log_framework(LOG_NOTICE, "restarting after interruption\n");
149 	st->st_initial = 0;
150 
151 	/*
152 	 * 3'.  Loop through the array, adopting them all where possible, and
153 	 * noting which one contains svc.configd (via a cookie vlaue of
154 	 * CONFIGD_COOKIE).
155 	 */
156 	for (n = 0; n < nctids; n++) {
157 		int ccfd;
158 		ct_stathdl_t cs;
159 
160 		if ((ccfd = contract_open(ctids[n], "process", "ctl",
161 		    O_WRONLY)) < 0) {
162 			log_error(LOG_WARNING, "Can not open contract %ld ctl "
163 			    "for adoption: %s\n", ctids[n], strerror(err));
164 
165 			continue;
166 		}
167 
168 		if ((csfd = contract_open(ctids[n], "process", "status",
169 		    O_RDONLY)) < 0) {
170 			log_error(LOG_WARNING, "Can not open contract %ld "
171 			    "status for cookie: %s\n", ctids[n], strerror(err));
172 			startd_close(ccfd);
173 
174 			continue;
175 		}
176 
177 		if (err = ct_ctl_adopt(ccfd)) {
178 			log_error(LOG_WARNING, "Can not adopt contract %ld: "
179 			    "%s\n", ctids[n], strerror(err));
180 			startd_close(ccfd);
181 			startd_close(csfd);
182 
183 			continue;
184 		}
185 
186 		startd_close(ccfd);
187 
188 		if (err = ct_status_read(csfd, CTD_COMMON, &cs)) {
189 			log_error(LOG_WARNING, "Can not read contract %ld"
190 			    "status; unable to fetch cookie: %s\n", ctids[n],
191 			    strerror(err));
192 
193 			ct_status_free(cs);
194 			startd_close(csfd);
195 
196 			continue;
197 		}
198 
199 		if (ct_status_get_cookie(cs) == CONFIGD_COOKIE)
200 			configd_ctid = ctids[n];
201 
202 		ct_status_free(cs);
203 
204 		startd_close(csfd);
205 	}
206 
207 	ct_status_free(s);
208 
209 	return (configd_ctid);
210 }
211 
212 int
213 contract_is_empty(ctid_t ctid)
214 {
215 	int fd;
216 	ct_stathdl_t ctstat;
217 	pid_t *members;
218 	uint_t num;
219 	int ret;
220 
221 	fd = contract_open(ctid, "process", "status", O_RDONLY);
222 	if (fd < 0)
223 		return (1);
224 
225 	ret = ct_status_read(fd, CTD_ALL, &ctstat);
226 	(void) close(fd);
227 	if (ret != 0)
228 		return (1);
229 
230 	ret = ct_pr_status_get_members(ctstat, &members, &num);
231 	ct_status_free(ctstat);
232 	if (ret != 0)
233 		return (1);
234 
235 	if (num == 0)
236 		return (1);
237 	else
238 		return (0);
239 }
240 
241 typedef struct contract_bucket {
242 	pthread_mutex_t cb_lock;
243 	uu_list_t	*cb_list;
244 } contract_bucket_t;
245 
246 #define	CI_HASH_SIZE	64
247 #define	CI_HASH_MASK	(CI_HASH_SIZE - 1);
248 
249 /*
250  * contract_hash is a hash table of contract ids to restarter instance
251  * IDs.  It can be used for quick lookups when processing contract events,
252  * because the restarter instance lock doesn't need to be held to access
253  * its entries.
254  */
255 static contract_bucket_t contract_hash[CI_HASH_SIZE];
256 
257 static contract_bucket_t *
258 contract_hold_bucket(ctid_t ctid)
259 {
260 	contract_bucket_t *bp;
261 	int hash;
262 
263 	hash = ctid & CI_HASH_MASK;
264 
265 	bp = &contract_hash[hash];
266 	MUTEX_LOCK(&bp->cb_lock);
267 	return (bp);
268 }
269 
270 static void
271 contract_release_bucket(contract_bucket_t *bp)
272 {
273 	assert(PTHREAD_MUTEX_HELD(&bp->cb_lock));
274 	MUTEX_UNLOCK(&bp->cb_lock);
275 }
276 
277 static contract_entry_t *
278 contract_lookup(contract_bucket_t *bp, ctid_t ctid)
279 {
280 	contract_entry_t *ce;
281 
282 	assert(PTHREAD_MUTEX_HELD(&bp->cb_lock));
283 
284 	if (bp->cb_list == NULL)
285 		return (NULL);
286 
287 	for (ce = uu_list_first(bp->cb_list); ce != NULL;
288 	    ce = uu_list_next(bp->cb_list, ce)) {
289 		if (ce->ce_ctid == ctid)
290 			return (ce);
291 	}
292 
293 	return (NULL);
294 }
295 
296 static void
297 contract_insert(contract_bucket_t *bp, contract_entry_t *ce)
298 {
299 	int r;
300 
301 	if (bp->cb_list == NULL)
302 		bp->cb_list = startd_list_create(contract_list_pool, bp, 0);
303 
304 	uu_list_node_init(ce, &ce->ce_link, contract_list_pool);
305 	r = uu_list_insert_before(bp->cb_list, NULL, ce);
306 	assert(r == 0);
307 }
308 
309 void
310 contract_hash_init()
311 {
312 	int i;
313 
314 	for (i = 0; i < CI_HASH_SIZE; i++)
315 		(void) pthread_mutex_init(&contract_hash[i].cb_lock,
316 		    &mutex_attrs);
317 }
318 
319 void
320 contract_hash_store(ctid_t ctid, int instid)
321 {
322 	contract_bucket_t *bp;
323 	contract_entry_t *ce;
324 
325 	bp = contract_hold_bucket(ctid);
326 	assert(contract_lookup(bp, ctid) == NULL);
327 	ce = startd_alloc(sizeof (contract_entry_t));
328 	ce->ce_ctid = ctid;
329 	ce->ce_instid = instid;
330 
331 	contract_insert(bp, ce);
332 
333 	contract_release_bucket(bp);
334 }
335 
336 void
337 contract_hash_remove(ctid_t ctid)
338 {
339 	contract_bucket_t *bp;
340 	contract_entry_t *ce;
341 
342 	bp = contract_hold_bucket(ctid);
343 
344 	ce = contract_lookup(bp, ctid);
345 	if (ce != NULL) {
346 		uu_list_remove(bp->cb_list, ce);
347 		startd_free(ce, sizeof (contract_entry_t));
348 	}
349 
350 	contract_release_bucket(bp);
351 }
352 
353 /*
354  * int lookup_inst_by_contract()
355  *   Lookup the instance id in the hash table by the contract id.
356  *   Returns instid if found, -1 if not.  Doesn't do a hold on the
357  *   instance, so a check for continued existence is required.
358  */
359 int
360 lookup_inst_by_contract(ctid_t ctid)
361 {
362 	contract_bucket_t *bp;
363 	contract_entry_t *ce;
364 	int id = -1;
365 
366 	bp = contract_hold_bucket(ctid);
367 	ce = contract_lookup(bp, ctid);
368 	if (ce != NULL)
369 		id = ce->ce_instid;
370 	contract_release_bucket(bp);
371 
372 	return (id);
373 }
374