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 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 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 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 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 * 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 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 * 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 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 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 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 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 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