static void libxl__device_nic_add(libxl__egc *egc, uint32_t domid, libxl_device_nic *nic, libxl__ao_device *aodev) { STATE_AO_GC(aodev->ao); flexarray_t *front; flexarray_t *back; libxl__device *device; int rc; xs_transaction_t t = XBT_NULL; libxl_domain_config d_config; libxl_device_nic nic_saved; libxl__domain_userdata_lock *lock = NULL; libxl_domain_config_init(&d_config); libxl_device_nic_init(&nic_saved); libxl_device_nic_copy(CTX, &nic_saved, nic); rc = libxl__device_nic_setdefault(gc, nic, domid, aodev->update_json); if (rc) goto out; front = flexarray_make(gc, 16, 1); back = flexarray_make(gc, 18, 1); if (nic->devid == -1) { if ((nic->devid = libxl__device_nextid(gc, domid, "vif")) < 0) { rc = ERROR_FAIL; goto out; } } libxl__update_config_nic(gc, &nic_saved, nic); GCNEW(device); rc = libxl__device_from_nic(gc, domid, nic, device); if ( rc != 0 ) goto out; flexarray_append(back, "frontend-id"); flexarray_append(back, GCSPRINTF("%d", domid)); flexarray_append(back, "online"); flexarray_append(back, "1"); flexarray_append(back, "state"); flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising)); if (nic->script) flexarray_append_pair(back, "script", libxl__abs_path(gc, nic->script, libxl__xen_script_dir_path())); if (nic->ifname) { flexarray_append(back, "vifname"); flexarray_append(back, nic->ifname); } if (nic->coloft_forwarddev) { flexarray_append(back, "forwarddev"); flexarray_append(back, nic->coloft_forwarddev); } flexarray_append(back, "mac"); flexarray_append(back,GCSPRINTF(LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac))); if (nic->ip) { flexarray_append(back, "ip"); flexarray_append(back, libxl__strdup(gc, nic->ip)); } if (nic->gatewaydev) { flexarray_append(back, "gatewaydev"); flexarray_append(back, libxl__strdup(gc, nic->gatewaydev)); } if (nic->rate_interval_usecs > 0) { flexarray_append(back, "rate"); flexarray_append(back, GCSPRINTF("%"PRIu64",%"PRIu32"", nic->rate_bytes_per_interval, nic->rate_interval_usecs)); } flexarray_append(back, "bridge"); flexarray_append(back, libxl__strdup(gc, nic->bridge)); flexarray_append(back, "handle"); flexarray_append(back, GCSPRINTF("%d", nic->devid)); flexarray_append(back, "type"); flexarray_append(back, libxl__strdup(gc, libxl_nic_type_to_string(nic->nictype))); flexarray_append(front, "backend-id"); flexarray_append(front, GCSPRINTF("%d", nic->backend_domid)); flexarray_append(front, "state"); flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising)); flexarray_append(front, "handle"); flexarray_append(front, GCSPRINTF("%d", nic->devid)); flexarray_append(front, "mac"); flexarray_append(front, GCSPRINTF( LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac))); if (aodev->update_json) { lock = libxl__lock_domain_userdata(gc, domid); if (!lock) { rc = ERROR_LOCK_FAIL; goto out; } rc = libxl__get_domain_configuration(gc, domid, &d_config); if (rc) goto out; DEVICE_ADD(nic, nics, domid, &nic_saved, COMPARE_DEVID, &d_config); rc = libxl__dm_check_start(gc, &d_config, domid); if (rc) goto out; } for (;;) { rc = libxl__xs_transaction_start(gc, &t); if (rc) goto out; rc = libxl__device_exists(gc, t, device); if (rc < 0) goto out; if (rc == 1) { /* already exists in xenstore */ LOG(ERROR, "device already exists in xenstore"); aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */ rc = ERROR_DEVICE_EXISTS; goto out; } if (aodev->update_json) { rc = libxl__set_domain_configuration(gc, domid, &d_config); if (rc) goto out; } libxl__device_generic_add(gc, t, device, libxl__xs_kvs_of_flexarray(gc, back, back->count), libxl__xs_kvs_of_flexarray(gc, front, front->count), NULL); rc = libxl__xs_transaction_commit(gc, &t); if (!rc) break; if (rc < 0) goto out; } aodev->dev = device; aodev->action = LIBXL__DEVICE_ACTION_ADD; libxl__wait_device_connection(egc, aodev); rc = 0; out: libxl__xs_transaction_abort(gc, &t); if (lock) libxl__unlock_domain_userdata(lock); libxl_device_nic_dispose(&nic_saved); libxl_domain_config_dispose(&d_config); aodev->rc = rc; if (rc) aodev->callback(egc, aodev); return; }
static void drbd_preresume(libxl__egc *egc, libxl__remus_device *dev) { STATE_AO_GC(dev->rds->ao); drbd_async_call(egc, dev, drbd_preresume_async, checkpoint_async_call_done); }
static void drbd_setup(libxl__egc *egc, libxl__remus_device *dev) { STATE_AO_GC(dev->rds->ao); match_async_exec(egc, dev); }
/* * Returns a boolean indicating whether a further action should be set * up by the caller. This is needed to prevent mutual recursion with * stream_continue(). * * It is a bug for this function to ever call stream_continue() or * setup_read_record(). */ static bool process_record(libxl__egc *egc, libxl__stream_read_state *stream) { STATE_AO_GC(stream->ao); libxl__domain_create_state *dcs = stream->dcs; libxl__sr_record_buf *rec; bool further_action_needed = false; int rc = 0; /* Pop a record from the head of the queue. */ assert(!LIBXL_STAILQ_EMPTY(&stream->record_queue)); rec = LIBXL_STAILQ_FIRST(&stream->record_queue); LIBXL_STAILQ_REMOVE_HEAD(&stream->record_queue, entry); LOG(DEBUG, "Record: %u, length %u", rec->hdr.type, rec->hdr.length); switch (rec->hdr.type) { case REC_TYPE_END: stream_complete(egc, stream, 0); break; case REC_TYPE_LIBXC_CONTEXT: libxl__xc_domain_restore(egc, dcs, &stream->shs, 0, 0, 0); break; case REC_TYPE_EMULATOR_XENSTORE_DATA: if (dcs->guest_config->b_info.device_model_version == LIBXL_DEVICE_MODEL_VERSION_NONE) { rc = ERROR_FAIL; LOG(ERROR, "Received a xenstore emulator record when none was expected"); goto err; } if (rec->hdr.length < sizeof(libxl__sr_emulator_hdr)) { rc = ERROR_FAIL; LOG(ERROR, "Emulator xenstore data record too short to contain header"); goto err; } rc = libxl__restore_emulator_xenstore_data(dcs, rec->body + sizeof(libxl__sr_emulator_hdr), rec->hdr.length - sizeof(libxl__sr_emulator_hdr)); if (rc) goto err; /* * libxl__restore_emulator_xenstore_data() is a synchronous function. * Request that our caller queues another action for us. */ further_action_needed = true; break; case REC_TYPE_EMULATOR_CONTEXT: if (dcs->guest_config->b_info.device_model_version == LIBXL_DEVICE_MODEL_VERSION_NONE) { rc = ERROR_FAIL; LOG(ERROR, "Received an emulator context record when none was expected"); goto err; } write_emulator_blob(egc, stream, rec); break; case REC_TYPE_CHECKPOINT_END: if (!stream->in_checkpoint) { LOG(ERROR, "Unexpected CHECKPOINT_END record in stream"); rc = ERROR_FAIL; goto err; } checkpoint_done(egc, stream, 0); break; default: LOG(ERROR, "Unrecognised record 0x%08x", rec->hdr.type); rc = ERROR_FAIL; goto err; } assert(!rc); free_record(rec); return further_action_needed; err: assert(rc); free_record(rec); stream_complete(egc, stream, rc); return false; }
static void stream_continue(libxl__egc *egc, libxl__stream_read_state *stream) { STATE_AO_GC(stream->ao); /* * Must not mutually recurse with process_record(). * * For records whose processing function is synchronous * (e.g. TOOLSTACK), process_record() does not start another async * operation, and a further operation should be started. * * A naive solution, which would function in general, would be for * process_record() to call stream_continue(). However, this * would allow the content of the stream to cause mutual * recursion, and possibly for us to fall off our stack. * * Instead, process_record() indicates with its return value * whether a further operation needs to start, and the * recursion_guard is in place to catch any code paths which get * this wrong. */ assert(stream->recursion_guard == false); stream->recursion_guard = true; switch (stream->phase) { case SRS_PHASE_NORMAL: /* * Normal phase (regular migration or restore from file): * * logically: * do { read_record(); process_record(); } while ( not END ); * * Alternate between reading a record from the stream, and * processing the record. There should never be two records * in the queue. */ if (LIBXL_STAILQ_EMPTY(&stream->record_queue)) setup_read_record(egc, stream); else { if (process_record(egc, stream)) setup_read_record(egc, stream); /* * process_record() had better have consumed the one and * only record in the queue. */ assert(LIBXL_STAILQ_EMPTY(&stream->record_queue)); } break; case SRS_PHASE_BUFFERING: { /* * Buffering phase (checkpointed streams only): * * logically: * do { read_record(); } while ( not CHECKPOINT_END ); * * Read and buffer all records from the stream until a * CHECKPOINT_END record is encountered. We need to peek at * the tail to spot the CHECKPOINT_END record, and switch to * the unbuffering phase. */ libxl__sr_record_buf *rec = LIBXL_STAILQ_LAST( &stream->record_queue, libxl__sr_record_buf, entry); assert(stream->in_checkpoint); if (!rec || (rec->hdr.type != REC_TYPE_CHECKPOINT_END)) { setup_read_record(egc, stream); break; } /* * There are now some number of buffered records, with a * CHECKPOINT_END at the end. Start processing them all. */ stream->phase = SRS_PHASE_UNBUFFERING; } /* FALLTHROUGH */ case SRS_PHASE_UNBUFFERING: /* * Unbuffering phase (checkpointed streams only): * * logically: * do { process_record(); } while ( not CHECKPOINT_END ); * * Process all records collected during the buffering phase. */ assert(stream->in_checkpoint); while (process_record(egc, stream)) ; /* * Nothing! process_record() helpfully tells us if no specific * futher actions have been set up, in which case we want to go * ahead and process the next record. */ break; default: abort(); } assert(stream->recursion_guard == true); stream->recursion_guard = false; }
static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs, const char *mode_arg, int stream_fd, int back_channel_fd, const int *preserve_fds, int num_preserve_fds, const unsigned long *argnums, int num_argnums) { STATE_AO_GC(shs->ao); const char *args[HELPER_NR_ARGS + 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__save_helper_init(shs); shs->abrt.ao = shs->ao; shs->abrt.callback = helper_stop; rc = libxl__ao_abortable_register(&shs->abrt); if (rc) goto out; 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") ?: LIBEXEC_BIN "/" "libxl-save-helper"; *arg++ = mode_arg; const char **stream_fd_arg = arg++; const char **back_channel_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) { stream_fd = dup_cloexec(gc, stream_fd, "migration stream fd"); *stream_fd_arg = GCSPRINTF("%d", stream_fd); if (back_channel_fd >= 0) back_channel_fd = dup_cloexec(gc, back_channel_fd, "migration back channel fd"); *back_channel_fd_arg = GCSPRINTF("%d", back_channel_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__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; }
static void datacopier_readable(libxl__egc *egc, libxl__ev_fd *ev, int fd, short events, short revents) { libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, toread); STATE_AO_GC(dc->ao); if (datacopier_pollhup_handled(egc, dc, revents, 0)) return; if (revents & ~POLLIN) { LOG(ERROR, "unexpected poll event 0x%x (should be POLLIN)" " on %s during copy of %s", revents, dc->readwhat, dc->copywhat); datacopier_callback(egc, dc, -1, 0); return; } assert(revents & POLLIN); for (;;) { while (dc->used >= dc->maxsz) { libxl__datacopier_buf *rm = LIBXL_TAILQ_FIRST(&dc->bufs); dc->used -= rm->used; assert(dc->used >= 0); LIBXL_TAILQ_REMOVE(&dc->bufs, rm, entry); free(rm); } libxl__datacopier_buf *buf = LIBXL_TAILQ_LAST(&dc->bufs, libxl__datacopier_bufs); if (!buf || buf->used >= sizeof(buf->buf)) { buf = malloc(sizeof(*buf)); if (!buf) libxl__alloc_failed(CTX, __func__, 1, sizeof(*buf)); buf->used = 0; LIBXL_TAILQ_INSERT_TAIL(&dc->bufs, buf, entry); } int r = read(ev->fd, buf->buf + buf->used, sizeof(buf->buf) - buf->used); if (r < 0) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK) break; LOGE(ERROR, "error reading %s during copy of %s", dc->readwhat, dc->copywhat); datacopier_callback(egc, dc, 0, errno); return; } if (r == 0) { libxl__ev_fd_deregister(gc, &dc->toread); break; } if (dc->log) { int wrote = fwrite(buf->buf + buf->used, 1, r, dc->log); if (wrote != r) { assert(ferror(dc->log)); assert(errno); LOGE(ERROR, "error logging %s", dc->copywhat); datacopier_callback(egc, dc, 0, errno); return; } } buf->used += r; dc->used += r; assert(buf->used <= sizeof(buf->buf)); } datacopier_check_state(egc, dc); }