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