X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=src%2Flaunch.c;h=8e171f7b28ab464839b46ab29379ebf905c1348c;hb=049116e1810680275a42faba430fb2f798cb17d8;hp=a2a9beaa53f5913e0e6bd51dcd73a8fd34536818;hpb=70daccfe4e9939078436c2830928d8e53432dd84;p=libguestfs.git diff --git a/src/launch.c b/src/launch.c index a2a9bea..8e171f7 100644 --- a/src/launch.c +++ b/src/launch.c @@ -63,6 +63,7 @@ #include #include "c-ctype.h" +#include "ignore-value.h" #include "glthread/lock.h" #include "guestfs.h" @@ -72,9 +73,38 @@ static int launch_appliance (guestfs_h *g); static int64_t timeval_diff (const struct timeval *x, const struct timeval *y); +static void print_qemu_command_line (guestfs_h *g, char **argv); static int connect_unix_socket (guestfs_h *g, const char *sock); static int qemu_supports (guestfs_h *g, const char *option); +#if 0 +static int qemu_supports_re (guestfs_h *g, const pcre *option_regex); + +static void compile_regexps (void) __attribute__((constructor)); +static void free_regexps (void) __attribute__((destructor)); + +static void +compile_regexps (void) +{ + const char *err; + int offset; + +#define COMPILE(re,pattern,options) \ + do { \ + re = pcre_compile ((pattern), (options), &err, &offset, NULL); \ + if (re == NULL) { \ + ignore_value (write (2, err, strlen (err))); \ + abort (); \ + } \ + } while (0) +} + +static void +free_regexps (void) +{ +} +#endif + /* Add a string to the current command line. */ static void incr_cmdline_size (guestfs_h *g) @@ -359,6 +389,8 @@ guestfs__launch (guestfs_h *g) return -1; } + TRACE0 (launch_start); + /* Make the temporary directory. */ if (!g->tmpdir) { TMP_TEMPLATE_ON_STACK (dir_template); @@ -409,11 +441,15 @@ launch_appliance (guestfs_h *g) gettimeofday (&g->launch_t, NULL); guestfs___launch_send_progress (g, 0); + TRACE0 (launch_build_appliance_start); + /* Locate and/or build the appliance. */ char *kernel = NULL, *initrd = NULL, *appliance = NULL; if (guestfs___build_appliance (g, &kernel, &initrd, &appliance) == -1) return -1; + TRACE0 (launch_build_appliance_end); + guestfs___launch_send_progress (g, 3); if (g->verbose) @@ -525,6 +561,12 @@ launch_appliance (guestfs_h *g) add_cmdline (g, "-nographic"); + if (g->smp > 1) { + snprintf (buf, sizeof buf, "%d", g->smp); + add_cmdline (g, "-smp"); + add_cmdline (g, buf); + } + snprintf (buf, sizeof buf, "%d", g->memsize); add_cmdline (g, "-m"); add_cmdline (g, buf); @@ -576,7 +618,7 @@ launch_appliance (guestfs_h *g) "panic=1 " /* force kernel to panic if daemon exits */ \ "console=ttyS0 " /* serial console */ \ "udevtimeout=300 " /* good for very slow systems (RHBZ#480319) */ \ - "noapic " /* workaround for RHBZ#502058 - ok if not SMP */ \ + "no_timer_check " /* fix for RHBZ#502058 */ \ "acpi=off " /* we don't need ACPI, turn it off */ \ "printk.time=1 " /* display timestamp before kernel messages */ \ "cgroup_disable=memory " /* saves us about 5 MB of RAM */ @@ -621,11 +663,8 @@ launch_appliance (guestfs_h *g) incr_cmdline_size (g); g->cmdline[g->cmdline_size-1] = NULL; - if (g->verbose) - guestfs___print_timestamped_argv (g, (const char **)g->cmdline); - if (!g->direct) { - /* Set up stdin, stdout. */ + /* Set up stdin, stdout, stderr. */ close (0); close (1); close (wfd[1]); @@ -641,16 +680,30 @@ launch_appliance (guestfs_h *g) if (dup (rfd[1]) == -1) goto dup_failed; + /* Particularly since qemu 0.15, qemu spews all sorts of debug + * information on stderr. It is useful to both capture this and + * not confuse casual users, so send stderr to the pipe as well. + */ + close (2); + if (dup (rfd[1]) == -1) + goto dup_failed; + close (wfd[0]); close (rfd[1]); } + /* Dump the command line (after setting up stderr above). */ + if (g->verbose) + print_qemu_command_line (g, g->cmdline); + /* Put qemu in a new process group. */ if (g->pgroup) setpgid (0, 0); setenv ("LC_ALL", "C", 1); + TRACE0 (launch_run_qemu); + execv (g->qemu, g->cmdline); /* Run qemu. */ perror (g->qemu); _exit (EXIT_FAILURE); @@ -685,7 +738,7 @@ launch_appliance (guestfs_h *g) setpgid (0, 0); /* Writing to argv is hideously complicated and error prone. See: - * http://anoncvs.postgresql.org/cvsweb.cgi/pgsql/src/backend/utils/misc/ps_status.c?rev=1.33.2.1;content-type=text%2Fplain + * http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/misc/ps_status.c;hb=HEAD */ /* Loop around waiting for one or both of the other processes to @@ -780,6 +833,8 @@ launch_appliance (guestfs_h *g) goto cleanup1; } + TRACE0 (launch_end); + guestfs___launch_send_progress (g, 12); return 0; @@ -965,47 +1020,9 @@ timeval_diff (const struct timeval *x, const struct timeval *y) return msec; } -void -guestfs___print_timestamped_argv (guestfs_h *g, const char * argv[]) -{ - int i = 0; - int needs_quote; - char *buf = NULL; - size_t len; - FILE *fp; - - fp = open_memstream (&buf, &len); - if (fp == NULL) { - warning (g, "open_memstream: %m"); - return; - } - - struct timeval tv; - gettimeofday (&tv, NULL); - fprintf (fp, "[%05" PRIi64 "ms] ", timeval_diff (&g->launch_t, &tv)); - - while (argv[i]) { - if (argv[i][0] == '-') /* -option starts a new line */ - fprintf (fp, " \\\n "); - - if (i > 0) fputc (' ', fp); - - /* Does it need shell quoting? This only deals with simple cases. */ - needs_quote = strcspn (argv[i], " ") != strlen (argv[i]); - - if (needs_quote) fputc ('\'', fp); - fprintf (fp, "%s", argv[i]); - if (needs_quote) fputc ('\'', fp); - i++; - } - - fclose (fp); - - debug (g, "%s", buf); - - free (buf); -} - +/* Note that since this calls 'debug' it should only be called + * from the parent process. + */ void guestfs___print_timestamped_message (guestfs_h *g, const char *fs, ...) { @@ -1027,6 +1044,37 @@ guestfs___print_timestamped_message (guestfs_h *g, const char *fs, ...) free (msg); } +/* This is called from the forked subprocess just before qemu runs, so + * it can just print the message straight to stderr, where it will be + * picked up and funnelled through the usual appliance event API. + */ +static void +print_qemu_command_line (guestfs_h *g, char **argv) +{ + int i = 0; + int needs_quote; + + 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 (argv[i], " ") != strlen (argv[i]); + + if (needs_quote) fputc ('\'', stderr); + fprintf (stderr, "%s", argv[i]); + if (needs_quote) fputc ('\'', stderr); + i++; + } +} + +static int test_qemu_cmd (guestfs_h *g, const char *cmd, char **ret); static int read_all (guestfs_h *g, FILE *fp, char **ret); /* Test qemu binary (or wrapper) runs, and do 'qemu -help' and @@ -1039,38 +1087,49 @@ test_qemu (guestfs_h *g) char cmd[1024]; FILE *fp; + free (g->qemu_help); + g->qemu_help = NULL; + free (g->qemu_version); + g->qemu_version = NULL; + snprintf (cmd, sizeof cmd, "LC_ALL=C '%s' -nographic -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); + if (test_qemu_cmd (g, cmd, &g->qemu_help) == -1) { + error (g, _("command failed: %s\n\nIf qemu is located on a non-standard path, try setting the LIBGUESTFS_QEMU\nenvironment variable. There may also be errors printed above."), + cmd); return -1; } - if (read_all (g, fp, &g->qemu_help) == -1) - goto error; - - if (pclose (fp) == -1) - goto error; - snprintf (cmd, sizeof cmd, "LC_ALL=C '%s' -nographic -version 2>/dev/null", g->qemu); + /* Intentionally ignore errors from qemu -version. */ + ignore_value (test_qemu_cmd (g, cmd, &g->qemu_version)); + + return 0; +} + +static int +test_qemu_cmd (guestfs_h *g, const char *cmd, char **ret) +{ + FILE *fp; + fp = popen (cmd, "r"); - if (fp) { - /* Intentionally ignore errors. */ - read_all (g, fp, &g->qemu_version); + if (fp == NULL) + return -1; + + if (read_all (g, fp, ret) == -1) { pclose (fp); + return -1; } + if (pclose (fp) != 0) + return -1; + return 0; } @@ -1122,6 +1181,20 @@ qemu_supports (guestfs_h *g, const char *option) return strstr (g->qemu_help, option) != NULL; } +#if 0 +/* As above but using a regex instead of a fixed string. */ +static int +qemu_supports_re (guestfs_h *g, const pcre *option_regex) +{ + if (!g->qemu_help) { + if (test_qemu (g) == -1) + return -1; + } + + return match (g, g->qemu_help, option_regex); +} +#endif + /* Check if a file can be opened. */ static int is_openable (guestfs_h *g, const char *path, int flags)