debuggers.hg

view tools/libxl/libxl_bootloader.c @ 22848:6341fe0f4e5a

Added tag 4.1.0-rc2 for changeset 9dca60d88c63
author Keir Fraser <keir@xen.org>
date Tue Jan 25 14:06:55 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 }