Home | History | Annotate | Download | only in startd
      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