#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
+#include <stdint.h>
+#include <inttypes.h>
#include <unistd.h>
-#include <ctype.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <netinet/in.h>
#include "guestfs.h"
+#include "guestfs-internal.h"
#include "guestfs-internal-actions.h"
#include "guestfs_protocol.h"
+#include "c-ctype.h"
#include "ignore-value.h"
#ifdef HAVE_GETTEXT
int sock; /* Daemon communications socket. */
pid_t pid; /* Qemu PID. */
pid_t recoverypid; /* Recovery process PID. */
- time_t start_t; /* The time when we started qemu. */
+
+ struct timeval launch_t; /* The time that we called guestfs_launch. */
char *tmpdir; /* Temporary directory containing socket. */
int verbose;
int trace;
int autosync;
+ int direct;
+ int recovery_proc;
char *path; /* Path to kernel, initrd. */
char *qemu; /* Qemu binary. */
g->error_cb = default_error_cb;
g->error_cb_data = NULL;
+ g->recovery_proc = 1;
+
str = getenv ("LIBGUESTFS_DEBUG");
- g->verbose = str != NULL && strcmp (str, "1") == 0;
+ g->verbose = str != NULL && STREQ (str, "1");
str = getenv ("LIBGUESTFS_TRACE");
- g->trace = str != NULL && strcmp (str, "1") == 0;
+ g->trace = str != NULL && STREQ (str, "1");
str = getenv ("LIBGUESTFS_PATH");
g->path = str != NULL ? strdup (str) : strdup (GUESTFS_DEFAULT_PATH);
return g->trace;
}
+int
+guestfs__set_direct (guestfs_h *g, int d)
+{
+ g->direct = !!d;
+ return 0;
+}
+
+int
+guestfs__get_direct (guestfs_h *g)
+{
+ return g->direct;
+}
+
+int
+guestfs__set_recovery_proc (guestfs_h *g, int f)
+{
+ g->recovery_proc = !!f;
+ return 0;
+}
+
+int
+guestfs__get_recovery_proc (guestfs_h *g)
+{
+ return g->recovery_proc;
+}
+
/* Add a string to the current command line. */
static void
incr_cmdline_size (guestfs_h *g)
/* A bit fascist, but the user will probably break the extra
* parameters that we add if they try to set any of these.
*/
- if (strcmp (qemu_param, "-kernel") == 0 ||
- strcmp (qemu_param, "-initrd") == 0 ||
- strcmp (qemu_param, "-nographic") == 0 ||
- strcmp (qemu_param, "-serial") == 0 ||
- strcmp (qemu_param, "-full-screen") == 0 ||
- strcmp (qemu_param, "-std-vga") == 0 ||
- strcmp (qemu_param, "-vnc") == 0) {
+ if (STREQ (qemu_param, "-kernel") ||
+ STREQ (qemu_param, "-initrd") ||
+ STREQ (qemu_param, "-nographic") ||
+ STREQ (qemu_param, "-serial") ||
+ STREQ (qemu_param, "-full-screen") ||
+ STREQ (qemu_param, "-std-vga") ||
+ STREQ (qemu_param, "-vnc")) {
error (g, _("guestfs_config: parameter '%s' isn't allowed"), qemu_param);
return -1;
}
return 1;
}
+static void print_timestamped_message (guestfs_h *g, const char *fs, ...);
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);
char unixsock[256];
struct sockaddr_un addr;
+ /* Start the clock ... */
+ gettimeofday (&g->launch_t, NULL);
+
#ifdef P_tmpdir
tmpdir = P_tmpdir;
#else
goto cleanup0;
}
+ if (g->verbose)
+ print_timestamped_message (g, "begin testing qemu features");
+
/* Get qemu help text and version. */
if (test_qemu (g) == -1)
goto cleanup0;
null_vmchannel_sock = 0;
}
- if (pipe (wfd) == -1 || pipe (rfd) == -1) {
- perrorf (g, "pipe");
- goto cleanup0;
+ if (!g->direct) {
+ if (pipe (wfd) == -1 || pipe (rfd) == -1) {
+ perrorf (g, "pipe");
+ goto cleanup0;
+ }
}
+ if (g->verbose)
+ print_timestamped_message (g, "finished testing qemu features");
+
r = fork ();
if (r == -1) {
perrorf (g, "fork");
- close (wfd[0]);
- close (wfd[1]);
- close (rfd[0]);
- close (rfd[1]);
+ if (!g->direct) {
+ close (wfd[0]);
+ close (wfd[1]);
+ close (rfd[0]);
+ close (rfd[1]);
+ }
goto cleanup0;
}
add_cmdline (g, "user,vlan=0,net=10.0.2.0/8");
snprintf (buf, sizeof buf,
- "guestfs_vmchannel=tcp:10.0.2.2:%d ", null_vmchannel_sock);
+ "guestfs_vmchannel=tcp:10.0.2.2:%d", null_vmchannel_sock);
vmchannel = strdup (buf);
}
add_cmdline (g, "-net");
add_cmdline (g, buf);
- vmchannel = "guestfs_vmchannel=tcp:" GUESTFWD_ADDR ":" GUESTFWD_PORT " ";
+ vmchannel = "guestfs_vmchannel=tcp:" GUESTFWD_ADDR ":" GUESTFWD_PORT;
}
/* Not guestfwd. HOPEFULLY this qemu uses the older -net channel
add_cmdline (g, "-net");
add_cmdline (g, "user,vlan=0,net=10.0.2.0/8");
- vmchannel = "guestfs_vmchannel=tcp:" GUESTFWD_ADDR ":" GUESTFWD_PORT " ";
+ vmchannel = "guestfs_vmchannel=tcp:" GUESTFWD_ADDR ":" GUESTFWD_PORT;
}
add_cmdline (g, "-net");
add_cmdline (g, "nic,model=" NET_IF ",vlan=0");
"udevtimeout=300 " /* good for very slow systems (RHBZ#480319) */ \
"noapic " /* workaround for RHBZ#502058 - ok if not SMP */ \
"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 */
/* Linux kernel command line. */
snprintf (buf, sizeof buf,
LINUX_CMDLINE
- "%s" /* (selinux) */
- "%s" /* (vmchannel) */
- "%s" /* (verbose) */
+ "%s " /* (selinux) */
+ "%s " /* (vmchannel) */
+ "%s " /* (verbose) */
"%s", /* (append) */
- g->selinux ? "selinux=1 enforcing=0 " : "selinux=0 ",
+ g->selinux ? "selinux=1 enforcing=0" : "selinux=0",
vmchannel ? vmchannel : "",
- g->verbose ? "guestfs_verbose=1 " : "",
+ g->verbose ? "guestfs_verbose=1" : "",
g->append ? g->append : "");
add_cmdline (g, "-kernel");
if (g->verbose)
print_cmdline (g);
- /* Set up stdin, stdout. */
- close (0);
- close (1);
- close (wfd[1]);
- close (rfd[0]);
+ if (!g->direct) {
+ /* Set up stdin, stdout. */
+ close (0);
+ close (1);
+ close (wfd[1]);
+ close (rfd[0]);
+
+ if (dup (wfd[0]) == -1) {
+ dup_failed:
+ perror ("dup failed");
+ _exit (1);
+ }
+ if (dup (rfd[1]) == -1)
+ goto dup_failed;
- if (dup (wfd[0]) == -1) {
- dup_failed:
- perror ("dup failed");
- _exit (1);
+ close (wfd[0]);
+ close (rfd[1]);
}
- if (dup (rfd[1]) == -1)
- goto dup_failed;
-
- close (wfd[0]);
- close (rfd[1]);
#if 0
/* Set up a new process group, so we can signal this process
setpgid (0, 0);
#endif
+ setenv ("LC_ALL", "C", 1);
+
execv (g->qemu, g->cmdline); /* Run qemu. */
perror (g->qemu);
_exit (1);
/* Fork the recovery process off which will kill qemu if the parent
* process fails to do so (eg. if the parent segfaults).
*/
- r = fork ();
- if (r == 0) {
- pid_t qemu_pid = g->pid;
- pid_t parent_pid = getppid ();
-
- /* 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
- */
+ g->recoverypid = -1;
+ if (g->recovery_proc) {
+ r = fork ();
+ if (r == 0) {
+ pid_t qemu_pid = g->pid;
+ pid_t parent_pid = getppid ();
+
+ /* 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
+ */
- /* Loop around waiting for one or both of the other processes to
- * disappear. It's fair to say this is very hairy. The PIDs that
- * we are looking at might be reused by another process. We are
- * effectively polling. Is the cure worse than the disease?
- */
- for (;;) {
- if (kill (qemu_pid, 0) == -1) /* qemu's gone away, we aren't needed */
- _exit (0);
- if (kill (parent_pid, 0) == -1) {
- /* Parent's gone away, qemu still around, so kill qemu. */
- kill (qemu_pid, 9);
- _exit (0);
+ /* Loop around waiting for one or both of the other processes to
+ * disappear. It's fair to say this is very hairy. The PIDs that
+ * we are looking at might be reused by another process. We are
+ * effectively polling. Is the cure worse than the disease?
+ */
+ for (;;) {
+ if (kill (qemu_pid, 0) == -1) /* qemu's gone away, we aren't needed */
+ _exit (0);
+ if (kill (parent_pid, 0) == -1) {
+ /* Parent's gone away, qemu still around, so kill qemu. */
+ kill (qemu_pid, 9);
+ _exit (0);
+ }
+ sleep (2);
}
- sleep (2);
}
- }
- /* Don't worry, if the fork failed, this will be -1. The recovery
- * process isn't essential.
- */
- g->recoverypid = r;
+ /* Don't worry, if the fork failed, this will be -1. The recovery
+ * process isn't essential.
+ */
+ g->recoverypid = r;
+ }
- /* Start the clock ... */
- time (&g->start_t);
+ if (!g->direct) {
+ /* Close the other ends of the pipe. */
+ close (wfd[0]);
+ close (rfd[1]);
- /* Close the other ends of the pipe. */
- close (wfd[0]);
- close (rfd[1]);
+ if (fcntl (wfd[1], F_SETFL, O_NONBLOCK) == -1 ||
+ fcntl (rfd[0], F_SETFL, O_NONBLOCK) == -1) {
+ perrorf (g, "fcntl");
+ goto cleanup1;
+ }
- if (fcntl (wfd[1], F_SETFL, O_NONBLOCK) == -1 ||
- fcntl (rfd[0], F_SETFL, O_NONBLOCK) == -1) {
- perrorf (g, "fcntl");
- goto cleanup1;
+ g->fd[0] = wfd[1]; /* stdin of child */
+ g->fd[1] = rfd[0]; /* stdout of child */
+ } else {
+ g->fd[0] = open ("/dev/null", O_RDWR);
+ if (g->fd[0] == -1) {
+ perrorf (g, "open /dev/null");
+ goto cleanup1;
+ }
+ g->fd[1] = dup (g->fd[0]);
+ if (g->fd[1] == -1) {
+ perrorf (g, "dup");
+ close (g->fd[0]);
+ goto cleanup1;
+ }
}
- g->fd[0] = wfd[1]; /* stdin of child */
- g->fd[1] = rfd[0]; /* stdout of child */
-
if (null_vmchannel_sock) {
int sock = -1;
uid_t uid;
goto cleanup1;
}
+ if (g->verbose)
+ print_timestamped_message (g, "appliance is up");
+
/* This is possible in some really strange situations, such as
* guestfsd starts up OK but then qemu immediately exits. Check for
* it because the caller is probably expecting to be able to send
return 0;
cleanup1:
- close (wfd[1]);
- close (rfd[0]);
+ if (!g->direct) {
+ close (wfd[1]);
+ close (rfd[0]);
+ }
kill (g->pid, 9);
if (g->recoverypid > 0) kill (g->recoverypid, 9);
waitpid (g->pid, NULL, 0);
g->fd[1] = -1;
g->pid = 0;
g->recoverypid = 0;
- g->start_t = 0;
+ memset (&g->launch_t, 0, sizeof g->launch_t);
cleanup0:
if (g->sock >= 0) {
char cmd[4096];
int r, len;
+ if (g->verbose)
+ print_timestamped_message (g, "begin building supermin appliance");
+
len = strlen (g->tmpdir);
*kernel = safe_malloc (g, len + 8);
snprintf (*kernel, len+8, "%s/kernel", g->tmpdir);
return -1;
}
+ if (g->verbose)
+ print_timestamped_message (g, "finished building supermin appliance");
+
return 0;
}
+/* 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;
+}
+
+static void
+print_timestamped_message (guestfs_h *g, const char *fs, ...)
+{
+ va_list args;
+ char *msg;
+ int err;
+ struct timeval tv, diff;
+
+ va_start (args, fs);
+ err = vasprintf (&msg, fs, args);
+ va_end (args);
+
+ if (err < 0) return;
+
+ gettimeofday (&tv, NULL);
+
+ fprintf (stderr, "[%05" PRIi64 "ms] %s\n",
+ timeval_diff (&g->launch_t, &tv), msg);
+
+ free (msg);
+}
+
static int read_all (guestfs_h *g, FILE *fp, char **ret);
/* Test qemu binary (or wrapper) runs, and do 'qemu -help' and
g->qemu_help = NULL;
g->qemu_version = NULL;
- snprintf (cmd, sizeof cmd, "'%s' -help", g->qemu);
+ snprintf (cmd, sizeof cmd, "LC_ALL=C '%s' -help", g->qemu);
fp = popen (cmd, "r");
/* qemu -help should always work (qemu -version OTOH wasn't
if (pclose (fp) == -1)
goto error;
- snprintf (cmd, sizeof cmd, "'%s' -version 2>/dev/null", g->qemu);
+ snprintf (cmd, sizeof cmd, "LC_ALL=C '%s' -version 2>/dev/null", g->qemu);
fp = popen (cmd, "r");
if (fp) {
g->sock = -1;
g->pid = 0;
g->recoverypid = 0;
- g->start_t = 0;
+ memset (&g->launch_t, 0, sizeof g->launch_t);
g->state = CONFIG;
if (g->subprocess_quit_cb)
g->subprocess_quit_cb (g, g->subprocess_quit_cb_data);
printf (" ");
printf ("|");
for (j = i; j < MIN (i+16, nr); ++j)
- if (isprint ((*(char **)buf_rtn)[j]))
+ if (c_isprint ((*(char **)buf_rtn)[j]))
printf ("%c", (*(char **)buf_rtn)[j]);
else
printf (".");