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