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 * Copyright 2011 Joyent, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <errno.h> 29 #include <fcntl.h> 30 #include <sys/fork.h> 31 #include <libcontract.h> 32 #include <libzonecfg.h> 33 #include <sys/contract/process.h> 34 #include <sys/ctfs.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <sys/wait.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 42 #include "zdoor-int.h" 43 #include "zerror.h" 44 45 #define ZDOOR_FMT_STR "/var/tmp/.%s" 46 47 48 static int 49 init_template(void) 50 { 51 int fd = 0; 52 int err = 0; 53 54 fd = open64(CTFS_ROOT "/process/template", O_RDWR); 55 if (fd == -1) 56 return (-1); 57 58 err |= ct_tmpl_set_critical(fd, 0); 59 err |= ct_tmpl_set_informative(fd, 0); 60 err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR); 61 err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT); 62 if (err || ct_tmpl_activate(fd)) { 63 (void) close(fd); 64 return (-1); 65 } 66 67 return (fd); 68 } 69 70 static int 71 contract_latest(ctid_t *id) 72 { 73 int cfd = 0; 74 int r = 0; 75 ct_stathdl_t st = {0}; 76 ctid_t result = {0}; 77 78 if ((cfd = open64(CTFS_ROOT "/process/latest", O_RDONLY)) == -1) 79 return (errno); 80 if ((r = ct_status_read(cfd, CTD_COMMON, &st)) != 0) { 81 (void) close(cfd); 82 return (r); 83 } 84 85 result = ct_status_get_id(st); 86 ct_status_free(st); 87 (void) close(cfd); 88 89 *id = result; 90 return (0); 91 } 92 93 static int 94 close_on_exec(int fd) 95 { 96 int flags = fcntl(fd, F_GETFD, 0); 97 if ((flags != -1) && (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1)) 98 return (0); 99 return (-1); 100 } 101 102 static int 103 contract_open(ctid_t ctid, const char *type, const char *file, int oflag) 104 { 105 char path[PATH_MAX]; 106 int n = 0; 107 int fd = 0; 108 109 if (type == NULL) 110 type = "all"; 111 112 n = snprintf(path, PATH_MAX, CTFS_ROOT "/%s/%ld/%s", type, ctid, file); 113 if (n >= sizeof (path)) { 114 errno = ENAMETOOLONG; 115 return (-1); 116 } 117 118 fd = open64(path, oflag); 119 if (fd != -1) { 120 if (close_on_exec(fd) == -1) { 121 int err = errno; 122 (void) close(fd); 123 errno = err; 124 return (-1); 125 } 126 } 127 return (fd); 128 } 129 130 static int 131 contract_abandon_id(ctid_t ctid) 132 { 133 int fd = 0; 134 int err = 0; 135 136 fd = contract_open(ctid, "all", "ctl", O_WRONLY); 137 if (fd == -1) 138 return (errno); 139 140 err = ct_ctl_abandon(fd); 141 (void) close(fd); 142 143 return (err); 144 } 145 146 /* 147 * zdoor_fattach(zone,service,door,detach_only) is heavily borrowed from 148 * zonestatd. Basically this forks, zone_enter's the targeted zone, 149 * fattaches to /var/tmp/.<service> with the door you've opened. 150 * detach_only gets passed in on door_stop to fdetach in the targeted zone. 151 * Note that this code really does require all the contract calls, which are 152 * all the static functions preceding this (have a look at zone_enter; without 153 * that code zone_enter will kick back EINVAL). 154 */ 155 int 156 zdoor_fattach(zoneid_t zoneid, const char *service, int door, int detach_only) 157 { 158 int fd = 0; 159 int len = 0; 160 int pid = 0; 161 int stat = 0; 162 int tmpl_fd = 0; 163 char path[MAXPATHLEN] = {0}; 164 ctid_t ct = -1; 165 166 if (zoneid < 0) { 167 zdoor_debug("zdoor_fattach: zoneid < 0"); 168 return (ZDOOR_ARGS_ERROR); 169 } 170 171 if (service == NULL) { 172 zdoor_debug("zdoor_fattach: NULL service"); 173 return (ZDOOR_ARGS_ERROR); 174 } 175 176 if ((tmpl_fd = init_template()) < 0) { 177 zdoor_warn("zdoor_fattach: init contract for %d:%s failed", 178 zoneid, service); 179 return (ZDOOR_ERROR); 180 } 181 182 len = snprintf(NULL, 0, ZDOOR_FMT_STR, service) + 1; 183 if (len > MAXPATHLEN) 184 return (ZDOOR_ARGS_ERROR); 185 (void) snprintf(path, len, ZDOOR_FMT_STR, service); 186 187 zdoor_info("zdoor_fattach: ensuring %s", path); 188 189 pid = fork(); 190 if (pid < 0) { 191 (void) ct_tmpl_clear(tmpl_fd); 192 zdoor_error("zdoor_fattach: unable to fork for zone_enter: %s", 193 strerror(errno)); 194 return (ZDOOR_OK); 195 } 196 197 if (pid == 0) { 198 zdoor_debug("zdoor_fattach(CHILD): starting"); 199 (void) ct_tmpl_clear(tmpl_fd); 200 (void) close(tmpl_fd); 201 if (zone_enter(zoneid) != 0) { 202 zdoor_debug("zdoor_fattach(CHILD): zone_enter fail %s", 203 strerror(errno)); 204 if (errno == EINVAL) { 205 _exit(0); 206 } 207 _exit(1); 208 } 209 (void) fdetach(path); 210 (void) unlink(path); 211 if (detach_only) { 212 zdoor_debug("zdoor_fattach(CHILD): detach only, done"); 213 _exit(0); 214 } 215 fd = open(path, O_CREAT|O_RDWR, 0644); 216 if (fd < 0) { 217 zdoor_debug("zdoor_fattach(CHILD): open failed: %s", 218 strerror(errno)); 219 _exit(2); 220 } 221 if (fattach(door, path) != 0) { 222 zdoor_debug("zdoor_fattach(CHILD): fattach failed: %s", 223 strerror(errno)); 224 _exit(3); 225 } 226 _exit(0); 227 } 228 if (contract_latest(&ct) == -1) 229 ct = -1; 230 (void) ct_tmpl_clear(tmpl_fd); 231 (void) close(tmpl_fd); 232 (void) contract_abandon_id(ct); 233 234 zdoor_debug("zdoor_fattach: waiting for child..."); 235 while (waitpid(pid, &stat, 0) != pid) 236 ; 237 if (WIFEXITED(stat) && WEXITSTATUS(stat) == 0) { 238 zdoor_debug(" child exited with success"); 239 zdoor_debug("zdoor_fattach: returning ZDOOR_OK"); 240 return (ZDOOR_OK); 241 } 242 243 zdoor_debug(" child exited with %d", WEXITSTATUS(stat)); 244 zdoor_debug("zdoor_fattach: returning ZDOOR_ERROR"); 245 return (ZDOOR_ERROR); 246 } 247 248 /* 249 * zdoor_zone_is_running(zone) returns 1 if the specified zone is running, or 0 250 * if it is any other state. It additionally eats any other errors it 251 * encounters and returns 0 upon encountering them. 252 */ 253 boolean_t 254 zdoor_zone_is_running(zoneid_t zoneid) 255 { 256 zone_state_t state; 257 char zone[ZONENAME_MAX]; 258 if (zoneid < 0) 259 return (B_FALSE); 260 261 if (getzonenamebyid(zoneid, zone, ZONENAME_MAX) < 0) 262 return (B_FALSE); 263 264 if (!zone_get_state((char *)zone, &state) == Z_OK) 265 return (B_FALSE); 266 267 return (state == ZONE_STATE_RUNNING); 268 } 269 270 /* 271 * zdoor_cookie_create simply allocates and initializes 272 * memory. Returns NULL on any error. 273 */ 274 zdoor_cookie_t * 275 zdoor_cookie_create(const char *zonename, const char *service, 276 const void *biscuit) 277 { 278 zdoor_cookie_t *cookie = NULL; 279 280 if (zonename == NULL || service == NULL) 281 return (NULL); 282 283 cookie = (zdoor_cookie_t *)calloc(1, sizeof (zdoor_cookie_t)); 284 if (cookie == NULL) { 285 OUT_OF_MEMORY(); 286 return (NULL); 287 } 288 cookie->zdc_biscuit = (void *)biscuit; 289 cookie->zdc_zonename = strdup((char *)zonename); 290 if (cookie->zdc_zonename == NULL) { 291 zdoor_cookie_free(cookie); 292 OUT_OF_MEMORY(); 293 return (NULL); 294 } 295 cookie->zdc_service = strdup((char *)service); 296 if (cookie->zdc_service == NULL) { 297 zdoor_cookie_free(cookie); 298 OUT_OF_MEMORY(); 299 return (NULL); 300 } 301 302 return (cookie); 303 } 304 305 /* 306 * zdoor_cookie_free(cookie) cleans up any memory associated with the 307 * specified cookie. 308 */ 309 void 310 zdoor_cookie_free(zdoor_cookie_t *cookie) 311 { 312 if (cookie == NULL) 313 return; 314 315 if (cookie->zdc_zonename != NULL) { 316 free(cookie->zdc_zonename); 317 cookie->zdc_zonename = NULL; 318 } 319 320 if (cookie->zdc_service != NULL) { 321 free(cookie->zdc_service); 322 cookie->zdc_service = NULL; 323 } 324 325 free(cookie); 326 }