static void stream_done(libxl__egc *egc, libxl__stream_read_state *stream, int rc) { libxl__sr_record_buf *rec, *trec; assert(stream->running); assert(!stream->in_checkpoint); stream->running = false; if (stream->incoming_record) free_record(stream->incoming_record); if (stream->emu_carefd) libxl__carefd_close(stream->emu_carefd); /* If we started a conversion helper, we took ownership of its carefd. */ if (stream->chs.v2_carefd) libxl__carefd_close(stream->chs.v2_carefd); /* The record queue had better be empty if the stream believes * itself to have been successful. */ assert(LIBXL_STAILQ_EMPTY(&stream->record_queue) || stream->rc); LIBXL_STAILQ_FOREACH_SAFE(rec, &stream->record_queue, entry, trec) free_record(rec); check_all_finished(egc, stream, rc); }
static void openpty_cleanup(libxl__openpty_state *op) { int i; for (i=0; i<op->count; i++) { libxl__openpty_result *res = &op->results[i]; libxl__carefd_close(res->master); res->master = 0; libxl__carefd_close(res->slave); res->slave = 0; } }
static void helper_done(libxl__egc *egc, libxl__save_helper_state *shs) { STATE_AO_GC(shs->ao); libxl__ev_fd_deregister(gc, &shs->readable); libxl__carefd_close(shs->pipes[0]); shs->pipes[0] = 0; libxl__carefd_close(shs->pipes[1]); shs->pipes[1] = 0; assert(!libxl__ev_child_inuse(&shs->child)); if (shs->toolstack_data_file) fclose(shs->toolstack_data_file); shs->egc = egc; shs->completion_callback(egc, shs->caller_state, shs->rc, shs->retval, shs->errnoval); shs->egc = 0; }
static void helper_done(libxl__egc *egc, libxl__save_helper_state *shs) { STATE_AO_GC(shs->ao); libxl__ao_abortable_deregister(&shs->abrt); libxl__ev_fd_deregister(gc, &shs->readable); libxl__carefd_close(shs->pipes[0]); shs->pipes[0] = 0; libxl__carefd_close(shs->pipes[1]); shs->pipes[1] = 0; assert(!libxl__save_helper_inuse(shs)); shs->egc = egc; shs->completion_callback(egc, shs->caller_state, shs->rc, shs->retval, shs->errnoval); shs->egc = 0; }
void libxl__unlock_domain_userdata(libxl__domain_userdata_lock *lock) { if (lock->path) unlink(lock->path); if (lock->lock_carefd) libxl__carefd_close(lock->lock_carefd); free(lock->path); free(lock); }
static void bootloader_cleanup(libxl__egc *egc, libxl__bootloader_state *bl) { STATE_AO_GC(bl->ao); int i; if (bl->outputpath) libxl__remove_file(gc, bl->outputpath); if (bl->outputdir) libxl__remove_directory(gc, bl->outputdir); libxl__domaindeathcheck_stop(gc,&bl->deathcheck); libxl__datacopier_kill(&bl->keystrokes); libxl__datacopier_kill(&bl->display); for (i=0; i<2; i++) { libxl__carefd_close(bl->ptys[i].master); libxl__carefd_close(bl->ptys[i].slave); } if (bl->display.log) { fclose(bl->display.log); bl->display.log = NULL; } }
static void write_emulator_done(libxl__egc *egc, libxl__datacopier_state *dc, int rc, int onwrite, int errnoval) { libxl__stream_read_state *stream = CONTAINER_OF(dc, *stream, emu_dc); STATE_AO_GC(dc->ao); libxl__carefd_close(stream->emu_carefd); stream->emu_carefd = NULL; if (rc) goto err; stream_continue(egc, stream); return; err: assert(rc); stream_complete(egc, stream, rc); }
void libxl__unlock_domain_userdata(libxl__domain_userdata_lock *lock) { /* It's important to unlink the file before closing fd to avoid * the following race (if close before unlink): * * P1 LOCK P2 UNLOCK * fd1 = open(lockfile) * close(fd2) * flock(fd1) * fstat and stat check success * unlink(lockfile) * return lock * * In above case P1 thinks it has got hold of the lock but * actually lock is released by P2 (lockfile unlinked). */ if (lock->path) unlink(lock->path); if (lock->carefd) libxl__carefd_close(lock->carefd); free(lock->path); free(lock); }
int libxl__openptys(libxl__openpty_state *op, struct termios *termp, struct winsize *winp) { /* * This is completely crazy. openpty calls grantpt which the spec * says may fork, and may not be called with a SIGCHLD handler. * Now our application may have a SIGCHLD handler so that's bad. * We could perhaps block it but we'd need to block it on all * threads. This is just Too Hard. * * So instead, we run openpty in a child process. That child * process then of course has only our own thread and our own * signal handlers. We pass the fds back. * * Since our only current caller actually wants two ptys, we * support calling openpty multiple times for a single fork. */ STATE_AO_GC(op->ao); int count = op->count; int r, i, rc, sockets[2], ptyfds[count][2]; libxl__carefd *for_child = 0; pid_t pid = -1; for (i=0; i<count; i++) { ptyfds[i][0] = ptyfds[i][1] = -1; libxl__openpty_result *res = &op->results[i]; assert(!res->master); assert(!res->slave); } sockets[0] = sockets[1] = -1; /* 0 is for us, 1 for our child */ libxl__carefd_begin(); r = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets); if (r) { sockets[0] = sockets[1] = -1; } for_child = libxl__carefd_opened(CTX, sockets[1]); if (r) { LOGE(ERROR,"socketpair failed"); rc = ERROR_FAIL; goto out; } pid = libxl__ev_child_fork(gc, &op->child, openpty_exited); if (pid == -1) { rc = ERROR_FAIL; goto out; } if (!pid) { /* child */ close(sockets[0]); signal(SIGCHLD, SIG_DFL); for (i=0; i<count; i++) { r = openpty(&ptyfds[i][0], &ptyfds[i][1], NULL, termp, winp); if (r) { LOGE(ERROR,"openpty failed"); _exit(-1); } } rc = libxl__sendmsg_fds(gc, sockets[1], "",1, 2*count, &ptyfds[0][0], "ptys"); if (rc) { LOGE(ERROR,"sendmsg to parent failed"); _exit(-1); } _exit(0); } libxl__carefd_close(for_child); for_child = 0; /* this should be fast so do it synchronously */ libxl__carefd_begin(); char buf[1]; rc = libxl__recvmsg_fds(gc, sockets[0], buf,1, 2*count, &ptyfds[0][0], "ptys"); if (!rc) { for (i=0; i<count; i++) { libxl__openpty_result *res = &op->results[i]; res->master = libxl__carefd_record(CTX, ptyfds[i][0]); res->slave = libxl__carefd_record(CTX, ptyfds[i][1]); } } /* now the pty fds are in the carefds, if they were ever open */ libxl__carefd_unlock(); if (rc) goto out; rc = 0; out: if (sockets[0] >= 0) close(sockets[0]); libxl__carefd_close(for_child); if (libxl__ev_child_inuse(&op->child)) { op->rc = rc; /* we will get a callback when the child dies */ return 0; } assert(rc); openpty_cleanup(op); return rc; }
/* Portability note: this lock utilises flock(2) so a proper implementation of * flock(2) is required. */ libxl__domain_userdata_lock *libxl__lock_domain_userdata(libxl__gc *gc, uint32_t domid) { libxl__domain_userdata_lock *lock = NULL; const char *lockfile; int fd; struct stat stab, fstab; lockfile = libxl__userdata_path(gc, domid, "domain-userdata-lock", "l"); if (!lockfile) goto out; lock = libxl__zalloc(NOGC, sizeof(libxl__domain_userdata_lock)); lock->path = libxl__strdup(NOGC, lockfile); while (true) { libxl__carefd_begin(); fd = open(lockfile, O_RDWR|O_CREAT, 0666); if (fd < 0) LOGE(ERROR, "cannot open lockfile %s, errno=%d", lockfile, errno); lock->lock_carefd = libxl__carefd_opened(CTX, fd); if (fd < 0) goto out; /* Lock the file in exclusive mode, wait indefinitely to * acquire the lock */ while (flock(fd, LOCK_EX)) { switch (errno) { case EINTR: /* Signal received, retry */ continue; default: /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */ LOGE(ERROR, "unexpected error while trying to lock %s, fd=%d, errno=%d", lockfile, fd, errno); goto out; } } if (fstat(fd, &fstab)) { LOGE(ERROR, "cannot fstat %s, fd=%d, errno=%d", lockfile, fd, errno); goto out; } if (stat(lockfile, &stab)) { if (errno != ENOENT) { LOGE(ERROR, "cannot stat %s, errno=%d", lockfile, errno); goto out; } } else { if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino) break; } libxl__carefd_close(lock->lock_carefd); } /* Check the domain is still there, if not we should release the * lock and clean up. */ if (libxl_domain_info(CTX, NULL, domid)) goto out; return lock; out: if (lock) libxl__unlock_domain_userdata(lock); return NULL; }
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);; }