#include <sys/select.h>
#include <dirent.h>
#include <signal.h>
+#include <assert.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
#include "c-ctype.h"
#include "glthread/lock.h"
-#include "ignore-value.h"
#include "guestfs.h"
#include "guestfs-internal.h"
}
int
+guestfs___checkpoint_cmdline (guestfs_h *g)
+{
+ return g->cmdline_size;
+}
+
+void
+guestfs___rollback_cmdline (guestfs_h *g, int pos)
+{
+ int i;
+
+ assert (g->cmdline_size >= pos);
+
+ for (i = g->cmdline_size - 1; i >= pos; --i)
+ free (g->cmdline[i]);
+
+ g->cmdline_size = pos;
+}
+
+/* Internal command to return the command line. */
+char **
+guestfs__debug_cmdline (guestfs_h *g)
+{
+ int i;
+ char **r;
+
+ if (g->cmdline == NULL) {
+ r = safe_malloc (g, sizeof (char *) * 1);
+ r[0] = NULL;
+ return r;
+ }
+
+ r = safe_malloc (g, sizeof (char *) * (g->cmdline_size + 1));
+ r[0] = safe_strdup (g, g->qemu); /* g->cmdline[0] is always NULL */
+
+ for (i = 1; i < g->cmdline_size; ++i)
+ r[i] = safe_strdup (g, g->cmdline[i]);
+
+ r[g->cmdline_size] = NULL;
+
+ return r; /* caller frees */
+}
+
+int
guestfs__config (guestfs_h *g,
const char *qemu_param, const char *qemu_value)
{
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);
}
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
}
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)
{
int r;
int wfd[2], rfd[2];
- int tries;
char unixsock[256];
struct sockaddr_un addr;
/* 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);
*/
g->cmdline[0] = g->qemu;
+ if (qemu_supports (g, "-nodefconfig"))
+ add_cmdline (g, "-nodefconfig");
+
/* 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).
add_cmdline (g, "-device");
add_cmdline (g, "virtserialport,chardev=channel0,name=org.libguestfs.channel.0");
+ /* Enable user networking. */
+ if (g->enable_network) {
+ add_cmdline (g, "-netdev");
+ add_cmdline (g, "user,id=usernet,net=169.254.0.0/16");
+ add_cmdline (g, "-device");
+ add_cmdline (g, NET_IF ",netdev=usernet");
+ }
+
#define LINUX_CMDLINE \
"panic=1 " /* force kernel to panic if daemon exits */ \
"console=ttyS0 " /* serial console */ \
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. */
kernel = NULL;
free (initrd);
initrd = NULL;
+ free (appliance);
+ appliance = NULL;
/* Fork the recovery process off which will kill qemu if the parent
* process fails to do so (eg. if the parent segfaults).
return -1;
}
+/* Return the location of the tmpdir (eg. "/tmp") and allow users
+ * to override it at runtime using $TMPDIR.
+ * http://www.pathname.com/fhs/pub/fhs-2.3.html#TMPTEMPORARYFILES
+ */
const char *
-guestfs___tmpdir (void)
+guestfs_tmpdir (void)
{
const char *tmpdir;
return tmpdir;
}
-/* This function is used to print the qemu command line before it gets
- * executed, when in verbose mode.
+/* Return the location of the persistent tmpdir (eg. "/var/tmp") and
+ * allow users to override it at runtime using $TMPDIR.
+ * http://www.pathname.com/fhs/pub/fhs-2.3.html#VARTMPTEMPORARYFILESPRESERVEDBETWEE
*/
-static void
-print_cmdline (guestfs_h *g)
+const char *
+guestfs___persistent_tmpdir (void)
{
- int i = 0;
- int needs_quote;
-
- while (g->cmdline[i]) {
- if (g->cmdline[i][0] == '-') /* -option starts a new line */
- fprintf (stderr, " \\\n ");
+ const char *tmpdir;
- if (i > 0) fputc (' ', stderr);
+ tmpdir = "/var/tmp";
- /* Does it need shell quoting? This only deals with simple cases. */
- needs_quote = strcspn (g->cmdline[i], " ") != strlen (g->cmdline[i]);
-
- if (needs_quote) fputc ('\'', stderr);
- fprintf (stderr, "%s", g->cmdline[i]);
- if (needs_quote) fputc ('\'', stderr);
- i++;
- }
+ const char *t = getenv ("TMPDIR");
+ if (t) tmpdir = t;
- fputc ('\n', stderr);
+ return tmpdir;
}
/* Compute Y - X and return the result in milliseconds.
}
void
+guestfs___print_timestamped_argv (guestfs_h *g, const 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++;
+ }
+
+ fputc ('\n', stderr);
+}
+
+void
guestfs___print_timestamped_message (guestfs_h *g, const char *fs, ...)
{
va_list args;
goto error;
snprintf (cmd, sizeof cmd, "LC_ALL=C '%s' -nographic -version 2>/dev/null",
- g->qemu);
+ g->qemu);
fp = popen (cmd, "r");
if (fp) {