static pid_t fork_exec_bootloader(int *master, const char *arg0, char **args) { struct termios termattr; pid_t pid = forkpty(master, NULL, NULL, NULL); if (pid == -1) return -1; else if (pid == 0) { setenv("TERM", "vt100", 1); libxl__exec(-1, -1, -1, arg0, args); return -1; } /* * On Solaris, the master pty side does not have terminal semantics, * so don't try to set any attributes, as it will fail. */ #if !defined(__sun__) tcgetattr(*master, &termattr); cfmakeraw(&termattr); tcsetattr(*master, TCSANOW, &termattr); #endif fcntl(*master, F_SETFL, O_NDELAY); return pid; }
int libxl__async_exec_start(libxl__async_exec_state *aes) { pid_t pid; /* Convenience aliases */ libxl__ao *ao = aes->ao; AO_GC; libxl__ev_child *const child = &aes->child; char ** const args = aes->args; aes->rc = 0; /* Set execution timeout */ if (libxl__ev_time_register_rel(ao, &aes->time, async_exec_timeout, aes->timeout_ms)) { LOG(ERROR, "unable to register timeout for executing: %s", aes->what); goto out; } LOG(DEBUG, "forking to execute: %s ", aes->what); /* Fork and exec */ pid = libxl__ev_child_fork(gc, child, async_exec_done); if (pid == -1) { LOG(ERROR, "unable to fork"); goto out; } if (!pid) { /* child */ libxl__exec(gc, aes->stdfds[0], aes->stdfds[1], aes->stdfds[2], args[0], args, aes->env); } return 0; out: return ERROR_FAIL; }
static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op) { libxl__bootloader_state *bl = CONTAINER_OF(op, *bl, openpty); STATE_AO_GC(bl->ao); int rc, r; char *const env[] = { "TERM", "vt100", NULL }; if (bl->openpty.rc) { rc = bl->openpty.rc; goto out; } /* * We need to present the bootloader's tty as a pty slave that xenconsole * can access. Since the bootloader itself needs a pty slave, * we end up with a connection like this: * * xenconsole -- (slave pty1 master) <-> (master pty2 slave) -- bootloader * * where we copy characters between the two master fds, as well as * listening on the bootloader's fifo for the results. */ char *dom_console_xs_path; char dom_console_slave_tty_path[PATH_MAX]; rc = setup_xenconsoled_pty(egc, bl, &dom_console_slave_tty_path[0], sizeof(dom_console_slave_tty_path)); if (rc) goto out; char *dompath = libxl__xs_get_dompath(gc, bl->domid); if (!dompath) { rc = ERROR_FAIL; goto out; } dom_console_xs_path = GCSPRINTF("%s/console/tty", dompath); rc = libxl__xs_write(gc, XBT_NULL, dom_console_xs_path, "%s", dom_console_slave_tty_path); if (rc) { LOGE(ERROR,"xs write console path %s := %s failed", dom_console_xs_path, dom_console_slave_tty_path); rc = ERROR_FAIL; goto out; } bl->deathcheck.what = "stopping bootloader"; bl->deathcheck.domid = bl->domid; bl->deathcheck.callback = bootloader_domaindeath; rc = libxl__domaindeathcheck_start(gc, &bl->deathcheck); if (rc) goto out; if (bl->console_available) bl->console_available(egc, bl); int bootloader_master = libxl__carefd_fd(bl->ptys[0].master); int xenconsole_master = libxl__carefd_fd(bl->ptys[1].master); libxl_fd_set_nonblock(CTX, bootloader_master, 1); libxl_fd_set_nonblock(CTX, xenconsole_master, 1); bl->keystrokes.writefd = bl->display.readfd = bootloader_master; bl->keystrokes.writewhat = bl->display.readwhat = "bootloader pty"; bl->keystrokes.readfd = bl->display.writefd = xenconsole_master; bl->keystrokes.readwhat = bl->display.writewhat = "xenconsole client pty"; bl->keystrokes.ao = ao; bl->keystrokes.maxsz = BOOTLOADER_BUF_OUT; bl->keystrokes.copywhat = GCSPRINTF("bootloader input for domain %"PRIu32, bl->domid); bl->keystrokes.callback = bootloader_keystrokes_copyfail; bl->keystrokes.callback_pollhup = bootloader_keystrokes_copyfail; /* pollhup gets called with errnoval==-1 which is not otherwise * possible since errnos are nonnegative, so it's unambiguous */ rc = libxl__datacopier_start(&bl->keystrokes); if (rc) goto out; bl->display.ao = ao; bl->display.maxsz = BOOTLOADER_BUF_IN; bl->display.copywhat = GCSPRINTF("bootloader output for domain %"PRIu32, bl->domid); bl->display.callback = bootloader_display_copyfail; bl->display.callback_pollhup = bootloader_display_copyfail; rc = libxl__datacopier_start(&bl->display); if (rc) goto out; LOG(DEBUG, "executing bootloader: %s", bl->args[0]); for (const char **blarg = bl->args; *blarg; blarg++) LOG(DEBUG, " bootloader arg: %s", *blarg); struct termios termattr; pid_t pid = libxl__ev_child_fork(gc, &bl->child, bootloader_finished); if (pid == -1) { rc = ERROR_FAIL; goto out; } if (!pid) { /* child */ r = login_tty(libxl__carefd_fd(bl->ptys[0].slave)); if (r) { LOGE(ERROR, "login_tty failed"); exit(-1); } libxl__exec(gc, -1, -1, -1, bl->args[0], (char **) bl->args, env); exit(-1); } /* parent */ /* * On Solaris, the master pty side does not have terminal semantics, * so don't try to set any attributes, as it will fail. */ #if !defined(__sun__) tcgetattr(bootloader_master, &termattr); cfmakeraw(&termattr); tcsetattr(bootloader_master, TCSANOW, &termattr); #endif return; out: bootloader_callback(egc, bl, rc); }
void libxl__spawn_local_dm(libxl__egc *egc, libxl__dm_spawn_state *dmss) { /* convenience aliases */ const int domid = dmss->guest_domid; libxl__domain_build_state *const state = dmss->build_state; libxl__spawn_state *const spawn = &dmss->spawn; STATE_AO_GC(dmss->spawn.ao); libxl_ctx *ctx = CTX; libxl_domain_config *guest_config = dmss->guest_config; const libxl_domain_create_info *c_info = &guest_config->c_info; const libxl_domain_build_info *b_info = &guest_config->b_info; const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config); char *path, *logfile; int logfile_w, null; int rc; char **args, **arg; xs_transaction_t t; char *vm_path; char **pass_stuff; const char *dm; if (libxl_defbool_val(b_info->device_model_stubdomain)) { abort(); } dm = libxl__domain_device_model(gc, b_info); if (!dm) { rc = ERROR_FAIL; goto out; } if (access(dm, X_OK) < 0) { LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "device model %s is not executable", dm); rc = ERROR_FAIL; goto out; } args = libxl__build_device_model_args(gc, dm, domid, guest_config, state); if (!args) { rc = ERROR_FAIL; goto out; } if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) { path = xs_get_domain_path(ctx->xsh, domid); libxl__xs_write(gc, XBT_NULL, libxl__sprintf(gc, "%s/hvmloader/bios", path), "%s", libxl_bios_type_to_string(b_info->u.hvm.bios)); /* Disable relocating memory to make the MMIO hole larger * unless we're running qemu-traditional */ libxl__xs_write(gc, XBT_NULL, libxl__sprintf(gc, "%s/hvmloader/allow-memory-relocate", path), "%d", b_info->device_model_version==LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL); free(path); } path = libxl__sprintf(gc, "/local/domain/0/device-model/%d", domid); xs_mkdir(ctx->xsh, XBT_NULL, path); if (b_info->type == LIBXL_DOMAIN_TYPE_HVM && b_info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL) libxl__xs_write(gc, XBT_NULL, libxl__sprintf(gc, "%s/disable_pf", path), "%d", !libxl_defbool_val(b_info->u.hvm.xen_platform_pci)); libxl_create_logfile(ctx, libxl__sprintf(gc, "qemu-dm-%s", c_info->name), &logfile); logfile_w = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0644); free(logfile); null = open("/dev/null", O_RDONLY); const char *dom_path = libxl__xs_get_dompath(gc, domid); spawn->pidpath = GCSPRINTF("%s/%s", dom_path, "image/device-model-pid"); if (vnc && vnc->passwd) { /* This xenstore key will only be used by qemu-xen-traditionnal. * The code to supply vncpasswd to qemu-xen is later. */ retry_transaction: /* Find uuid and the write the vnc password to xenstore for qemu. */ t = xs_transaction_start(ctx->xsh); vm_path = libxl__xs_read(gc,t,libxl__sprintf(gc, "%s/vm", dom_path)); if (vm_path) { /* Now write the vncpassword into it. */ pass_stuff = libxl__calloc(gc, 3, sizeof(char *)); pass_stuff[0] = "vncpasswd"; pass_stuff[1] = vnc->passwd; libxl__xs_writev(gc,t,vm_path,pass_stuff); if (!xs_transaction_end(ctx->xsh, t, 0)) if (errno == EAGAIN) goto retry_transaction; } } LIBXL__LOG(CTX, XTL_DEBUG, "Spawning device-model %s with arguments:", dm); for (arg = args; *arg; arg++) LIBXL__LOG(CTX, XTL_DEBUG, " %s", *arg); spawn->what = GCSPRINTF("domain %d device model", domid); spawn->xspath = GCSPRINTF("/local/domain/0/device-model/%d/state", domid); spawn->timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000; spawn->pidpath = GCSPRINTF("%s/image/device-model-pid", dom_path); spawn->midproc_cb = libxl__spawn_record_pid; spawn->confirm_cb = device_model_confirm; spawn->failure_cb = device_model_startup_failed; spawn->detached_cb = device_model_detached; rc = libxl__spawn_spawn(egc, spawn); if (rc < 0) goto out_close; if (!rc) { /* inner child */ setsid(); libxl__exec(gc, null, logfile_w, logfile_w, dm, args, NULL); } rc = 0; out_close: close(null); close(logfile_w); out: if (rc) device_model_spawn_outcome(egc, dmss, rc); }
static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs, const char *mode_arg, int stream_fd, const int *preserve_fds, int num_preserve_fds, const unsigned long *argnums, int num_argnums) { STATE_AO_GC(shs->ao); const char *args[4 + num_argnums]; const char **arg = args; int i, rc; /* Resources we must free */ libxl__carefd *childs_pipes[2] = { 0,0 }; /* Convenience aliases */ const uint32_t domid = shs->domid; shs->rc = 0; shs->completed = 0; shs->pipes[0] = shs->pipes[1] = 0; libxl__ev_fd_init(&shs->readable); libxl__ev_child_init(&shs->child); shs->stdin_what = GCSPRINTF("domain %"PRIu32" save/restore helper" " stdin pipe", domid); shs->stdout_what = GCSPRINTF("domain %"PRIu32" save/restore helper" " stdout pipe", domid); *arg++ = getenv("LIBXL_SAVE_HELPER") ?: PRIVATE_BINDIR "/" "libxl-save-helper"; *arg++ = mode_arg; const char **stream_fd_arg = arg++; for (i=0; i<num_argnums; i++) *arg++ = GCSPRINTF("%lu", argnums[i]); *arg++ = 0; assert(arg == args + ARRAY_SIZE(args)); libxl__carefd_begin(); int childfd; for (childfd=0; childfd<2; childfd++) { /* Setting up the pipe for the child's fd childfd */ int fds[2]; if (libxl_pipe(CTX,fds)) { rc = ERROR_FAIL; libxl__carefd_unlock(); goto out; } int childs_end = childfd==0 ? 0 /*read*/ : 1 /*write*/; int our_end = childfd==0 ? 1 /*write*/ : 0 /*read*/; childs_pipes[childfd] = libxl__carefd_record(CTX, fds[childs_end]); shs->pipes[childfd] = libxl__carefd_record(CTX, fds[our_end]); } libxl__carefd_unlock(); pid_t pid = libxl__ev_child_fork(gc, &shs->child, helper_exited); if (!pid) { if (stream_fd <= 2) { stream_fd = dup(stream_fd); if (stream_fd < 0) { LOGE(ERROR,"dup migration stream fd"); exit(-1); } } libxl_fd_set_cloexec(CTX, stream_fd, 0); *stream_fd_arg = GCSPRINTF("%d", stream_fd); for (i=0; i<num_preserve_fds; i++) if (preserve_fds[i] >= 0) { assert(preserve_fds[i] > 2); libxl_fd_set_cloexec(CTX, preserve_fds[i], 0); } libxl__exec(gc, libxl__carefd_fd(childs_pipes[0]), libxl__carefd_fd(childs_pipes[1]), -1, args[0], (char**)args, 0); } libxl__carefd_close(childs_pipes[0]); libxl__carefd_close(childs_pipes[1]); rc = libxl__ev_fd_register(gc, &shs->readable, helper_stdout_readable, libxl__carefd_fd(shs->pipes[1]), POLLIN|POLLPRI); if (rc) goto out; return; out: libxl__carefd_close(childs_pipes[0]); libxl__carefd_close(childs_pipes[1]); helper_failed(egc, shs, rc);; }
int libxl__create_device_model(libxl_ctx *ctx, libxl_device_model_info *info, libxl_device_disk *disks, int num_disks, libxl_device_nic *vifs, int num_vifs, libxl__device_model_starting **starting_r) { libxl__gc gc = LIBXL_INIT_GC(ctx); char *path, *logfile; int logfile_w, null; int rc; char **args; libxl__device_model_starting buf_starting, *p; xs_transaction_t t; char *vm_path; char **pass_stuff; if (strstr(info->device_model, "stubdom-dm")) { libxl_device_vfb vfb; libxl_device_vkb vkb; libxl_vfb_and_vkb_from_device_model_info(ctx, info, &vfb, &vkb); rc = libxl_create_stubdom(ctx, info, disks, num_disks, vifs, num_vifs, &vfb, &vkb, starting_r); goto out; } args = libxl_build_device_model_args(&gc, info, vifs, num_vifs); if (!args) { rc = ERROR_FAIL; goto out; } path = libxl__sprintf(&gc, "/local/domain/0/device-model/%d", info->domid); xs_mkdir(ctx->xsh, XBT_NULL, path); libxl__xs_write(&gc, XBT_NULL, libxl__sprintf(&gc, "%s/disable_pf", path), "%d", !info->xen_platform_pci); libxl_create_logfile(ctx, libxl__sprintf(&gc, "qemu-dm-%s", info->dom_name), &logfile); logfile_w = open(logfile, O_WRONLY|O_CREAT, 0644); free(logfile); null = open("/dev/null", O_RDONLY); if (starting_r) { rc = ERROR_NOMEM; *starting_r = calloc(sizeof(libxl__device_model_starting), 1); if (!*starting_r) goto out_close; p = *starting_r; p->for_spawn = calloc(sizeof(libxl__spawn_starting), 1); } else { p = &buf_starting; p->for_spawn = NULL; } p->domid = info->domid; p->dom_path = libxl__xs_get_dompath(&gc, info->domid); if (!p->dom_path) { rc = ERROR_FAIL; goto out_close; } if (info->vncpasswd) { retry_transaction: /* Find uuid and the write the vnc password to xenstore for qemu. */ t = xs_transaction_start(ctx->xsh); vm_path = libxl__xs_read(&gc,t,libxl__sprintf(&gc, "%s/vm", p->dom_path)); if (vm_path) { /* Now write the vncpassword into it. */ pass_stuff = libxl__calloc(&gc, 3, sizeof(char *)); pass_stuff[0] = "vncpasswd"; pass_stuff[1] = info->vncpasswd; libxl__xs_writev(&gc,t,vm_path,pass_stuff); if (!xs_transaction_end(ctx->xsh, t, 0)) if (errno == EAGAIN) goto retry_transaction; } } rc = libxl__spawn_spawn(ctx, p, "device model", dm_xenstore_record_pid); if (rc < 0) goto out_close; if (!rc) { /* inner child */ setsid(); libxl__exec(null, logfile_w, logfile_w, libxl__abs_path(&gc, info->device_model, libxl_libexec_path()), args); } rc = 0; out_close: close(null); close(logfile_w); free(args); out: libxl__free_all(&gc); return rc; }