#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"
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;
+ }
+
+ /* 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;
+ }
}
- size_t len = strlen (filename) + 64;
+ /* 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)
/* 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);
/* Enable user networking. */
if (g->enable_network) {
add_cmdline (g, "-netdev");
- add_cmdline (g, "user,id=usernet");
+ add_cmdline (g, "user,id=usernet,net=169.254.0.0/16");
add_cmdline (g, "-device");
add_cmdline (g, NET_IF ",netdev=usernet");
}
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.
+ */
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.
+/* 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 void
-print_cmdline (guestfs_h *g)
+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;
+}
+
+void
+guestfs___print_timestamped_argv (guestfs_h *g, const char * argv[])
{
int i = 0;
int needs_quote;
- while (g->cmdline[i]) {
- if (g->cmdline[i][0] == '-') /* -option starts a new line */
+ 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 (g->cmdline[i], " ") != strlen (g->cmdline[i]);
+ needs_quote = strcspn (argv[i], " ") != strlen (argv[i]);
if (needs_quote) fputc ('\'', stderr);
- fprintf (stderr, "%s", g->cmdline[i]);
+ fprintf (stderr, "%s", argv[i]);
if (needs_quote) fputc ('\'', stderr);
i++;
}
fputc ('\n', stderr);
}
-/* 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;
-}
-
void
guestfs___print_timestamped_message (guestfs_h *g, const char *fs, ...)
{