X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=src%2Flaunch.c;h=e5bca56e8d3572c34a35fc602ca3fe2797912dc7;hp=e0ad165fc30ceb38b77cb2ff2a1c21c541f6dd83;hb=06fef60db5c7a96cb59aa92c4708e10333345e90;hpb=6d276dae8d1bbb54d8708c94d23879d39f5fd4a3;ds=sidebyside diff --git a/src/launch.c b/src/launch.c index e0ad165..e5bca56 100644 --- a/src/launch.c +++ b/src/launch.c @@ -61,6 +61,7 @@ #include #include +#include "c-ctype.h" #include "glthread/lock.h" #include "guestfs.h" @@ -131,64 +132,108 @@ guestfs__config (guestfs_h *g, return 0; } -int -guestfs__add_drive_with_if (guestfs_h *g, const char *filename, - const char *drive_if) +/* cache=off improves reliability in the event of a host crash. + * + * However this option causes qemu to try to open the file with + * O_DIRECT. This fails on some filesystem types (notably tmpfs). + * So we check if we can open the file with or without O_DIRECT, + * and use cache=off (or not) accordingly. + */ +static int +test_cache_off (guestfs_h *g, const char *filename) { - size_t len = strlen (filename) + 64; - char buf[len]; - - if (strchr (filename, ',') != NULL) { - error (g, _("filename cannot contain ',' (comma) character")); - return -1; + int fd = open (filename, O_RDONLY|O_DIRECT); + if (fd >= 0) { + close (fd); + return 1; } - /* cache=off improves reliability in the event of a host crash. - * - * However this option causes qemu to try to open the file with - * O_DIRECT. This fails on some filesystem types (notably tmpfs). - * So we check if we can open the file with or without O_DIRECT, - * and use cache=off (or not) accordingly. - * - * This test also checks for the presence of the file, which - * is a documented semantic of this interface. - */ - int fd = open (filename, O_RDONLY|O_DIRECT); + fd = open (filename, O_RDONLY); if (fd >= 0) { close (fd); - snprintf (buf, len, "file=%s,cache=off,if=%s", filename, drive_if); - } else { - fd = open (filename, O_RDONLY); - if (fd >= 0) { - close (fd); - snprintf (buf, len, "file=%s,if=%s", filename, drive_if); - } else { - perrorf (g, "%s", filename); - return -1; - } + return 0; } - return guestfs__config (g, "-drive", buf); + perrorf (g, "%s", filename); + return -1; +} + +/* Check string parameter matches ^[-_[:alnum:]]+$ (in C locale). */ +static int +valid_format_iface (const char *str) +{ + size_t len = strlen (str); + + if (len == 0) + return 0; + + while (len > 0) { + char c = *str++; + len--; + if (c != '-' && c != '_' && !c_isalnum (c)) + return 0; + } + return 1; } int -guestfs__add_drive_ro_with_if (guestfs_h *g, const char *filename, - const char *drive_if) +guestfs__add_drive_opts (guestfs_h *g, const char *filename, + const struct guestfs_add_drive_opts_argv *optargs) { + int readonly; + const char *format; + const char *iface; + if (strchr (filename, ',') != NULL) { error (g, _("filename cannot contain ',' (comma) character")); return -1; } - if (access (filename, F_OK) == -1) { - perrorf (g, "%s", filename); + readonly = optargs->bitmask & GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK + ? optargs->readonly : 0; + format = optargs->bitmask & GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK + ? optargs->format : NULL; + iface = optargs->bitmask & GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK + ? optargs->iface : DRIVE_IF; + + if (format && !valid_format_iface (format)) { + error (g, _("%s parameter is empty or contains disallowed characters"), + "format"); + return -1; + } + if (!valid_format_iface (iface)) { + error (g, _("%s parameter is empty or contains disallowed characters"), + "iface"); return -1; } - size_t len = strlen (filename) + 64; + /* For writable files, see if we can use cache=off. This also + * checks for the existence of the file. For readonly we have + * to do the check explicitly. + */ + int use_cache_off = readonly ? 0 : test_cache_off (g, filename); + if (use_cache_off == -1) + return -1; + + if (readonly) { + if (access (filename, F_OK) == -1) { + perrorf (g, "%s", filename); + return -1; + } + } + + /* Construct the final -drive parameter. */ + size_t len = 64 + strlen (filename) + strlen (iface); + if (format) len += strlen (format); char buf[len]; - snprintf (buf, len, "file=%s,snapshot=on,if=%s", filename, drive_if); + snprintf (buf, len, "file=%s%s%s%s%s,if=%s", + filename, + readonly ? ",snapshot=on" : "", + use_cache_off ? ",cache=off" : "", + format ? ",format=" : "", + format ? format : "", + iface); return guestfs__config (g, "-drive", buf); } @@ -196,13 +241,48 @@ guestfs__add_drive_ro_with_if (guestfs_h *g, const char *filename, int guestfs__add_drive (guestfs_h *g, const char *filename) { - return guestfs__add_drive_with_if (g, filename, DRIVE_IF); + struct guestfs_add_drive_opts_argv optargs = { + .bitmask = 0, + }; + + return guestfs__add_drive_opts (g, filename, &optargs); } int guestfs__add_drive_ro (guestfs_h *g, const char *filename) { - return guestfs__add_drive_ro_with_if (g, filename, DRIVE_IF); + struct guestfs_add_drive_opts_argv optargs = { + .bitmask = GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK, + .readonly = 1, + }; + + return guestfs__add_drive_opts (g, filename, &optargs); +} + +int +guestfs__add_drive_with_if (guestfs_h *g, const char *filename, + const char *iface) +{ + struct guestfs_add_drive_opts_argv optargs = { + .bitmask = GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK, + .iface = iface, + }; + + return guestfs__add_drive_opts (g, filename, &optargs); +} + +int +guestfs__add_drive_ro_with_if (guestfs_h *g, const char *filename, + const char *iface) +{ + struct guestfs_add_drive_opts_argv optargs = { + .bitmask = GUESTFS_ADD_DRIVE_OPTS_IFACE_BITMASK + | GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK, + .iface = iface, + .readonly = 1, + }; + + return guestfs__add_drive_opts (g, filename, &optargs); } int @@ -222,7 +302,6 @@ guestfs__add_cdrom (guestfs_h *g, const char *filename) } static int is_openable (guestfs_h *g, const char *path, int flags); -static void print_cmdline (guestfs_h *g); int guestfs__launch (guestfs_h *g) @@ -249,10 +328,7 @@ guestfs__launch (guestfs_h *g) /* Make the temporary directory. */ if (!g->tmpdir) { - const char *tmpdir = guestfs___tmpdir (); - char dir_template[strlen (tmpdir) + 32]; - sprintf (dir_template, "%s/libguestfsXXXXXX", tmpdir); - + TMP_TEMPLATE_ON_STACK (dir_template); g->tmpdir = safe_strdup (g, dir_template); if (mkdtemp (g->tmpdir) == NULL) { perrorf (g, _("%s: cannot create temporary directory"), dir_template); @@ -414,7 +490,7 @@ guestfs__launch (guestfs_h *g) /* Enable user networking. */ if (g->enable_network) { add_cmdline (g, "-netdev"); - add_cmdline (g, "user,id=usernet"); + add_cmdline (g, "user,id=usernet,net=169.254.0.0/16"); add_cmdline (g, "-device"); add_cmdline (g, NET_IF ",netdev=usernet"); } @@ -469,7 +545,7 @@ guestfs__launch (guestfs_h *g) g->cmdline[g->cmdline_size-1] = NULL; if (g->verbose) - print_cmdline (g); + guestfs___print_timestamped_argv (g, (const char **)g->cmdline); if (!g->direct) { /* Set up stdin, stdout. */ @@ -649,8 +725,11 @@ guestfs__launch (guestfs_h *g) return -1; } +/* Return the location of the tmpdir (eg. "/tmp") and allow users + * to override it at runtime using $TMPDIR. + */ const char * -guestfs___tmpdir (void) +guestfs_tmpdir (void) { const char *tmpdir; @@ -666,26 +745,41 @@ guestfs___tmpdir (void) return tmpdir; } -/* This function is used to print the qemu command line before it gets - * executed, when in verbose mode. +/* Compute Y - X and return the result in milliseconds. + * Approximately the same as this code: + * http://www.mpp.mpg.de/~huber/util/timevaldiff.c */ -static void -print_cmdline (guestfs_h *g) +static int64_t +timeval_diff (const struct timeval *x, const struct timeval *y) +{ + int64_t msec; + + msec = (y->tv_sec - x->tv_sec) * 1000; + msec += (y->tv_usec - x->tv_usec) / 1000; + return msec; +} + +void +guestfs___print_timestamped_argv (guestfs_h *g, const char * argv[]) { int i = 0; int needs_quote; - while (g->cmdline[i]) { - if (g->cmdline[i][0] == '-') /* -option starts a new line */ + struct timeval tv; + gettimeofday (&tv, NULL); + fprintf (stderr, "[%05" PRIi64 "ms] ", timeval_diff (&g->launch_t, &tv)); + + while (argv[i]) { + if (argv[i][0] == '-') /* -option starts a new line */ fprintf (stderr, " \\\n "); if (i > 0) fputc (' ', stderr); /* Does it need shell quoting? This only deals with simple cases. */ - needs_quote = strcspn (g->cmdline[i], " ") != strlen (g->cmdline[i]); + needs_quote = strcspn (argv[i], " ") != strlen (argv[i]); if (needs_quote) fputc ('\'', stderr); - fprintf (stderr, "%s", g->cmdline[i]); + fprintf (stderr, "%s", argv[i]); if (needs_quote) fputc ('\'', stderr); i++; } @@ -693,20 +787,6 @@ print_cmdline (guestfs_h *g) fputc ('\n', stderr); } -/* Compute Y - X and return the result in milliseconds. - * Approximately the same as this code: - * http://www.mpp.mpg.de/~huber/util/timevaldiff.c - */ -static int64_t -timeval_diff (const struct timeval *x, const struct timeval *y) -{ - int64_t msec; - - msec = (y->tv_sec - x->tv_sec) * 1000; - msec += (y->tv_usec - x->tv_usec) / 1000; - return msec; -} - void guestfs___print_timestamped_message (guestfs_h *g, const char *fs, ...) {