diff -r 8ad5450b580b Makefile.target --- a/Makefile.target Tue Jan 16 17:04:55 2007 -0600 +++ b/Makefile.target Tue Jan 16 17:04:56 2007 -0600 @@ -300,7 +300,7 @@ endif # must use static linking to avoid leaving stuff in virtual address space VL_OBJS=vl.o osdep.o readline.o monitor.o pci.o console.o loader.o isa_mmio.o -VL_OBJS+=cutils.o +VL_OBJS+=cutils.o migration.o VL_OBJS+=block.o block-raw.o VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o block-qcow2.o ifdef CONFIG_WIN32 diff -r 8ad5450b580b cpu-all.h --- a/cpu-all.h Tue Jan 16 17:04:55 2007 -0600 +++ b/cpu-all.h Sun Jan 21 17:39:13 2007 -0600 @@ -884,6 +884,7 @@ int cpu_memory_rw_debug(CPUState *env, t #define VGA_DIRTY_FLAG 0x01 #define CODE_DIRTY_FLAG 0x02 +#define MIGRATION_DIRTY_FLAG 0x04 /* read dirty bit (return 0 or 1) */ static inline int cpu_physical_memory_is_dirty(ram_addr_t addr) @@ -905,6 +906,10 @@ void cpu_physical_memory_reset_dirty(ram void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, int dirty_flags); void cpu_tlb_update_dirty(CPUState *env); + +void cpu_physical_memory_set_dirty_tracking(int enable); + +int cpu_physical_memory_get_dirty_tracking(void); void dump_exec_info(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); diff -r 8ad5450b580b cutils.c --- a/cutils.c Tue Jan 16 17:04:55 2007 -0600 +++ b/cutils.c Sun Jan 21 16:58:07 2007 -0600 @@ -81,3 +81,43 @@ int stristart(const char *str, const cha *ptr = p; return 1; } + +int hex2bin(char ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'A' && ch <= 'Z') + return 10 + ch - 'A'; + else if (ch >= 'a' && ch <= 'z') + return 10 + ch - 'a'; + + return -1; +} + +char *urldecode(const char *ptr) +{ + char *ret; + int i; + + ret = qemu_mallocz(strlen(ptr) + 1); + if (ret == NULL) + return NULL; + + for (i = 0; *ptr; ptr++, i++) { + switch (*ptr) { + case '%': + if (ptr[1] == 0 || ptr[2] == 0) + break; + ret[i] = hex2bin(ptr[1]) << 4 | hex2bin(ptr[2]); + ptr += 2; + break; + default: + ret[i] = *ptr; + break; + } + } + ret[i] = 0; + + return ret; +} + diff -r 8ad5450b580b exec.c --- a/exec.c Tue Jan 16 17:04:55 2007 -0600 +++ b/exec.c Sun Jan 21 17:40:00 2007 -0600 @@ -82,6 +82,7 @@ int phys_ram_fd; int phys_ram_fd; uint8_t *phys_ram_base; uint8_t *phys_ram_dirty; +static int in_migration; CPUState *first_cpu; /* current CPU in the current thread. It is only valid inside @@ -1406,6 +1407,16 @@ void cpu_physical_memory_reset_dirty(ram } } #endif +} + +void cpu_physical_memory_set_dirty_tracking(int enable) +{ + in_migration = enable; +} + +int cpu_physical_memory_get_dirty_tracking(void) +{ + return in_migration; } static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry) @@ -2231,6 +2242,14 @@ uint32_t lduw_phys(target_phys_addr_t ad return tswap16(val); } +#ifdef __GNUC__ +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define likely(x) x +#define unlikely(x) x +#endif + /* warning: addr must be aligned. The ram page is not masked as dirty and the code inside is not invalidated. It is useful if the dirty bits are used to track modified PTEs */ @@ -2252,9 +2271,21 @@ void stl_phys_notdirty(target_phys_addr_ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val); } else { - ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + - (addr & ~TARGET_PAGE_MASK); + unsigned long addr1; + addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); + + ptr = phys_ram_base + addr1; stl_p(ptr, val); + + if (unlikely(in_migration)) { + if (!cpu_physical_memory_is_dirty(addr1)) { + /* invalidate code */ + tb_invalidate_phys_page_range(addr1, addr1 + 4, 0); + /* set dirty bit */ + phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |= + (0xff & ~CODE_DIRTY_FLAG); + } + } } } diff -r 8ad5450b580b monitor.c --- a/monitor.c Tue Jan 16 17:04:55 2007 -0600 +++ b/monitor.c Sun Jan 21 16:57:09 2007 -0600 @@ -1253,6 +1253,8 @@ static term_cmd_t term_cmds[] = { "capture index", "stop capture" }, { "memsave", "lis", do_memory_save, "addr size file", "save to disk virtual memory dump starting at 'addr' of size 'size'", }, + { "migrate", "-ds", do_migrate, + "[-d] command", "migrate the VM using command (use -d to not wait for command to complete)" }, { NULL, NULL, }, }; @@ -2428,12 +2430,26 @@ static void term_read(void *opaque, cons readline_handle_byte(buf[i]); } +static int monitor_suspended; + +void monitor_suspend(void) +{ + monitor_suspended = 1; +} + +void monitor_resume(void) +{ + monitor_suspended = 0; + monitor_start_input(); +} + static void monitor_start_input(void); static void monitor_handle_command1(void *opaque, const char *cmdline) { monitor_handle_command(cmdline); - monitor_start_input(); + if (!monitor_suspended) + monitor_start_input(); } static void monitor_start_input(void) diff -r 8ad5450b580b vl.c --- a/vl.c Tue Jan 16 17:04:55 2007 -0600 +++ b/vl.c Sun Jan 21 20:13:13 2007 -0600 @@ -169,6 +169,7 @@ int fd_bootchk = 1; int fd_bootchk = 1; int no_reboot = 0; int daemonize = 0; +int incoming = 0; const char *option_rom[MAX_OPTION_ROMS]; int nb_option_roms; @@ -4764,6 +4765,73 @@ int qemu_loadvm_state(QEMUFile *f) } /* always seek to exact end of record */ qemu_fseek(f, cur_pos + record_len, SEEK_SET); + } + ret = 0; + the_end: + return ret; +} + +int qemu_live_savevm_state(QEMUFile *f) +{ + SaveStateEntry *se; + int len, ret; + + qemu_put_be32(f, QEMU_VM_FILE_MAGIC); + qemu_put_be32(f, QEMU_VM_FILE_VERSION); + + for(se = first_se; se != NULL; se = se->next) { + len = strlen(se->idstr); + + qemu_put_byte(f, len); + qemu_put_buffer(f, se->idstr, len); + qemu_put_be32(f, se->instance_id); + qemu_put_be32(f, se->version_id); + + se->save_state(f, se->opaque); + } + + qemu_put_byte(f, 0); + + ret = 0; + return ret; +} + +int qemu_live_loadvm_state(QEMUFile *f) +{ + SaveStateEntry *se; + int len, ret, instance_id, version_id; + unsigned int v; + char idstr[256]; + + v = qemu_get_be32(f); + if (v != QEMU_VM_FILE_MAGIC) + goto fail; + v = qemu_get_be32(f); + if (v != QEMU_VM_FILE_VERSION) { + fail: + ret = -1; + goto the_end; + } + + for(;;) { + len = qemu_get_byte(f); + if (len == 0) + break; + qemu_get_buffer(f, idstr, len); + idstr[len] = '\0'; + instance_id = qemu_get_be32(f); + version_id = qemu_get_be32(f); + se = find_se(idstr, instance_id); + if (!se) { + fprintf(stderr, "qemu: warning: instance 0x%x of device '%s' not present in current VM\n", + instance_id, idstr); + } else { + ret = se->load_state(f, se->opaque, version_id); + if (ret < 0) { + fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n", + instance_id, idstr); + } + } } ret = 0; the_end: @@ -5589,7 +5657,20 @@ static void ram_decompress_close(RamDeco inflateEnd(&s->zstream); } -static void ram_save(QEMUFile *f, void *opaque) +static void ram_save_live(QEMUFile *f, void *opaque) +{ + target_ulong addr; + + for (addr = 0; addr < phys_ram_size; addr += TARGET_PAGE_SIZE) { + if (cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) { + qemu_put_be32(f, addr); + qemu_put_buffer(f, phys_ram_base + addr, TARGET_PAGE_SIZE); + } + } + qemu_put_be32(f, 1); +} + +static void ram_save_static(QEMUFile *f, void *opaque) { int i; RamCompressState s1, *s = &s1; @@ -5633,16 +5714,39 @@ static void ram_save(QEMUFile *f, void * ram_compress_close(s); } -static int ram_load(QEMUFile *f, void *opaque, int version_id) +static void ram_save(QEMUFile *f, void *opaque) +{ + int in_migration = cpu_physical_memory_get_dirty_tracking(); + + qemu_put_byte(f, in_migration); + + if (in_migration) + ram_save_live(f, opaque); + else + ram_save_static(f, opaque); +} + +static int ram_load_live(QEMUFile *f, void *opaque) +{ + target_ulong addr; + + do { + addr = qemu_get_be32(f); + if (addr == 1) + break; + + qemu_get_buffer(f, phys_ram_base + addr, TARGET_PAGE_SIZE); + } while (1); + + return 0; +} + +static int ram_load_static(QEMUFile *f, void *opaque) { RamDecompressState s1, *s = &s1; uint8_t buf[10]; int i; - if (version_id == 1) - return ram_load_v1(f, opaque); - if (version_id != 2) - return -EINVAL; if (qemu_get_be32(f) != phys_ram_size) return -EINVAL; if (ram_decompress_open(s, f) < 0) @@ -5686,6 +5790,30 @@ static int ram_load(QEMUFile *f, void *o } ram_decompress_close(s); return 0; +} + +static int ram_load(QEMUFile *f, void *opaque, int version_id) +{ + int ret; + + switch (version_id) { + case 1: + ret = ram_load_v1(f, opaque); + break; + case 3: + if (qemu_get_byte(f)) { + ret = ram_load_live(f, opaque); + break; + } + case 2: + ret = ram_load_static(f, opaque); + break; + default: + ret = -EINVAL; + break; + } + + return ret; } /***********************************************************/ @@ -6309,6 +6437,7 @@ enum { QEMU_OPTION_no_reboot, QEMU_OPTION_daemonize, QEMU_OPTION_option_rom, + QEMU_OPTION_incoming, }; typedef struct QEMUOption { @@ -6374,6 +6503,7 @@ const QEMUOption qemu_options[] = { { "serial", 1, QEMU_OPTION_serial }, { "parallel", 1, QEMU_OPTION_parallel }, { "loadvm", HAS_ARG, QEMU_OPTION_loadvm }, + { "incoming", 0, QEMU_OPTION_incoming }, { "full-screen", 0, QEMU_OPTION_full_screen }, #ifdef CONFIG_SDL { "no-quit", 0, QEMU_OPTION_no_quit }, @@ -6594,6 +6724,17 @@ static BOOL WINAPI qemu_ctrl_handler(DWO #endif #define MAX_NET_CLIENTS 32 + +static int saved_argc; +static char **saved_argv; + +void qemu_get_launch_info(int *argc, char ***argv, int *opt_daemonize, int *opt_incoming) +{ + *argc = saved_argc; + *argv = saved_argv; + *opt_daemonize = daemonize; + *opt_incoming = incoming; +} int main(int argc, char **argv) { @@ -6623,6 +6764,9 @@ int main(int argc, char **argv) char usb_devices[MAX_USB_CMDLINE][128]; int usb_devices_index; int fds[2]; + + saved_argc = argc; + saved_argv = argv; LIST_INIT (&vm_change_state_head); #ifndef _WIN32 @@ -6987,6 +7131,9 @@ int main(int argc, char **argv) case QEMU_OPTION_loadvm: loadvm = optarg; break; + case QEMU_OPTION_incoming: + incoming = 1; + break; case QEMU_OPTION_full_screen: full_screen = 1; break; @@ -7057,11 +7204,6 @@ int main(int argc, char **argv) } #ifndef _WIN32 - if (daemonize && !nographic && vnc_display == NULL) { - fprintf(stderr, "Can only daemonize if using -nographic or -vnc\n"); - daemonize = 0; - } - if (daemonize) { pid_t pid; @@ -7096,7 +7238,6 @@ int main(int argc, char **argv) exit(1); umask(027); - chdir("/"); signal(SIGTSTP, SIG_IGN); signal(SIGTTOU, SIG_IGN); @@ -7240,7 +7381,7 @@ int main(int argc, char **argv) } register_savevm("timer", 0, 2, timer_save, timer_load, NULL); - register_savevm("ram", 0, 2, ram_save, ram_load, NULL); + register_savevm("ram", 0, 3, ram_save, ram_load, NULL); init_ioports(); @@ -7322,8 +7463,16 @@ int main(int argc, char **argv) } } else #endif - if (loadvm) - do_loadvm(loadvm); + if (loadvm) { + do_loadvm(loadvm); + } + + if (incoming) { + if (migrate_incoming(STDIN_FILENO) == -1) { + printf("Migration failed\n"); + exit(1); + } + } { /* XXX: simplify init */ @@ -7346,6 +7495,7 @@ int main(int argc, char **argv) if (len != 1) exit(1); + chdir("/"); fd = open("/dev/null", O_RDWR); if (fd == -1) exit(1); diff -r 8ad5450b580b vl.h --- a/vl.h Tue Jan 16 17:04:55 2007 -0600 +++ b/vl.h Sun Jan 21 18:06:27 2007 -0600 @@ -107,8 +107,12 @@ char *pstrcat(char *buf, int buf_size, c char *pstrcat(char *buf, int buf_size, const char *s); int strstart(const char *str, const char *val, const char **ptr); int stristart(const char *str, const char *val, const char **ptr); +int hex2bin(char ch); +char *urldecode(const char *ptr); /* vl.c */ +void qemu_get_launch_info(int *argc, char ***argv, int *opt_daemonize, int *opt_incoming); + uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c); void hw_error(const char *fmt, ...); @@ -436,8 +440,8 @@ typedef void (QEMUFileCloseFunc)(void *o QEMUFile *qemu_fopen(void *opaque, QEMUFilePutBufferFunc *put_buffer, QEMUFileGetBufferFunc *get_buffer, QEMUFileCloseFunc *close); - QEMUFile *qemu_fopen_file(const char *filename, const char *mode); +QEMUFile *qemu_fopen_fd(int fd); void qemu_fflush(QEMUFile *f); void qemu_fclose(QEMUFile *f); void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size); @@ -525,6 +529,9 @@ void do_loadvm(const char *name); void do_loadvm(const char *name); void do_delvm(const char *name); void do_info_snapshots(void); + +int qemu_live_savevm_state(QEMUFile *f); +int qemu_live_loadvm_state(QEMUFile *f); /* bottom halves */ typedef void QEMUBHFunc(void *opaque); @@ -1362,6 +1369,10 @@ pflash_t *pflash_register (target_ulong #endif /* defined(QEMU_TOOL) */ +/* migration.c */ +void do_migrate(int detach, const char *command); +int migrate_incoming(int fd); + /* monitor.c */ void monitor_init(CharDriverState *hd, int show_banner); void term_puts(const char *str); @@ -1372,6 +1383,8 @@ void term_print_help(void); void term_print_help(void); void monitor_readline(const char *prompt, int is_password, char *buf, int buf_size); +void monitor_suspend(void); +void monitor_resume(void); /* readline.c */ typedef void ReadLineFunc(void *opaque, const char *str); diff -r 8ad5450b580b migration.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/migration.c Sun Jan 21 20:52:26 2007 -0600 @@ -0,0 +1,392 @@ +/* + * QEMU migration support + * + * Copyright (C) 2007 Anthony Liguori + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "vl.h" + +#include + +#define MAX_THROTTLE (32 << 20) +#define MIN_FINALIZE_SIZE (200 << 10) + +typedef struct MigrationState +{ + int fd; + pid_t pid; + int detach; + int throttle_count; + int n_buffer; + int *has_error; + char buffer[TARGET_PAGE_SIZE + 4]; + target_ulong addr; + int updated_pages; + QEMUTimer *timer; +} MigrationState; + +/* QEMUFile migration implementation */ + +static void migrate_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) +{ + MigrationState *s = opaque; + int offset = 0; + + if (*s->has_error) + return; + + while (offset < size) { + ssize_t len; + + len = write(s->fd, buf + offset, size - offset); + if (len == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + *s->has_error = 1; + break; + } else if (len == 0) { + *s->has_error = 1; + break; + } + + offset += len; + } +} + +static void migrate_close(void *opaque) +{ + MigrationState *s = opaque; + int status = 0; + + close(s->fd); +again: + if (!s->detach && waitpid(s->pid, &status, 0) == -1) { + if (errno == EINTR || errno == EAGAIN) + goto again; + } + + if (status != 0) + *s->has_error = 1; + + qemu_free(s); +} + +/* Outgoing migration routines */ + +static void migrate_finish(MigrationState *s) +{ + QEMUFile *f; + int ret; + int *has_error = s->has_error; + + fcntl(s->fd, F_SETFL, 0); + + f = qemu_fopen(s, migrate_put_buffer, NULL, migrate_close); + qemu_aio_flush(); + vm_stop(0); + qemu_put_be32(f, 1); + ret = qemu_live_savevm_state(f); + qemu_fclose(f); + if (ret != 0 || *has_error) { + term_printf("Migration failed!\n"); + vm_start(); + } + if (!s->detach) + monitor_resume(); + qemu_free(has_error); + cpu_physical_memory_set_dirty_tracking(0); +} + +static int migrate_write_buffer(MigrationState *s) +{ + if (*s->has_error) + return 0; + + if (s->n_buffer != sizeof(s->buffer)) { + ssize_t len; + again: + len = write(s->fd, s->buffer + s->n_buffer, sizeof(s->buffer) - s->n_buffer); + if (len == -1) { + if (errno == EINTR) + goto again; + if (errno == EAGAIN) + return 1; + *s->has_error = 1; + return 0; + } + if (len == 0) { + *s->has_error = 1; + return 0; + } + + s->throttle_count += len; + s->n_buffer += len; + if (s->n_buffer != sizeof(s->buffer)) + goto again; + } + + if (s->throttle_count > MAX_THROTTLE) { + printf("exceeded throttle count\n"); + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + return 1; + } + + return 0; +} + +static int migrate_check_convergence(MigrationState *s) +{ + target_ulong addr; + int dirty_count = 0; + + for (addr = 0; addr < phys_ram_size; addr += TARGET_PAGE_SIZE) { + if (cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) + dirty_count++; + } + + return ((dirty_count * TARGET_PAGE_SIZE) < MIN_FINALIZE_SIZE); +} + +static void migrate_write(void *opaque) +{ + MigrationState *s = opaque; + + if (migrate_write_buffer(s)) + return; + + if (migrate_check_convergence(s)) { + qemu_del_timer(s->timer); + qemu_free_timer(s->timer); + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + migrate_finish(s); + return; + } + + while (s->addr < phys_ram_size) { + if (cpu_physical_memory_get_dirty(s->addr, MIGRATION_DIRTY_FLAG)) { + uint32_t value = cpu_to_be32(s->addr); + + memcpy(s->buffer, &value, 4); + memcpy(s->buffer + 4, phys_ram_base + s->addr, TARGET_PAGE_SIZE); + s->n_buffer = 0; + + cpu_physical_memory_reset_dirty(s->addr, s->addr + TARGET_PAGE_SIZE, MIGRATION_DIRTY_FLAG); + + s->addr += TARGET_PAGE_SIZE; + + if (migrate_write_buffer(s)) + return; + } else + s->addr += TARGET_PAGE_SIZE; + } + + s->updated_pages = 0; + s->addr = 0; +} + +static void migrate_reset_throttle(void *opaque) +{ + MigrationState *s = opaque; + + if (s->throttle_count > MAX_THROTTLE) { + qemu_set_fd_handler2(s->fd, NULL, NULL, migrate_write, s); + } + s->throttle_count = 0; + qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000); +} + +static int start_migration(MigrationState *s) +{ + uint32_t value = cpu_to_be32(phys_ram_size); + target_phys_addr_t addr; + size_t offset = 0; + + while (offset != 4) { + ssize_t len = write(s->fd, ((char *)&value) + offset, 4 - offset); + if (len == -1 && errno == EINTR) + continue; + + if (len < 1) + return -EIO; + + offset += len; + } + + fcntl(s->fd, F_SETFL, O_NONBLOCK); + + for (addr = 0; addr < phys_ram_size; addr += TARGET_PAGE_SIZE) { + if (!cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) + cpu_physical_memory_set_dirty(addr); + } + + cpu_physical_memory_set_dirty_tracking(1); + + s->updated_pages = 0; + s->addr = 0; + s->n_buffer = sizeof(s->buffer); + s->timer = qemu_new_timer(rt_clock, migrate_reset_throttle, s); + + qemu_mod_timer(s->timer, qemu_get_clock(rt_clock)); + qemu_set_fd_handler2(s->fd, NULL, NULL, migrate_write, s); +} + +/* Incoming migration */ + +int migrate_incoming(int fd) +{ + int ret; + QEMUFile *f = qemu_fopen_fd(STDIN_FILENO); + uint32_t addr; + + if (qemu_get_be32(f) != phys_ram_size) + return -1; + + do { + int l; + addr = qemu_get_be32(f); + if (addr == 1) + break; + l = qemu_get_buffer(f, phys_ram_base + addr, TARGET_PAGE_SIZE); + if (l != TARGET_PAGE_SIZE) + return -1; + } while (1); + + qemu_aio_flush(); + vm_stop(0); + ret = qemu_live_loadvm_state(f); + qemu_fclose(f); + + return ret; +} + +/* Migration monitor command */ + +void do_migrate(int detach, const char *uri) +{ + MigrationState *s; + int fds[2]; + const char *ptr; + char *command = NULL; + char **argv = NULL; + int i; + + if (strstart(uri, "exec:", &ptr)) { + char *subcommand = urldecode(ptr); + if (subcommand == NULL) { + term_printf("Allocation error\n"); + return; + } + + command = strdup("/bin/sh"); + argv = qemu_mallocz(sizeof(char *) * 4); + argv[0] = strdup("sh"); + argv[1] = strdup("-c"); + argv[2] = subcommand; + argv[3] = NULL; + } else if (strstart(uri, "ssh:", &ptr)) { + char *host, *end; + int qemu_argc, daemonize = 0, incoming = 0, argc, i; + char **qemu_argv; + + if (ptr[0] != '/' && ptr[1] != '/') { + term_printf("Malformed ssh uri\n"); + return; + } + + qemu_get_launch_info(&qemu_argc, &qemu_argv, &daemonize, &incoming); + + argc = 3 + qemu_argc; + if (!daemonize) + argc++; + if (!incoming) + argc++; + + host = strdup(ptr + 2); + end = strchr(host, '/'); + if (end) *end = 0; + + command = strdup("ssh"); + argv = qemu_mallocz(sizeof(char *) * (argc + 1)); + argv[0] = strdup("ssh"); + argv[1] = strdup("-XC"); + argv[2] = host; + + for (i = 0; i < qemu_argc; i++) + argv[3 + i] = strdup(qemu_argv[i]); + + if (!daemonize) + argv[3 + i++] = strdup("-daemonize"); + if (!incoming) + argv[3 + i++] = strdup("-incoming"); + argv[3 + i] = NULL; + } else { + term_printf("Unknown migration protocol '%s'\n", uri); + return; + } + + if (pipe(fds) == -1) { + term_printf("pipe()\n"); + return; + } + +#if 0 + term_printf("execlp('%s'", command); + for (i = 0; argv[i]; i++) { + term_printf(", '%s'", argv[i]); + } + term_printf(", NULL);\n"); + return; +#endif + + s = qemu_mallocz(sizeof(MigrationState)); + if (s == NULL) { + term_printf("Allocation error\n"); + return; + } + + s->fd = fds[1]; + s->pid = fork(); + if (s->pid == -1) { + term_printf("fork error\n"); + return; + } + if (s->pid == 0) { + close(fds[1]); + dup2(fds[0], STDIN_FILENO); + execvp(command, argv); + exit(1); + } else + close(fds[0]); + + s->detach = detach; + s->has_error = qemu_mallocz(sizeof(int)); +#if 1 + qemu_free(command); + for (i = 0; argv[i]; i++) + qemu_free(argv[i]); + qemu_free(argv); +#endif + + if (start_migration(s) == -1) + term_printf("Could not start migration\n"); + else if (!s->detach) + monitor_suspend(); +}