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 (path == NULL) {
172 zdoor_debug("zdoor_fattach: NULL PATH");
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 }