#include <netinet/in.h>
#include "c-ctype.h"
+#include "ignore-value.h"
#include "glthread/lock.h"
#include "guestfs.h"
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)
return -1;
}
+ TRACE0 (launch_start);
+
/* Make the temporary directory. */
if (!g->tmpdir) {
TMP_TEMPLATE_ON_STACK (dir_template);
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)
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);
"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 */
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, stderr. */
close (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);
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
goto cleanup1;
}
+ TRACE0 (launch_end);
+
guestfs___launch_send_progress (g, 12);
return 0;
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, ...)
{
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
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;
}
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)