/* * Copyright (C) 2012 Citrix Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ /* * The libxl-save-helper utility speaks a protocol to its caller for * the callbacks. The protocol is as follows. * * The helper talks on stdin and stdout, in binary in machine * endianness. The helper speaks first, and only when it has a * callback to make. It writes a 16-bit number being the message * length, and then the message body. * * Each message starts with a 16-bit number indicating which of the * messages it is, and then some arguments in a binary marshalled form. * If the callback does not need a reply (it returns void), the helper * just continues. Otherwise the helper waits for its caller to send a * single int which is to be the return value from the callback. * * Where feasible the stubs and callbacks have prototypes identical to * those required by xc_domain_save and xc_domain_restore, so that the * autogenerated functions can be used/provided directly. * * The actual messages are in the array @msgs in libxl_save_msgs_gen.pl */ #include "libxl_osdeps.h" #include #include #include #include #include "libxl.h" #include "xenctrl.h" #include "xenguest.h" #include "_libxl_save_msgs_helper.h" /*----- globals -----*/ static const char *program = "libxl-save-helper"; static xentoollog_logger *logger; static xc_interface *xch; /*----- error handling -----*/ static void fail(int errnoval, const char *fmt, ...) __attribute__((noreturn,format(printf,2,3))); static void fail(int errnoval, const char *fmt, ...) { va_list al; va_start(al,fmt); xtl_logv(logger,XTL_ERROR,errnoval,program,fmt,al); exit(-1); } static int read_exactly(int fd, void *buf, size_t len) /* returns 0 if we get eof, even if we got it midway through; 1 if ok */ { while (len) { ssize_t r = read(fd, buf, len); if (r<=0) return r; assert(r <= len); len -= r; buf = (char*)buf + r; } return 1; } static void *xmalloc(size_t sz) { if (!sz) return 0; void *r = malloc(sz); if (!r) { perror("memory allocation failed"); exit(-1); } return r; } /*----- logger -----*/ typedef struct { xentoollog_logger vtable; } xentoollog_logger_tellparent; static void tellparent_vmessage(xentoollog_logger *logger_in, xentoollog_level level, int errnoval, const char *context, const char *format, va_list al) { char *formatted; int r = vasprintf(&formatted, format, al); if (r < 0) { perror("memory allocation failed during logging"); exit(-1); } helper_stub_log(level, errnoval, context, formatted, 0); free(formatted); } static void tellparent_progress(struct xentoollog_logger *logger_in, const char *context, const char *doing_what, int percent, unsigned long done, unsigned long total) { helper_stub_progress(context, doing_what, done, total, 0); } static void tellparent_destroy(struct xentoollog_logger *logger_in) { abort(); } static xentoollog_logger_tellparent *createlogger_tellparent(void) { xentoollog_logger_tellparent newlogger; return XTL_NEW_LOGGER(tellparent, newlogger); } /*----- helper functions called by autogenerated stubs -----*/ unsigned char * helper_allocbuf(int len, void *user) { return xmalloc(len); } static void transmit(const unsigned char *msg, int len, void *user) { while (len) { int r = write(1, msg, len); if (r<0) { perror("write"); exit(-1); } assert(r >= 0); assert(r <= len); len -= r; msg += r; } } void helper_transmitmsg(unsigned char *msg_freed, int len_in, void *user) { assert(len_in < 64*1024); uint16_t len = len_in; transmit((const void*)&len, sizeof(len), user); transmit(msg_freed, len, user); free(msg_freed); } int helper_getreply(void *user) { int v; int r = read_exactly(0, &v, sizeof(v)); if (r<=0) exit(-2); return v; } /*----- other callbacks -----*/ static int toolstack_save_fd; static uint32_t toolstack_save_len; static int toolstack_save_cb(uint32_t domid, uint8_t **buf, uint32_t *len, void *data) { assert(toolstack_save_fd > 0); int r = lseek(toolstack_save_fd, 0, SEEK_SET); if (r) fail(errno,"rewind toolstack data tmpfile"); *buf = xmalloc(toolstack_save_len); r = read_exactly(toolstack_save_fd, *buf, toolstack_save_len); if (r<0) fail(errno,"read toolstack data"); if (r==0) fail(0,"read toolstack data eof"); *len = toolstack_save_len; return 0; } static void startup(const char *op) { logger = (xentoollog_logger*)createlogger_tellparent(); if (!logger) { fprintf(stderr, "%s: cannot initialise logger\n", program); exit(-1); } xtl_log(logger,XTL_DEBUG,0,program,"starting %s",op); xch = xc_interface_open(logger,logger,0); if (!xch) fail(errno,"xc_interface_open failed"); } static void complete(int retval) { int errnoval = retval ? errno : 0; /* suppress irrelevant errnos */ xtl_log(logger,XTL_DEBUG,errnoval,program,"complete r=%d",retval); helper_stub_complete(retval,errnoval,0); exit(0); } static struct save_callbacks helper_save_callbacks; static struct restore_callbacks helper_restore_callbacks; int main(int argc, char **argv) { int r; #define NEXTARG (++argv, assert(*argv), *argv) const char *mode = *++argv; assert(mode); if (!strcmp(mode,"--save-domain")) { int io_fd = atoi(NEXTARG); uint32_t dom = strtoul(NEXTARG,0,10); uint32_t max_iters = strtoul(NEXTARG,0,10); uint32_t max_factor = strtoul(NEXTARG,0,10); uint32_t flags = strtoul(NEXTARG,0,10); int hvm = atoi(NEXTARG); unsigned long genidad = strtoul(NEXTARG,0,10); toolstack_save_fd = atoi(NEXTARG); toolstack_save_len = strtoul(NEXTARG,0,10); unsigned cbflags = strtoul(NEXTARG,0,10); assert(!*++argv); if (toolstack_save_fd >= 0) helper_save_callbacks.toolstack_save = toolstack_save_cb; helper_setcallbacks_save(&helper_save_callbacks, cbflags); startup("save"); r = xc_domain_save(xch, io_fd, dom, max_iters, max_factor, flags, &helper_save_callbacks, hvm, genidad); complete(r); } else if (!strcmp(mode,"--restore-domain")) { int io_fd = atoi(NEXTARG); uint32_t dom = strtoul(NEXTARG,0,10); unsigned store_evtchn = strtoul(NEXTARG,0,10); domid_t store_domid = strtoul(NEXTARG,0,10); unsigned console_evtchn = strtoul(NEXTARG,0,10); domid_t console_domid = strtoul(NEXTARG,0,10); unsigned int hvm = strtoul(NEXTARG,0,10); unsigned int pae = strtoul(NEXTARG,0,10); int superpages = strtoul(NEXTARG,0,10); int no_incr_genidad = strtoul(NEXTARG,0,10); unsigned cbflags = strtoul(NEXTARG,0,10); int checkpointed = strtoul(NEXTARG,0,10); assert(!*++argv); helper_setcallbacks_restore(&helper_restore_callbacks, cbflags); unsigned long store_mfn = 0; unsigned long console_mfn = 0; unsigned long genidad = 0; startup("restore"); r = xc_domain_restore(xch, io_fd, dom, store_evtchn, &store_mfn, store_domid, console_evtchn, &console_mfn, console_domid, hvm, pae, superpages, no_incr_genidad, checkpointed, &genidad, &helper_restore_callbacks); helper_stub_restore_results(store_mfn,console_mfn,genidad,0); complete(r); } else { assert(!"unexpected mode argument"); } } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */