X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Fguestfs.c;h=350d84817157a25f61f2dbc5bdac0688063d1902;hp=8f06d3ba4671f61fd72c6813518bcc8802d85c8a;hb=e9f5144537a4d89cc875bce42b269d1e2b119d8f;hpb=414aa67f2bcbbc5009b92f32611aa9196836736b diff --git a/src/guestfs.c b/src/guestfs.c index 8f06d3b..350d848 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -1,5 +1,5 @@ /* libguestfs - * Copyright (C) 2009 Red Hat Inc. + * Copyright (C) 2009 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -162,6 +163,8 @@ struct guestfs_h char *tmpdir; /* Temporary directory containing socket. */ + char *qemu_help, *qemu_version; /* Output of qemu -help, qemu -version. */ + char **cmdline; /* Qemu command line. */ int cmdline_size; @@ -172,6 +175,8 @@ struct guestfs_h char *qemu; /* Qemu binary. */ char *append; /* Append to kernel command line. */ + int memsize; /* Size of RAM (megabytes). */ + char *last_error; /* Callbacks. */ @@ -244,6 +249,22 @@ guestfs_create (void) if (!g->append) goto error; } + /* Choose a suitable memory size. Previously we tried to choose + * a minimal memory size, but this isn't really necessary since + * recent QEMU and KVM don't do anything nasty like locking + * memory into core any more. Thus we can safely choose a + * large, generous amount of memory, and it'll just get swapped + * on smaller systems. + */ + str = getenv ("LIBGUESTFS_MEMSIZE"); + if (str) { + if (sscanf (str, "%d", &g->memsize) != 1 || g->memsize <= 256) { + fprintf (stderr, "libguestfs: non-numeric or too small value for LIBGUESTFS_MEMSIZE\n"); + goto error; + } + } else + g->memsize = 500; + g->main_loop = guestfs_get_default_main_loop (); /* Start with large serial numbers so they are easy to spot @@ -346,6 +367,8 @@ guestfs_close (guestfs_h *g) free (g->path); free (g->qemu); free (g->append); + free (g->qemu_help); + free (g->qemu_version); free (g); } @@ -382,9 +405,11 @@ guestfs_error (guestfs_h *g, const char *fs, ...) char *msg; va_start (args, fs); - vasprintf (&msg, fs, args); + int err = vasprintf (&msg, fs, args); va_end (args); + if (err < 0) return; + if (g->error_cb) g->error_cb (g, g->error_cb_data, msg); set_last_error (g, msg); @@ -429,6 +454,42 @@ guestfs_safe_malloc (guestfs_h *g, size_t nbytes) return ptr; } +/* Return 1 if an array of N objects, each of size S, cannot exist due + to size arithmetic overflow. S must be positive and N must be + nonnegative. This is a macro, not an inline function, so that it + works correctly even when SIZE_MAX < N. + + By gnulib convention, SIZE_MAX represents overflow in size + calculations, so the conservative dividend to use here is + SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value. + However, malloc (SIZE_MAX) fails on all known hosts where + sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for + exactly-SIZE_MAX allocations on such hosts; this avoids a test and + branch when S is known to be 1. */ +# define xalloc_oversized(n, s) \ + ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n)) + +/* Technically we should add an autoconf test for this, testing for the desired + functionality, like what's done in gnulib, but for now, this is fine. */ +#define HAVE_GNU_CALLOC (__GLIBC__ >= 2) + +/* Allocate zeroed memory for N elements of S bytes, with error + checking. S must be nonzero. */ +void * +guestfs_safe_calloc (guestfs_h *g, size_t n, size_t s) +{ + /* From gnulib's calloc function in xmalloc.c. */ + void *p; + /* Test for overflow, since some calloc implementations don't have + proper overflow checks. But omit overflow and size-zero tests if + HAVE_GNU_CALLOC, since GNU calloc catches overflow and never + returns NULL if successful. */ + if ((! HAVE_GNU_CALLOC && xalloc_oversized (n, s)) + || (! (p = calloc (n, s)) && (HAVE_GNU_CALLOC || n != 0))) + g->abort_cb (); + return p; +} + void * guestfs_safe_realloc (guestfs_h *g, void *ptr, int nbytes) { @@ -593,6 +654,19 @@ guestfs_get_append (guestfs_h *g) return g->append; } +int +guestfs_set_memsize (guestfs_h *g, int memsize) +{ + g->memsize = memsize; + return 0; +} + +int +guestfs_get_memsize (guestfs_h *g) +{ + return g->memsize; +} + /* Add a string to the current command line. */ static void incr_cmdline_size (guestfs_h *g) @@ -638,7 +712,6 @@ guestfs_config (guestfs_h *g, strcmp (qemu_param, "-initrd") == 0 || strcmp (qemu_param, "-nographic") == 0 || strcmp (qemu_param, "-serial") == 0 || - strcmp (qemu_param, "-vnc") == 0 || strcmp (qemu_param, "-full-screen") == 0 || strcmp (qemu_param, "-std-vga") == 0 || strcmp (qemu_param, "-vnc") == 0) { @@ -671,7 +744,8 @@ guestfs_add_drive (guestfs_h *g, const char *filename) return -1; } - snprintf (buf, len, "file=%s", filename); + /* cache=off improves reliability in the event of a host crash. */ + snprintf (buf, len, "file=%s,cache=off,if=%s", filename, DRIVE_IF); return guestfs_config (g, "-drive", buf); } @@ -692,7 +766,7 @@ guestfs_add_drive_ro (guestfs_h *g, const char *filename) return -1; } - snprintf (buf, len, "file=%s,snapshot=on", filename); + snprintf (buf, len, "file=%s,snapshot=on,if=%s", filename, DRIVE_IF); return guestfs_config (g, "-drive", buf); } @@ -745,6 +819,8 @@ dir_contains_files (const char *dir, ...) } static int build_supermin_appliance (guestfs_h *g, const char *path, char **kernel, char **initrd); +static int test_qemu (guestfs_h *g); +static int qemu_supports (guestfs_h *g, const char *option); static const char *kernel_name = "vmlinuz." REPO "." host_cpu; static const char *initrd_name = "initramfs." REPO "." host_cpu ".img"; @@ -757,7 +833,7 @@ int guestfs_launch (guestfs_h *g) { static const char *dir_template = "/tmp/libguestfsXXXXXX"; - int r, i, pmore, memsize; + int r, i, pmore; size_t len; int wfd[2], rfd[2]; int tries; @@ -876,14 +952,9 @@ guestfs_launch (guestfs_h *g) goto cleanup0; } - /* Choose a suitable memory size. Previously we tried to choose - * a minimal memory size, but this isn't really necessary since - * recent QEMU and KVM don't do anything nasty like locking - * memory into core any more. This we can safely choose a - * large, generous amount of memory, and it'll just get swapped - * on smaller systems. - */ - memsize = 384; + /* Get qemu help text and version. */ + if (test_qemu (g) == -1) + goto cleanup0; /* Make the vmchannel socket. */ snprintf (unixsock, sizeof unixsock, "%s/sock", g->tmpdir); @@ -926,13 +997,10 @@ guestfs_launch (guestfs_h *g) g->verbose ? " guestfs_verbose=1" : "", g->append ? " " : "", g->append ? g->append : ""); - snprintf (memsize_str, sizeof memsize_str, "%d", memsize); + snprintf (memsize_str, sizeof memsize_str, "%d", g->memsize); add_cmdline (g, "-m"); add_cmdline (g, memsize_str); -#if 0 - add_cmdline (g, "-no-kqemu"); /* Avoids a warning. */ -#endif add_cmdline (g, "-no-reboot"); /* Force exit instead of reboot on panic */ add_cmdline (g, "-kernel"); add_cmdline (g, (char *) kernel); @@ -949,6 +1017,15 @@ guestfs_launch (guestfs_h *g) add_cmdline (g, "user,vlan=0"); add_cmdline (g, "-net"); add_cmdline (g, "nic,model=virtio,vlan=0"); + + /* These options recommended by KVM developers to improve reliability. */ + if (qemu_supports (g, "-no-hpet")) + add_cmdline (g, "-no-hpet"); + + if (qemu_supports (g, "-rtc-td-hack")) + add_cmdline (g, "-rtc-td-hack"); + + /* Finish off the command line. */ incr_cmdline_size (g); g->cmdline[g->cmdline_size-1] = NULL; @@ -1148,23 +1225,116 @@ build_supermin_appliance (guestfs_h *g, const char *path, char **kernel, char **initrd) { char cmd[4096]; - int r; + int r, len; + + len = strlen (g->tmpdir); + *kernel = safe_malloc (g, len + 8); + snprintf (*kernel, len+8, "%s/kernel", g->tmpdir); + *initrd = safe_malloc (g, len + 8); + snprintf (*initrd, len+8, "%s/initrd", g->tmpdir); snprintf (cmd, sizeof cmd, "PATH='%s':$PATH " - "guestfs-supermin-helper '%s' %s/kernel %s/initrd", + "libguestfs-supermin-helper '%s' %s %s", path, - path, g->tmpdir, g->tmpdir); + path, *kernel, *initrd); r = system (cmd); if (r == -1 || WEXITSTATUS(r) != 0) { error (g, _("external command failed: %s"), cmd); + free (*kernel); + free (*initrd); + *kernel = *initrd = NULL; + return -1; + } + + return 0; +} + +static int read_all (guestfs_h *g, FILE *fp, char **ret); + +/* Test qemu binary (or wrapper) runs, and do 'qemu -help' and + * 'qemu -version' so we know what options this qemu supports and + * the version. + */ +static int +test_qemu (guestfs_h *g) +{ + char cmd[1024]; + FILE *fp; + + free (g->qemu_help); + free (g->qemu_version); + g->qemu_help = NULL; + g->qemu_version = NULL; + + snprintf (cmd, sizeof cmd, "'%s' -help", g->qemu); + + fp = popen (cmd, "r"); + /* qemu -help should always work (qemu -version OTOH wasn't + * supported by qemu 0.9). If this command doesn't work then it + * probably indicates that the qemu binary is missing. + */ + if (!fp) { + /* XXX This error is never printed, even if the qemu binary + * doesn't exist. Why? + */ + error: + perrorf (g, _("%s: command failed: If qemu is located on a non-standard path, try setting the LIBGUESTFS_QEMU environment variable."), cmd); return -1; } + if (read_all (g, fp, &g->qemu_help) == -1) + goto error; + + if (pclose (fp) == -1) + goto error; + + snprintf (cmd, sizeof cmd, "'%s' -version 2>/dev/null", g->qemu); + + fp = popen (cmd, "r"); + if (fp) { + /* Intentionally ignore errors. */ + read_all (g, fp, &g->qemu_version); + pclose (fp); + } + return 0; } +static int +read_all (guestfs_h *g, FILE *fp, char **ret) +{ + int r, n = 0; + char *p; + + again: + if (feof (fp)) { + *ret = safe_realloc (g, *ret, n + 1); + (*ret)[n] = '\0'; + return n; + } + + *ret = safe_realloc (g, *ret, n + BUFSIZ); + p = &(*ret)[n]; + r = fread (p, 1, BUFSIZ, fp); + if (ferror (fp)) { + perrorf (g, "read"); + return -1; + } + n += r; + goto again; +} + +/* Test if option is supported by qemu command line (just by grepping + * the help text). + */ +static int +qemu_supports (guestfs_h *g, const char *option) +{ + return g->qemu_help && strstr (g->qemu_help, option) != NULL; +} + static void finish_wait_ready (guestfs_h *g, void *vp) { @@ -1340,6 +1510,13 @@ guestfs_free_lvm_lv_list (struct guestfs_lvm_lv_list *x) free (x); } +void +guestfs_free_dirent_list (struct guestfs_dirent_list *x) +{ + xdr_free ((xdrproc_t) xdr_guestfs_int_dirent_list, (char *) x); + free (x); +} + /* We don't know if stdout_event or sock_read_event will be the * first to receive EOF if the qemu process dies. This function * has the common cleanup code for both. @@ -1568,7 +1745,7 @@ static void sock_write_event (struct guestfs_main_loop *ml, guestfs_h *g, void *data, int watch, int fd, int events) { - int n; + int n, err; if (g->verbose) fprintf (stderr, @@ -1592,8 +1769,11 @@ sock_write_event (struct guestfs_main_loop *ml, guestfs_h *g, void *data, n = write (g->sock, g->msg_out + g->msg_out_pos, g->msg_out_size - g->msg_out_pos); if (n == -1) { - if (errno != EAGAIN) + err = errno; + if (err != EAGAIN) perrorf (g, "write"); + if (err == EPIPE) /* Disconnected from guest (RHBZ#508713). */ + child_cleanup (g); return; }