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 */
14
15 #include "libxl_osdeps.h" /* must come before any other headers */
16
17 #include <pwd.h>
18 #include <termios.h>
19 #ifdef HAVE_UTMP_H
20 #include <utmp.h>
21 #endif
22
23 #include "libxl_internal.h"
24
25 #define BOOTLOADER_BUF_OUT 65536
26 #define BOOTLOADER_BUF_IN 4096
27
28 static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op);
29 static void bootloader_keystrokes_copyfail(libxl__egc *egc,
30 libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
31 static void bootloader_display_copyfail(libxl__egc *egc,
32 libxl__datacopier_state *dc, int rc, int onwrite, int errnoval);
33 static void bootloader_timeout(libxl__egc *egc, libxl__ev_time *ev,
34 const struct timeval *requested_abs, int rc);
35 static void bootloader_domaindeath(libxl__egc*, libxl__domaindeathcheck *dc,
36 int rc);
37 static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child,
38 pid_t pid, int status);
39
40 /*----- bootloader arguments -----*/
41
bootloader_arg(libxl__bootloader_state * bl,const char * arg)42 static void bootloader_arg(libxl__bootloader_state *bl, const char *arg)
43 {
44 assert(bl->nargs < bl->argsspace);
45 bl->args[bl->nargs++] = arg;
46 }
47
bootloader_uid(libxl__gc * gc,domid_t guest_domid,const char * user,uid_t * intended_uid)48 static int bootloader_uid(libxl__gc *gc, domid_t guest_domid,
49 const char *user, uid_t *intended_uid)
50 {
51 struct passwd *user_base, user_pwbuf;
52 int rc;
53
54 if (user) {
55 rc = userlookup_helper_getpwnam(gc, user, &user_pwbuf, &user_base);
56 if (rc) return rc;
57
58 if (!user_base) {
59 LOGD(ERROR, guest_domid, "Couldn't find user %s", user);
60 return ERROR_INVAL;
61 }
62
63 *intended_uid = user_base->pw_uid;
64 return 0;
65 }
66
67 /* Re-use QEMU user range for the bootloader. */
68 rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_RANGE_BASE,
69 &user_pwbuf, &user_base);
70 if (rc) return rc;
71
72 if (user_base) {
73 struct passwd *user_clash, user_clash_pwbuf;
74 uid_t temp_uid = user_base->pw_uid + guest_domid;
75
76 rc = userlookup_helper_getpwuid(gc, temp_uid, &user_clash_pwbuf,
77 &user_clash);
78 if (rc) return rc;
79
80 if (user_clash) {
81 LOGD(ERROR, guest_domid,
82 "wanted to use uid %ld (%s + %d) but that is user %s !",
83 (long)temp_uid, LIBXL_QEMU_USER_RANGE_BASE,
84 guest_domid, user_clash->pw_name);
85 return ERROR_INVAL;
86 }
87
88 *intended_uid = temp_uid;
89 return 0;
90 }
91
92 rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_SHARED, &user_pwbuf,
93 &user_base);
94 if (rc) return rc;
95
96 if (user_base) {
97 LOGD(WARN, guest_domid, "Could not find user %s, falling back to %s",
98 LIBXL_QEMU_USER_RANGE_BASE, LIBXL_QEMU_USER_SHARED);
99 *intended_uid = user_base->pw_uid;
100
101 return 0;
102 }
103
104 LOGD(ERROR, guest_domid,
105 "Could not find user %s or range base pseudo-user %s, cannot restrict",
106 LIBXL_QEMU_USER_SHARED, LIBXL_QEMU_USER_RANGE_BASE);
107
108 return ERROR_INVAL;
109 }
110
make_bootloader_args(libxl__gc * gc,libxl__bootloader_state * bl,const char * bootloader_path)111 static int make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl,
112 const char *bootloader_path)
113 {
114 const libxl_domain_build_info *info = bl->info;
115
116 bl->argsspace = 9 + libxl_string_list_length(&info->bootloader_args);
117
118 GCNEW_ARRAY(bl->args, bl->argsspace);
119
120 #define ARG(arg) bootloader_arg(bl, (arg))
121
122 ARG(bootloader_path);
123
124 if (info->kernel)
125 ARG(GCSPRINTF("--kernel=%s", info->kernel));
126 if (info->ramdisk)
127 ARG(GCSPRINTF("--ramdisk=%s", info->ramdisk));
128 if (info->cmdline && *info->cmdline != '\0')
129 ARG(GCSPRINTF("--args=%s", info->cmdline));
130 if (libxl_defbool_val(info->bootloader_restrict)) {
131 uid_t uid = -1;
132 int rc = bootloader_uid(gc, bl->domid, info->bootloader_user,
133 &uid);
134
135 if (rc) return rc;
136
137 assert(uid != -1);
138 if (!uid) {
139 LOGD(ERROR, bl->domid, "bootloader restrict UID is 0 (root)!");
140 return ERROR_INVAL;
141 }
142 LOGD(DEBUG, bl->domid, "using uid %ld", (long)uid);
143 ARG(GCSPRINTF("--runas=%ld", (long)uid));
144 ARG("--quiet");
145 }
146
147 ARG(GCSPRINTF("--output=%s", bl->outputpath));
148 ARG("--output-format=simple0");
149 ARG(GCSPRINTF("--output-directory=%s", bl->outputdir));
150
151 if (info->bootloader_args) {
152 char **p = info->bootloader_args;
153 while (*p) {
154 ARG(*p);
155 p++;
156 }
157 }
158
159 ARG(bl->dls.diskpath);
160
161 /* Sentinel for execv */
162 ARG(NULL);
163
164 return 0;
165 #undef ARG
166 }
167
168 /*----- synchronous subroutines -----*/
169
setup_xenconsoled_pty(libxl__egc * egc,libxl__bootloader_state * bl,char * slave_path,size_t slave_path_len)170 static int setup_xenconsoled_pty(libxl__egc *egc, libxl__bootloader_state *bl,
171 char *slave_path, size_t slave_path_len)
172 {
173 STATE_AO_GC(bl->ao);
174 struct termios termattr;
175 int r, rc;
176 int slave = libxl__carefd_fd(bl->ptys[1].slave);
177 int master = libxl__carefd_fd(bl->ptys[1].master);
178
179 r = ttyname_r(slave, slave_path, slave_path_len);
180 if (r == -1) {
181 LOGED(ERROR, bl->domid, "ttyname_r failed");
182 rc = ERROR_FAIL;
183 goto out;
184 }
185
186 /*
187 * On Solaris, the pty master side will get cranky if we try
188 * to write to it while there is no slave. To work around this,
189 * keep the slave descriptor open until we're done. Set it
190 * to raw terminal parameters, otherwise it will echo back
191 * characters, which will confuse the I/O loop below.
192 * Furthermore, a raw master pty device has no terminal
193 * semantics on Solaris, so don't try to set any attributes
194 * for it.
195 */
196 tcgetattr(master, &termattr);
197 cfmakeraw(&termattr);
198 tcsetattr(master, TCSANOW, &termattr);
199
200 return 0;
201
202 out:
203 return rc;
204 }
205
bootloader_result_command(libxl__gc * gc,const char * buf,const char * prefix,size_t prefixlen,uint32_t domid)206 static const char *bootloader_result_command(libxl__gc *gc, const char *buf,
207 const char *prefix, size_t prefixlen, uint32_t domid) {
208 if (strncmp(buf, prefix, prefixlen))
209 return 0;
210
211 const char *rhs = buf + prefixlen;
212 if (!CTYPE(isspace,*rhs))
213 return 0;
214
215 while (CTYPE(isspace,*rhs))
216 rhs++;
217
218 LOGD(DEBUG, domid, "bootloader output contained %s %s", prefix, rhs);
219
220 return rhs;
221 }
222
parse_bootloader_result(libxl__egc * egc,libxl__bootloader_state * bl)223 static int parse_bootloader_result(libxl__egc *egc,
224 libxl__bootloader_state *bl)
225 {
226 STATE_AO_GC(bl->ao);
227 char buf[PATH_MAX*2];
228 FILE *f = 0;
229 int rc = ERROR_FAIL;
230
231 f = fopen(bl->outputpath, "r");
232 if (!f) {
233 LOGED(ERROR, bl->domid, "open bootloader output file %s",
234 bl->outputpath);
235 goto out;
236 }
237
238 for (;;) {
239 /* Read a nul-terminated "line" and put the result in
240 * buf, and its length (not including the nul) in l */
241 int l = 0, c;
242 while ((c = getc(f)) != EOF && c != '\0') {
243 if (l < sizeof(buf)-1)
244 buf[l] = c;
245 l++;
246 }
247 if (c == EOF) {
248 if (ferror(f)) {
249 LOGED(ERROR, bl->domid, "read bootloader output file %s",
250 bl->outputpath);
251 goto out;
252 }
253 if (!l)
254 break;
255 }
256 if (l >= sizeof(buf)) {
257 LOGD(WARN, bl->domid, "bootloader output contained"
258 " overly long item `%.150s...'", buf);
259 continue;
260 }
261 buf[l] = 0;
262
263 const char *rhs;
264 #define COMMAND(s) ((rhs = bootloader_result_command(gc, buf, s, sizeof(s)-1, bl->domid)))
265
266 if (COMMAND("kernel")) {
267 bl->kernel->path = libxl__strdup(gc, rhs);
268 libxl__file_reference_map(bl->kernel);
269 unlink(bl->kernel->path);
270 } else if (COMMAND("ramdisk")) {
271 bl->ramdisk->path = libxl__strdup(gc, rhs);
272 libxl__file_reference_map(bl->ramdisk);
273 unlink(bl->ramdisk->path);
274 } else if (COMMAND("args")) {
275 bl->cmdline = libxl__strdup(gc, rhs);
276 } else if (l) {
277 LOGD(WARN, bl->domid,
278 "unexpected output from bootloader: `%s'", buf);
279 }
280 }
281 rc = 0;
282
283 out:
284 if (f) fclose(f);
285 return rc;
286 }
287
288
289 /*----- init and cleanup -----*/
290
libxl__bootloader_init(libxl__bootloader_state * bl)291 void libxl__bootloader_init(libxl__bootloader_state *bl)
292 {
293 assert(bl->ao);
294 bl->rc = 0;
295 bl->dls.diskpath = NULL;
296 bl->openpty.ao = bl->ao;
297 bl->dls.ao = bl->ao;
298 bl->ptys[0].master = bl->ptys[0].slave = 0;
299 bl->ptys[1].master = bl->ptys[1].slave = 0;
300 libxl__ev_child_init(&bl->child);
301 libxl__ev_time_init(&bl->time);
302 libxl__domaindeathcheck_init(&bl->deathcheck);
303 bl->keystrokes.ao = bl->ao; libxl__datacopier_init(&bl->keystrokes);
304 bl->display.ao = bl->ao; libxl__datacopier_init(&bl->display);
305 bl->got_pollhup = 0;
306 }
307
bootloader_cleanup(libxl__egc * egc,libxl__bootloader_state * bl)308 static void bootloader_cleanup(libxl__egc *egc, libxl__bootloader_state *bl)
309 {
310 STATE_AO_GC(bl->ao);
311 int i;
312
313 if (bl->outputpath) libxl__remove_file(gc, bl->outputpath);
314 if (bl->outputdir) libxl__remove_directory(gc, bl->outputdir);
315
316 libxl__domaindeathcheck_stop(gc,&bl->deathcheck);
317 libxl__datacopier_kill(&bl->keystrokes);
318 libxl__datacopier_kill(&bl->display);
319 libxl__ev_time_deregister(gc, &bl->time);
320 for (i=0; i<2; i++) {
321 libxl__carefd_close(bl->ptys[i].master);
322 libxl__carefd_close(bl->ptys[i].slave);
323 }
324 if (bl->display.log) {
325 fclose(bl->display.log);
326 bl->display.log = NULL;
327 }
328 }
329
bootloader_setpaths(libxl__gc * gc,libxl__bootloader_state * bl)330 static void bootloader_setpaths(libxl__gc *gc, libxl__bootloader_state *bl)
331 {
332 uint32_t domid = bl->domid;
333 bl->outputdir = GCSPRINTF(XEN_LIB_DIR "/bootloader.%"PRIu32".d", domid);
334 bl->outputpath = GCSPRINTF(XEN_LIB_DIR "/bootloader.%"PRIu32".out", domid);
335 }
336
337 /* Callbacks */
338
339 static void bootloader_local_detached_cb(libxl__egc *egc,
340 libxl__disk_local_state *dls,
341 int rc);
342
bootloader_callback(libxl__egc * egc,libxl__bootloader_state * bl,int rc)343 static void bootloader_callback(libxl__egc *egc, libxl__bootloader_state *bl,
344 int rc)
345 {
346 if (!bl->rc)
347 bl->rc = rc;
348
349 bootloader_cleanup(egc, bl);
350
351 bl->dls.callback = bootloader_local_detached_cb;
352 libxl__device_disk_local_initiate_detach(egc, &bl->dls);
353 }
354
bootloader_local_detached_cb(libxl__egc * egc,libxl__disk_local_state * dls,int rc)355 static void bootloader_local_detached_cb(libxl__egc *egc,
356 libxl__disk_local_state *dls,
357 int rc)
358 {
359 STATE_AO_GC(dls->ao);
360 libxl__bootloader_state *bl = CONTAINER_OF(dls, *bl, dls);
361
362 if (rc) {
363 LOGD(ERROR, bl->domid,
364 "unable to detach locally attached disk");
365 if (!bl->rc)
366 bl->rc = rc;
367 }
368
369 bl->callback(egc, bl, bl->rc);
370 }
371
372 /* might be called at any time, provided it's init'd */
bootloader_stop(libxl__egc * egc,libxl__bootloader_state * bl,int rc)373 static void bootloader_stop(libxl__egc *egc,
374 libxl__bootloader_state *bl, int rc)
375 {
376 STATE_AO_GC(bl->ao);
377 int r;
378
379 libxl__datacopier_kill(&bl->keystrokes);
380 libxl__datacopier_kill(&bl->display);
381 libxl__ev_time_deregister(gc, &bl->time);
382 if (libxl__ev_child_inuse(&bl->child)) {
383 r = kill(bl->child.pid, SIGTERM);
384 if (r) LOGED(WARN, bl->domid, "%sfailed to kill bootloader [%lu]",
385 rc ? "after failure, " : "", (unsigned long)bl->child.pid);
386 }
387 if (!bl->rc)
388 bl->rc = rc;
389 }
390
391 /*----- main flow of control -----*/
392
393 /* Callbacks */
394
395 static void bootloader_disk_attached_cb(libxl__egc *egc,
396 libxl__disk_local_state *dls,
397 int rc);
398
libxl__bootloader_run(libxl__egc * egc,libxl__bootloader_state * bl)399 void libxl__bootloader_run(libxl__egc *egc, libxl__bootloader_state *bl)
400 {
401 STATE_AO_GC(bl->ao);
402 const libxl_domain_build_info *info = bl->info;
403 uint32_t domid = bl->domid;
404 char *logfile_tmp = NULL;
405 int rc, r;
406
407 libxl__bootloader_init(bl);
408
409 if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
410 LOGD(DEBUG, domid, "not a PV/PVH domain, skipping bootloader");
411 rc = 0;
412 goto out_ok;
413 }
414
415 if (!info->bootloader) {
416 LOGD(DEBUG, domid,
417 "no bootloader configured, using user supplied kernel");
418 bl->kernel->path = bl->info->kernel;
419 bl->ramdisk->path = bl->info->ramdisk;
420 bl->cmdline = bl->info->cmdline;
421 rc = 0;
422 goto out_ok;
423 }
424
425 if (!bl->disk) {
426 LOGD(ERROR, domid, "cannot run bootloader with no boot disk");
427 rc = ERROR_FAIL;
428 goto out;
429 }
430
431 bootloader_setpaths(gc, bl);
432
433 const char *logfile_leaf = GCSPRINTF("bootloader.%"PRIu32, domid);
434 rc = libxl_create_logfile(CTX, logfile_leaf, &logfile_tmp);
435 if (rc) goto out;
436
437 /* Transfer ownership of log filename to bl and the gc */
438 bl->logfile = logfile_tmp;
439 libxl__ptr_add(gc, logfile_tmp);
440 logfile_tmp = NULL;
441
442 bl->display.log = fopen(bl->logfile, "a");
443 if (!bl->display.log) {
444 LOGED(ERROR, domid,
445 "failed to create bootloader logfile %s", bl->logfile);
446 rc = ERROR_FAIL;
447 goto out;
448 }
449
450 for (;;) {
451 r = mkdir(bl->outputdir, 0600);
452 if (!r) break;
453 if (errno == EINTR) continue;
454 if (errno == EEXIST) break;
455 LOGED(ERROR, domid,
456 "failed to create bootloader dir %s", bl->outputdir);
457 rc = ERROR_FAIL;
458 goto out;
459 }
460
461 for (;;) {
462 r = open(bl->outputpath, O_WRONLY|O_CREAT|O_TRUNC, 0600);
463 if (r>=0) { close(r); break; }
464 if (errno == EINTR) continue;
465 LOGED(ERROR, domid,
466 "failed to precreate bootloader output %s", bl->outputpath);
467 rc = ERROR_FAIL;
468 goto out;
469 }
470
471
472 /* This sets the state of the dls struct from Undefined to Idle */
473 libxl__device_disk_local_init(&bl->dls);
474 bl->dls.ao = ao;
475 bl->dls.in_disk = bl->disk;
476 bl->dls.blkdev_start = info->blkdev_start;
477 bl->dls.callback = bootloader_disk_attached_cb;
478 libxl__device_disk_local_initiate_attach(egc, &bl->dls);
479 return;
480
481 out:
482 assert(rc);
483 out_ok:
484 free(logfile_tmp);
485 bootloader_callback(egc, bl, rc);
486 }
487
bootloader_disk_attached_cb(libxl__egc * egc,libxl__disk_local_state * dls,int rc)488 static void bootloader_disk_attached_cb(libxl__egc *egc,
489 libxl__disk_local_state *dls,
490 int rc)
491 {
492 STATE_AO_GC(dls->ao);
493 libxl__bootloader_state *bl = CONTAINER_OF(dls, *bl, dls);
494 const libxl_domain_build_info *info = bl->info;
495 const char *bootloader;
496
497 if (rc) {
498 LOGD(ERROR, bl->domid,
499 "failed to attach local disk for bootloader execution");
500 goto out;
501 }
502
503 LOGD(DEBUG, bl->domid,
504 "Config bootloader value: %s", info->bootloader);
505
506 if ( !strcmp(info->bootloader, "/usr/bin/pygrub") )
507 LOGD(WARN, bl->domid,
508 "bootloader='/usr/bin/pygrub' is deprecated; use " \
509 "bootloader='pygrub' instead");
510
511 bootloader = info->bootloader;
512
513 /* If the full path is not specified, check in the libexec path */
514 if ( bootloader[0] != '/' ) {
515 const char *bltmp;
516 struct stat st;
517
518 bltmp = libxl__abs_path(gc, bootloader, libxl__private_bindir_path());
519 /* Check to see if the file exists in this location; if not,
520 * fall back to checking the path */
521 LOGD(DEBUG, bl->domid,
522 "Checking for bootloader in libexec path: %s", bltmp);
523
524 if ( lstat(bltmp, &st) )
525 LOGD(DEBUG, bl->domid,
526 "%s doesn't exist, falling back to config path",
527 bltmp);
528 else
529 bootloader = bltmp;
530 }
531
532 rc = make_bootloader_args(gc, bl, bootloader);
533 if (rc) goto out;
534
535 bl->openpty.ao = ao;
536 bl->openpty.callback = bootloader_gotptys;
537 bl->openpty.count = 2;
538 bl->openpty.results = bl->ptys;
539 rc = libxl__openptys(&bl->openpty, 0,0);
540 if (rc) goto out;
541
542 return;
543
544 out:
545 assert(rc);
546 bootloader_callback(egc, bl, rc);
547 }
548
bootloader_gotptys(libxl__egc * egc,libxl__openpty_state * op)549 static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op)
550 {
551 libxl__bootloader_state *bl = CONTAINER_OF(op, *bl, openpty);
552 STATE_AO_GC(bl->ao);
553 int rc, r;
554 char *const env[] = { "TERM", "vt100", NULL };
555
556 if (bl->openpty.rc) {
557 rc = bl->openpty.rc;
558 goto out;
559 }
560
561 /*
562 * We need to present the bootloader's tty as a pty slave that xenconsole
563 * can access. Since the bootloader itself needs a pty slave,
564 * we end up with a connection like this:
565 *
566 * xenconsole -- (slave pty1 master) <-> (master pty2 slave) -- bootloader
567 *
568 * where we copy characters between the two master fds, as well as
569 * listening on the bootloader's fifo for the results.
570 */
571
572 char *dom_console_xs_path;
573 char dom_console_slave_tty_path[PATH_MAX];
574 rc = setup_xenconsoled_pty(egc, bl,
575 &dom_console_slave_tty_path[0],
576 sizeof(dom_console_slave_tty_path));
577 if (rc) goto out;
578
579 char *dompath = libxl__xs_get_dompath(gc, bl->domid);
580 if (!dompath) { rc = ERROR_FAIL; goto out; }
581
582 dom_console_xs_path = GCSPRINTF("%s/console/tty", dompath);
583
584 rc = libxl__xs_printf(gc, XBT_NULL, dom_console_xs_path, "%s",
585 dom_console_slave_tty_path);
586 if (rc) {
587 LOGED(ERROR, bl->domid, "xs write console path %s := %s failed",
588 dom_console_xs_path, dom_console_slave_tty_path);
589 rc = ERROR_FAIL;
590 goto out;
591 }
592
593 bl->deathcheck.what = "stopping bootloader";
594 bl->deathcheck.domid = bl->domid;
595 bl->deathcheck.callback = bootloader_domaindeath;
596 rc = libxl__domaindeathcheck_start(ao, &bl->deathcheck);
597 if (rc) goto out;
598
599 if (bl->console_available)
600 bl->console_available(egc, bl);
601
602 int bootloader_master = libxl__carefd_fd(bl->ptys[0].master);
603 int xenconsole_master = libxl__carefd_fd(bl->ptys[1].master);
604
605 libxl_fd_set_nonblock(CTX, bootloader_master, 1);
606 libxl_fd_set_nonblock(CTX, xenconsole_master, 1);
607
608 bl->keystrokes.writefd = bl->display.readfd = bootloader_master;
609 bl->keystrokes.writewhat = bl->display.readwhat = "bootloader pty";
610
611 bl->keystrokes.readfd = bl->display.writefd = xenconsole_master;
612 bl->keystrokes.readwhat = bl->display.writewhat = "xenconsole client pty";
613
614 bl->keystrokes.ao = ao;
615 bl->keystrokes.maxsz = BOOTLOADER_BUF_OUT;
616 bl->keystrokes.bytes_to_read = -1;
617 bl->keystrokes.copywhat =
618 GCSPRINTF("bootloader input for domain %"PRIu32, bl->domid);
619 bl->keystrokes.callback = bootloader_keystrokes_copyfail;
620 bl->keystrokes.callback_pollhup = bootloader_keystrokes_copyfail;
621 /* pollhup gets called with errnoval==-1 which is not otherwise
622 * possible since errnos are nonnegative, so it's unambiguous */
623 rc = libxl__datacopier_start(&bl->keystrokes);
624 if (rc) goto out;
625
626 bl->display.ao = ao;
627 bl->display.maxsz = BOOTLOADER_BUF_IN;
628 bl->display.bytes_to_read = -1;
629 bl->display.copywhat =
630 GCSPRINTF("bootloader output for domain %"PRIu32, bl->domid);
631 bl->display.callback = bootloader_display_copyfail;
632 bl->display.callback_pollhup = bootloader_display_copyfail;
633 rc = libxl__datacopier_start(&bl->display);
634 if (rc) goto out;
635
636 LOGD(DEBUG, bl->domid, "executing bootloader: %s", bl->args[0]);
637 for (const char **blarg = bl->args;
638 *blarg;
639 blarg++)
640 LOGD(DEBUG, bl->domid, " bootloader arg: %s", *blarg);
641
642 struct termios termattr;
643 const libxl_domain_build_info *info = bl->info;
644
645 if (libxl_defbool_val(info->bootloader_restrict)) {
646 const char *timeout_env = getenv("LIBXL_BOOTLOADER_TIMEOUT");
647 int timeout = timeout_env ? atoi(timeout_env)
648 : LIBXL_BOOTLOADER_TIMEOUT;
649
650 if (timeout) {
651 /* Set execution timeout */
652 rc = libxl__ev_time_register_rel(ao, &bl->time,
653 bootloader_timeout,
654 timeout * 1000);
655 if (rc) {
656 LOGED(ERROR, bl->domid,
657 "unable to register timeout for bootloader execution");
658 goto out;
659 }
660 }
661 }
662
663 pid_t pid = libxl__ev_child_fork(gc, &bl->child, bootloader_finished);
664 if (pid == -1) {
665 rc = ERROR_FAIL;
666 goto out;
667 }
668
669 if (!pid) {
670 /* child */
671 r = login_tty(libxl__carefd_fd(bl->ptys[0].slave));
672 if (r) { LOGED(ERROR, bl->domid, "login_tty failed"); exit(-1); }
673 libxl__exec(gc, -1, -1, -1, bl->args[0], (char **) bl->args, env);
674 }
675
676 /* parent */
677
678 /*
679 * On Solaris, the master pty side does not have terminal semantics,
680 * so don't try to set any attributes, as it will fail.
681 */
682 #if !defined(__sun__)
683 tcgetattr(bootloader_master, &termattr);
684 cfmakeraw(&termattr);
685 tcsetattr(bootloader_master, TCSANOW, &termattr);
686 #endif
687
688 return;
689
690 out:
691 bootloader_callback(egc, bl, rc);
692 }
693
694 /* perhaps one of these will be called, but perhaps not */
bootloader_copyfail(libxl__egc * egc,const char * which,libxl__bootloader_state * bl,int ondisplay,int rc,int onwrite,int errnoval)695 static void bootloader_copyfail(libxl__egc *egc, const char *which,
696 libxl__bootloader_state *bl, int ondisplay,
697 int rc, int onwrite, int errnoval)
698 {
699 STATE_AO_GC(bl->ao);
700
701 if (errnoval==-1) {
702 /* POLLHUP */
703 if (!!ondisplay != !!onwrite) {
704 rc = 0;
705 bl->got_pollhup = 1;
706 } else {
707 LOGD(ERROR, bl->domid, "unexpected POLLHUP on %s", which);
708 }
709 } else if (!rc) {
710 LOGD(ERROR, bl->domid, "unexpected eof copying %s", which);
711 rc = ERROR_FAIL;
712 }
713
714 bootloader_stop(egc, bl, rc);
715 }
bootloader_keystrokes_copyfail(libxl__egc * egc,libxl__datacopier_state * dc,int rc,int onwrite,int errnoval)716 static void bootloader_keystrokes_copyfail(libxl__egc *egc,
717 libxl__datacopier_state *dc, int rc, int onwrite, int errnoval)
718 {
719 libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, keystrokes);
720 bootloader_copyfail(egc, "bootloader input", bl, 0, rc,onwrite,errnoval);
721 }
bootloader_display_copyfail(libxl__egc * egc,libxl__datacopier_state * dc,int rc,int onwrite,int errnoval)722 static void bootloader_display_copyfail(libxl__egc *egc,
723 libxl__datacopier_state *dc, int rc, int onwrite, int errnoval)
724 {
725 libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, display);
726 bootloader_copyfail(egc, "bootloader output", bl, 1, rc,onwrite,errnoval);
727 }
bootloader_timeout(libxl__egc * egc,libxl__ev_time * ev,const struct timeval * requested_abs,int rc)728 static void bootloader_timeout(libxl__egc *egc, libxl__ev_time *ev,
729 const struct timeval *requested_abs, int rc)
730 {
731 libxl__bootloader_state *bl = CONTAINER_OF(ev, *bl, time);
732 STATE_AO_GC(bl->ao);
733
734 libxl__ev_time_deregister(gc, &bl->time);
735
736 assert(libxl__ev_child_inuse(&bl->child));
737 LOGD(ERROR, bl->domid, "killing bootloader because of timeout");
738
739 libxl__ev_child_kill_deregister(ao, &bl->child, SIGKILL);
740
741 bootloader_callback(egc, bl, rc);
742 }
743
bootloader_domaindeath(libxl__egc * egc,libxl__domaindeathcheck * dc,int rc)744 static void bootloader_domaindeath(libxl__egc *egc,
745 libxl__domaindeathcheck *dc,
746 int rc)
747 {
748 libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, deathcheck);
749 bootloader_stop(egc, bl, rc);
750 }
751
bootloader_finished(libxl__egc * egc,libxl__ev_child * child,pid_t pid,int status)752 static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child,
753 pid_t pid, int status)
754 {
755 libxl__bootloader_state *bl = CONTAINER_OF(child, *bl, child);
756 STATE_AO_GC(bl->ao);
757 int rc;
758
759 libxl__ev_time_deregister(gc, &bl->time);
760 libxl__datacopier_kill(&bl->keystrokes);
761 libxl__datacopier_kill(&bl->display);
762
763 if (status) {
764 if (bl->got_pollhup && WIFSIGNALED(status) && WTERMSIG(status)==SIGTERM)
765 LOGD(ERROR, bl->domid, "got POLLHUP, sent SIGTERM");
766 LOGD(ERROR, bl->domid,
767 "bootloader failed - consult logfile %s", bl->logfile);
768 libxl_report_child_exitstatus(CTX, XTL_ERROR, "bootloader",
769 pid, status);
770 rc = ERROR_FAIL;
771 goto out;
772 } else {
773 LOGD(DEBUG, bl->domid, "bootloader completed");
774 }
775
776 if (bl->rc) {
777 /* datacopier went wrong */
778 rc = bl->rc;
779 goto out;
780 }
781
782 rc = parse_bootloader_result(egc, bl);
783 if (rc) goto out;
784
785 rc = 0;
786 LOGD(DEBUG, bl->domid, "bootloader execution successful");
787
788 out:
789 bootloader_callback(egc, bl, rc);
790 }
791
792 /*
793 * Local variables:
794 * mode: C
795 * c-basic-offset: 4
796 * indent-tabs-mode: nil
797 * End:
798 */
799