/* libguestfs
- * Copyright (C) 2009 Red Hat Inc.
+ * Copyright (C) 2009-2010 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
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
+#include <stdint.h>
+#include <inttypes.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
+#include "c-ctype.h"
+#include "glthread/lock.h"
+#include "ignore-value.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
#include "gettext.h"
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 msg_next_serial;
};
+gl_lock_define_initialized (static, handles_lock);
static guestfs_h *handles = NULL;
static int atexit_handler_set = 0;
*/
g->msg_next_serial = 0x00123400;
- /* Link the handles onto a global list. This is the one area
- * where the library needs to be made thread-safe. (XXX)
- */
- /* acquire mutex (XXX) */
+ /* Link the handles onto a global list. */
+ gl_lock_lock (handles_lock);
g->next = handles;
handles = g;
if (!atexit_handler_set) {
atexit (close_handles);
atexit_handler_set = 1;
}
- /* release mutex (XXX) */
+ gl_lock_unlock (handles_lock);
if (g->verbose)
fprintf (stderr, "new guestfs handle %p\n", g);
/* Mark the handle as dead before freeing it. */
g->state = NO_HANDLE;
- /* acquire mutex (XXX) */
+ gl_lock_lock (handles_lock);
if (handles == g)
handles = g->next;
else {
;
gg->next = g->next;
}
- /* release mutex (XXX) */
+ gl_lock_unlock (handles_lock);
free (g->last_error);
free (g->path);
}
int
-guestfs__add_drive (guestfs_h *g, const char *filename)
+guestfs__add_drive_with_if (guestfs_h *g, const char *filename,
+ const char *drive_if)
{
size_t len = strlen (filename) + 64;
char buf[len];
int fd = open (filename, O_RDONLY|O_DIRECT);
if (fd >= 0) {
close (fd);
- snprintf (buf, len, "file=%s,cache=off,if=" DRIVE_IF, filename);
+ 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=" DRIVE_IF, filename);
+ snprintf (buf, len, "file=%s,if=%s", filename, drive_if);
} else {
perrorf (g, "%s", filename);
return -1;
}
int
-guestfs__add_drive_ro (guestfs_h *g, const char *filename)
+guestfs__add_drive_ro_with_if (guestfs_h *g, const char *filename,
+ const char *drive_if)
{
size_t len = strlen (filename) + 64;
char buf[len];
return -1;
}
- snprintf (buf, len, "file=%s,snapshot=on,if=%s", filename, DRIVE_IF);
+ snprintf (buf, len, "file=%s,snapshot=on,if=%s", filename, drive_if);
return guestfs__config (g, "-drive", buf);
}
int
+guestfs__add_drive (guestfs_h *g, const char *filename)
+{
+ return guestfs__add_drive_with_if (g, filename, DRIVE_IF);
+}
+
+int
+guestfs__add_drive_ro (guestfs_h *g, const char *filename)
+{
+ return guestfs__add_drive_ro_with_if (g, filename, DRIVE_IF);
+}
+
+int
guestfs__add_cdrom (guestfs_h *g, const char *filename)
{
if (strchr (filename, ',') != NULL) {
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);
+static int is_openable (guestfs_h *g, const char *path, int flags);
static void print_cmdline (guestfs_h *g);
static const char *kernel_name = "vmlinuz." REPO "." host_cpu;
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;
}
}
+ if (g->verbose)
+ print_timestamped_message (g, "finished testing qemu features");
+
r = fork ();
if (r == -1) {
perrorf (g, "fork");
*/
g->cmdline[0] = g->qemu;
- snprintf (buf, sizeof buf, "%d", g->memsize);
- add_cmdline (g, "-m");
- add_cmdline (g, buf);
+ /* qemu sometimes needs this option to enable hardware
+ * virtualization, but some versions of 'qemu-kvm' will use KVM
+ * regardless (even where this option appears in the help text).
+ * It is rumoured that there are versions of qemu where supplying
+ * this option when hardware virtualization is not available will
+ * cause qemu to fail, so we we have to check at least that
+ * /dev/kvm is openable. That's not reliable, since /dev/kvm
+ * might be openable by qemu but not by us (think: SELinux) in
+ * which case the user would not get hardware virtualization,
+ * although at least shouldn't fail. A giant clusterfuck with the
+ * qemu command line, again.
+ */
+ if (qemu_supports (g, "-enable-kvm") &&
+ is_openable (g, "/dev/kvm", O_RDWR))
+ add_cmdline (g, "-enable-kvm");
+
+ /* Newer versions of qemu (from around 2009/12) changed the
+ * behaviour of monitors so that an implicit '-monitor stdio' is
+ * assumed if we are in -nographic mode and there is no other
+ * -monitor option. Only a single stdio device is allowed, so
+ * this broke the '-serial stdio' option. There is a new flag
+ * called -nodefaults which gets rid of all this default crud, so
+ * let's use that to avoid this and any future surprises.
+ */
+ if (qemu_supports (g, "-nodefaults"))
+ add_cmdline (g, "-nodefaults");
- add_cmdline (g, "-no-reboot"); /* Force exit instead of reboot on panic */
add_cmdline (g, "-nographic");
add_cmdline (g, "-serial");
add_cmdline (g, "stdio");
+ snprintf (buf, sizeof buf, "%d", g->memsize);
+ add_cmdline (g, "-m");
+ add_cmdline (g, buf);
+
+ /* Force exit instead of reboot on panic */
+ add_cmdline (g, "-no-reboot");
+
/* These options recommended by KVM developers to improve reliability. */
if (qemu_supports (g, "-no-hpet"))
add_cmdline (g, "-no-hpet");
"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. */
g->recoverypid = r;
}
- /* Start the clock ... */
- time (&g->start_t);
-
if (!g->direct) {
/* Close the other ends of the pipe. */
close (wfd[0]);
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
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);
snprintf (cmd, sizeof cmd,
"PATH='%s':$PATH "
- "libguestfs-supermin-helper '%s' %s %s",
+ "libguestfs-supermin-helper '%s' " host_cpu " " REPO " %s %s",
path,
path, *kernel, *initrd);
+ if (g->verbose)
+ print_timestamped_message (g, "%s", cmd);
r = system (cmd);
if (r == -1 || WEXITSTATUS(r) != 0) {
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
return g->qemu_help && strstr (g->qemu_help, option) != NULL;
}
+/* Check if a file can be opened. */
+static int
+is_openable (guestfs_h *g, const char *path, int flags)
+{
+ int fd = open (path, flags);
+ if (fd == -1) {
+ if (g->verbose)
+ perror (path);
+ return 0;
+ }
+ close (fd);
+ return 1;
+}
+
/* Check the peer effective UID for a TCP socket. Ideally we'd like
* SO_PEERCRED for a loopback TCP socket. This isn't possible on
* Linux (but it is on Solaris!) so we read /proc/net/tcp instead.
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);