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