debuggers.hg

view tools/libxl/libxl_bootloader.c @ 22855:1d1eec7e1fb4

xl: Perform minimal validation of virtual disk file while parsing config file

This patch performs some very basic validation on the virtual disk
file passed through the config file. This validation ensures that we
don't go too far with the initialization like spawn qemu and more
while there could be some potentially fundamental issues.

[ Patch fixed up to work with PHYSTYPE_EMPTY 22808:6ec61438713a -iwj ]

Signed-off-by: Kamala Narasimhan <kamala.narasimhan@citrix.com>
Acked-by: Ian Jackson <ian.jackson@eu.citrix.com>
Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com>
Committed-by: Ian Jackson <ian.jackson@eu.citrix.com>
author Kamala Narasimhan <kamala.narasimhan@gmail.com>
date Tue Jan 25 18:09:49 2011 +0000 (2011-01-25)
parents 83a97418d9cd
children
line source
1 /*
2 * Copyright (C) 2010 Citrix Ltd.
3 * Author Ian Campbell <ian.campbell@citrix.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published
7 * by the Free Software Foundation; version 2.1 only.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 */
15 #include "libxl_osdeps.h"
17 #include <string.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <termios.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
25 #include "libxl.h"
26 #include "libxl_internal.h"
28 #include "flexarray.h"
30 #define XENCONSOLED_BUF_SIZE 16
31 #define BOOTLOADER_BUF_SIZE 1024
33 static char **make_bootloader_args(libxl__gc *gc,
34 libxl_domain_build_info *info,
35 uint32_t domid,
36 const char *fifo, char *disk)
37 {
38 flexarray_t *args;
39 int nr = 0;
41 args = flexarray_make(1, 1);
42 if (!args)
43 return NULL;
45 flexarray_set(args, nr++, (char *)info->u.pv.bootloader);
47 if (info->kernel.path)
48 flexarray_set(args, nr++, libxl__sprintf(gc, "--kernel=%s", info->kernel.path));
49 if (info->u.pv.ramdisk.path)
50 flexarray_set(args, nr++, libxl__sprintf(gc, "--ramdisk=%s", info->u.pv.ramdisk.path));
51 if (info->u.pv.cmdline && *info->u.pv.cmdline != '\0')
52 flexarray_set(args, nr++, libxl__sprintf(gc, "--args=%s", info->u.pv.cmdline));
54 flexarray_set(args, nr++, libxl__sprintf(gc, "--output=%s", fifo));
55 flexarray_set(args, nr++, "--output-format=simple0");
56 flexarray_set(args, nr++, libxl__sprintf(gc, "--output-directory=%s", "/var/run/libxl/"));
58 if (info->u.pv.bootloader_args) {
59 char *saveptr;
60 /* Operate on a duplicate since strtok modifes the argument */
61 char *dup = libxl__strdup(gc, info->u.pv.bootloader_args);
62 char *t = strtok_r(dup, " \t\n", &saveptr);
63 do {
64 flexarray_set(args, nr++, t);
65 } while ((t = strtok_r(NULL, " \t\n", &saveptr)));
66 }
68 flexarray_set(args, nr++, disk);
70 /* Sentinal for execv */
71 flexarray_set(args, nr++, NULL);
73 return (char **) flexarray_contents(args); /* Frees args */
74 }
76 static int open_xenconsoled_pty(int *master, int *slave, char *slave_path, size_t slave_path_len)
77 {
78 struct termios termattr;
79 int ret;
81 ret = openpty(master, slave, NULL, NULL, NULL);
82 if (ret < 0)
83 return -1;
85 ret = ttyname_r(*slave, slave_path, slave_path_len);
86 if (ret == -1) {
87 close(*master);
88 close(*slave);
89 *master = *slave = -1;
90 return -1;
91 }
93 /*
94 * On Solaris, the pty master side will get cranky if we try
95 * to write to it while there is no slave. To work around this,
96 * keep the slave descriptor open until we're done. Set it
97 * to raw terminal parameters, otherwise it will echo back
98 * characters, which will confuse the I/O loop below.
99 * Furthermore, a raw master pty device has no terminal
100 * semantics on Solaris, so don't try to set any attributes
101 * for it.
102 */
103 #if !defined(__sun__) && !defined(__NetBSD__)
104 tcgetattr(*master, &termattr);
105 cfmakeraw(&termattr);
106 tcsetattr(*master, TCSANOW, &termattr);
108 close(*slave);
109 *slave = -1;
110 #else
111 tcgetattr(*slave, &termattr);
112 cfmakeraw(&termattr);
113 tcsetattr(*slave, TCSANOW, &termattr);
114 #endif
116 fcntl(*master, F_SETFL, O_NDELAY);
118 return 0;
119 }
121 static pid_t fork_exec_bootloader(int *master, const char *arg0, char **args)
122 {
123 struct termios termattr;
124 pid_t pid = forkpty(master, NULL, NULL, NULL);
125 if (pid == -1)
126 return -1;
127 else if (pid == 0) {
128 setenv("TERM", "vt100", 1);
129 libxl__exec(-1, -1, -1, arg0, args);
130 return -1;
131 }
133 /*
134 * On Solaris, the master pty side does not have terminal semantics,
135 * so don't try to set any attributes, as it will fail.
136 */
137 #if !defined(__sun__)
138 tcgetattr(*master, &termattr);
139 cfmakeraw(&termattr);
140 tcsetattr(*master, TCSANOW, &termattr);
141 #endif
143 fcntl(*master, F_SETFL, O_NDELAY);
145 return pid;
146 }
148 /*
149 * filedescriptors:
150 * fifo_fd - bootstring output from the bootloader
151 * xenconsoled_fd - input/output from/to xenconsole
152 * bootloader_fd - input/output from/to pty that controls the bootloader
153 * The filedescriptors are NDELAY, so it's ok to try to read
154 * bigger chunks than may be available, to keep e.g. curses
155 * screen redraws in the bootloader efficient. xenconsoled_fd is the side that
156 * gets xenconsole input, which will be keystrokes, so a small number
157 * is sufficient. bootloader_fd is pygrub output, which will be curses screen
158 * updates, so a larger number (1024) is appropriate there.
159 *
160 * For writeable descriptors, only include them in the set for select
161 * if there is actual data to write, otherwise this would loop too fast,
162 * eating up CPU time.
163 */
164 static char * bootloader_interact(libxl__gc *gc, int xenconsoled_fd, int bootloader_fd, int fifo_fd)
165 {
166 int ret;
168 size_t nr_out = 0, size_out = 0;
169 char *output = NULL;
171 /* input from xenconsole. read on xenconsoled_fd write to bootloader_fd */
172 int xenconsoled_prod = 0, xenconsoled_cons = 0;
173 char xenconsoled_buf[XENCONSOLED_BUF_SIZE];
174 /* output from bootloader. read on bootloader_fd write to xenconsoled_fd */
175 int bootloader_prod = 0, bootloader_cons = 0;
176 char bootloader_buf[BOOTLOADER_BUF_SIZE];
178 while(1) {
179 fd_set wsel, rsel;
180 int nfds;
182 if (xenconsoled_prod == xenconsoled_cons)
183 xenconsoled_prod = xenconsoled_cons = 0;
184 if (bootloader_prod == bootloader_cons)
185 bootloader_prod = bootloader_cons = 0;
187 FD_ZERO(&rsel);
188 FD_SET(fifo_fd, &rsel);
189 nfds = fifo_fd + 1;
190 if (xenconsoled_prod == 0 || (xenconsoled_prod < BOOTLOADER_BUF_SIZE && xenconsoled_cons == 0)) {
191 FD_SET(xenconsoled_fd, &rsel);
192 nfds = xenconsoled_fd + 1 > nfds ? xenconsoled_fd + 1 : nfds;
193 }
194 if (bootloader_prod == 0 || (bootloader_prod < BOOTLOADER_BUF_SIZE && bootloader_cons == 0)) {
195 FD_SET(bootloader_fd, &rsel);
196 nfds = bootloader_fd + 1 > nfds ? bootloader_fd + 1 : nfds;
197 }
199 FD_ZERO(&wsel);
200 if (bootloader_prod != bootloader_cons) {
201 FD_SET(xenconsoled_fd, &wsel);
202 nfds = xenconsoled_fd + 1 > nfds ? xenconsoled_fd + 1 : nfds;
203 }
204 if (xenconsoled_prod != xenconsoled_cons) {
205 FD_SET(bootloader_fd, &wsel);
206 nfds = bootloader_fd + 1 > nfds ? bootloader_fd + 1 : nfds;
207 }
209 ret = select(nfds, &rsel, &wsel, NULL, NULL);
210 if (ret < 0)
211 goto out_err;
213 /* Input from xenconsole, read xenconsoled_fd, write bootloader_fd */
214 if (FD_ISSET(xenconsoled_fd, &rsel)) {
215 ret = read(xenconsoled_fd, &xenconsoled_buf[xenconsoled_prod], XENCONSOLED_BUF_SIZE - xenconsoled_prod);
216 if (ret < 0 && errno != EIO && errno != EAGAIN)
217 goto out_err;
218 if (ret > 0)
219 xenconsoled_prod += ret;
220 }
221 if (FD_ISSET(bootloader_fd, &wsel)) {
222 ret = write(bootloader_fd, &xenconsoled_buf[xenconsoled_cons], xenconsoled_prod - xenconsoled_cons);
223 if (ret < 0 && errno != EIO && errno != EAGAIN)
224 goto out_err;
225 if (ret > 0)
226 xenconsoled_cons += ret;
227 }
229 /* Input from bootloader, read bootloader_fd, write xenconsoled_fd */
230 if (FD_ISSET(bootloader_fd, &rsel)) {
231 ret = read(bootloader_fd, &bootloader_buf[bootloader_prod], BOOTLOADER_BUF_SIZE - bootloader_prod);
232 if (ret < 0 && errno != EIO && errno != EAGAIN)
233 goto out_err;
234 if (ret > 0)
235 bootloader_prod += ret;
236 }
237 if (FD_ISSET(xenconsoled_fd, &wsel)) {
238 ret = write(xenconsoled_fd, &bootloader_buf[bootloader_cons], bootloader_prod - bootloader_cons);
239 if (ret < 0 && errno != EIO && errno != EAGAIN)
240 goto out_err;
241 if (ret > 0)
242 bootloader_cons += ret;
243 }
245 if (FD_ISSET(fifo_fd, &rsel)) {
246 if (size_out - nr_out < 256) {
247 char *temp;
248 size_t new_size = size_out == 0 ? 32 : size_out * 2;
250 temp = realloc(output, new_size);
251 if (temp == NULL)
252 goto out_err;
253 output = temp;
254 memset(output + size_out, 0, new_size - size_out);
255 size_out = new_size;
256 }
258 ret = read(fifo_fd, output + nr_out, size_out - nr_out);
259 if (ret > 0)
260 nr_out += ret;
261 if (ret == 0)
262 break;
263 }
264 }
266 libxl__ptr_add(gc, output);
267 return output;
269 out_err:
270 free(output);
271 return NULL;
272 }
274 static void parse_bootloader_result(libxl_ctx *ctx,
275 libxl_domain_build_info *info,
276 const char *o)
277 {
278 while (*o != '\0') {
279 if (strncmp("kernel ", o, strlen("kernel ")) == 0) {
280 free(info->kernel.path);
281 info->kernel.path = strdup(o + strlen("kernel "));
282 libxl__file_reference_map(&info->kernel);
283 unlink(info->kernel.path);
284 } else if (strncmp("ramdisk ", o, strlen("ramdisk ")) == 0) {
285 free(info->u.pv.ramdisk.path);
286 info->u.pv.ramdisk.path = strdup(o + strlen("ramdisk "));
287 libxl__file_reference_map(&info->u.pv.ramdisk);
288 unlink(info->u.pv.ramdisk.path);
289 } else if (strncmp("args ", o, strlen("args ")) == 0) {
290 free(info->u.pv.cmdline);
291 info->u.pv.cmdline = strdup(o + strlen("args "));
292 }
294 o = o + strlen(o) + 1;
295 }
296 }
298 int libxl_run_bootloader(libxl_ctx *ctx,
299 libxl_domain_build_info *info,
300 libxl_device_disk *disk,
301 uint32_t domid)
302 {
303 libxl__gc gc = LIBXL_INIT_GC(ctx);
304 int ret, rc = 0;
305 char *fifo = NULL;
306 char *diskpath = NULL;
307 char **args = NULL;
309 char tempdir_template[] = "/var/run/libxl/bl.XXXXXX";
310 char *tempdir;
312 char *dom_console_xs_path;
313 char dom_console_slave_tty_path[PATH_MAX];
315 int xenconsoled_fd = -1, xenconsoled_slave = -1;
316 int bootloader_fd = -1, fifo_fd = -1;
318 int blrc;
319 pid_t pid;
320 char *blout;
322 struct stat st_buf;
324 if (info->hvm || !info->u.pv.bootloader)
325 goto out;
327 rc = ERROR_INVAL;
328 if (!disk)
329 goto out;
331 rc = ERROR_FAIL;
332 ret = mkdir("/var/run/libxl/", S_IRWXU);
333 if (ret < 0 && errno != EEXIST)
334 goto out;
336 ret = stat("/var/run/libxl/", &st_buf);
337 if (ret < 0)
338 goto out;
340 if (!S_ISDIR(st_buf.st_mode))
341 goto out;
343 tempdir = mkdtemp(tempdir_template);
344 if (tempdir == NULL)
345 goto out;
347 ret = asprintf(&fifo, "%s/fifo", tempdir);
348 if (ret < 0) {
349 fifo = NULL;
350 goto out_close;
351 }
353 ret = mkfifo(fifo, 0600);
354 if (ret < 0) {
355 goto out_close;
356 }
358 diskpath = libxl_device_disk_local_attach(ctx, disk);
359 if (!diskpath) {
360 goto out_close;
361 }
363 args = make_bootloader_args(&gc, info, domid, fifo, diskpath);
364 if (args == NULL) {
365 rc = ERROR_NOMEM;
366 goto out_close;
367 }
369 /*
370 * We need to present the bootloader's tty as a pty slave that xenconsole
371 * can access. Since the bootloader itself needs a pty slave,
372 * we end up with a connection like this:
373 *
374 * xenconsole -- (slave pty1 master) <-> (master pty2 slave) -- bootloader
375 *
376 * where we copy characters between the two master fds, as well as
377 * listening on the bootloader's fifo for the results.
378 */
379 ret = open_xenconsoled_pty(&xenconsoled_fd, &xenconsoled_slave,
380 &dom_console_slave_tty_path[0],
381 sizeof(dom_console_slave_tty_path));
382 if (ret < 0) {
383 goto out_close;
384 }
386 dom_console_xs_path = libxl__sprintf(&gc, "%s/console/tty", libxl__xs_get_dompath(&gc, domid));
387 libxl__xs_write(&gc, XBT_NULL, dom_console_xs_path, "%s", dom_console_slave_tty_path);
389 pid = fork_exec_bootloader(&bootloader_fd, info->u.pv.bootloader, args);
390 if (pid < 0) {
391 goto out_close;
392 }
394 while (1) {
395 fifo_fd = open(fifo, O_RDONLY);
396 if (fifo_fd > -1)
397 break;
399 if (errno == EINTR)
400 continue;
402 goto out_close;
403 }
405 fcntl(fifo_fd, F_SETFL, O_NDELAY);
407 blout = bootloader_interact(&gc, xenconsoled_fd, bootloader_fd, fifo_fd);
408 if (blout == NULL) {
409 goto out_close;
410 }
412 pid = waitpid(pid, &blrc, 0);
413 if (pid == -1 || (pid > 0 && WIFEXITED(blrc) && WEXITSTATUS(blrc) != 0)) {
414 goto out_close;
415 }
417 parse_bootloader_result(ctx, info, blout);
419 rc = 0;
420 out_close:
421 if (diskpath) {
422 libxl_device_disk_local_detach(ctx, disk);
423 free(diskpath);
424 }
425 if (fifo_fd > -1)
426 close(fifo_fd);
427 if (bootloader_fd > -1)
428 close(bootloader_fd);
429 if (xenconsoled_fd > -1)
430 close(xenconsoled_fd);
431 if (xenconsoled_slave > -1)
432 close(xenconsoled_slave);
434 if (fifo) {
435 unlink(fifo);
436 free(fifo);
437 }
439 rmdir(tempdir);
441 free(args);
443 out:
444 libxl__free_all(&gc);
445 return rc;
446 }