From 8358ea9524509c02448fe52d5bea205c9c3f869e Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 4 Apr 2009 12:54:47 +0100 Subject: [PATCH] LIBGUESTFS_PATH implementation. --- configure.ac | 10 +++--- fish/Makefile.am | 4 ++- fish/fish.c | 8 +++++ guestfs.pod | 57 ++++++++++++++++++++++++++++-- libguestfs.spec.in | 2 ++ src/Makefile.am | 4 ++- src/guestfs.c | 102 ++++++++++++++++++++++++++++++++++++++++++++--------- src/guestfs.h | 2 ++ 8 files changed, 164 insertions(+), 25 deletions(-) diff --git a/configure.ac b/configure.ac index dc1d81f..ff5d1b9 100644 --- a/configure.ac +++ b/configure.ac @@ -50,10 +50,9 @@ AC_CHECK_PROG([POD2TEXT],[pod2text],[pod2text],[no]) test "x$POD2TEXT" = "xno" && AC_MSG_ERROR([pod2text must be installed]) -dnl Check for QEMU. We only check for the basic 'qemu' program here -dnl (ie. the i386 full system qemu). But at runtime we might choose -dnl a different qemu to run, eg. qemu-system-ppc. -AC_PATH_PROG([QEMU],[qemu],[no], +dnl Check for QEMU for running binaries on this $host_cpu, fall +dnl back to basic 'qemu'. +AC_PATH_PROGS([QEMU],[qemu-system-$host_cpu qemu],[no], [$PATH$PATH_SEPARATOR/usr/sbin$PATH_SEPARATOR/sbin]) test "x$QEMU" = "xno" && AC_MSG_ERROR([qemu must be installed]) AC_DEFINE_UNQUOTED([QEMU],["$QEMU"],[Location of qemu binary.]) @@ -84,6 +83,9 @@ AC_ARG_WITH([repo], [with_repo=fedora-10]) REPO="$with_repo" AC_SUBST(REPO) +AC_DEFINE_UNQUOTED([REPO],["$REPO"],[Name of Fedora repository.]) + +AC_DEFINE_UNQUOTED([host_cpu],["$host_cpu"],[Host architecture.]) dnl --with-mirror to specify a local Fedora mirror. AC_ARG_WITH([mirror], diff --git a/fish/Makefile.am b/fish/Makefile.am index b8f07ed..76a2894 100644 --- a/fish/Makefile.am +++ b/fish/Makefile.am @@ -21,5 +21,7 @@ guestfish_SOURCES = \ cmds.c \ fish.c \ fish.h -guestfish_CFLAGS = -I$(top_builddir)/src -Wall +guestfish_CFLAGS = \ + -I$(top_builddir)/src -Wall \ + -DGUESTFS_DEFAULT_PATH='"$(libdir)/guestfs"' guestfish_LDADD = $(top_builddir)/src/libguestfs.la diff --git a/fish/fish.c b/fish/fish.c index 5de84bc..0fc14ce 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -117,6 +117,14 @@ main (int argc, char *argv[]) guestfs_set_autosync (g, 1); + /* If developing, add . to the path. Note that libtools interferes + * with this because uninstalled guestfish is a shell script that runs + * the real program with an absolute path. Detect that too. + */ + if (argv[0] && + (argv[0][0] != '/' || strstr (argv[0], "/.libs/lt-") != NULL)) + guestfs_set_path (g, ".:" GUESTFS_DEFAULT_PATH); + for (;;) { c = getopt_long (argc, argv, options, long_options, NULL); if (c == -1) break; diff --git a/guestfs.pod b/guestfs.pod index 129c681..3c28b04 100644 --- a/guestfs.pod +++ b/guestfs.pod @@ -237,6 +237,44 @@ situations. This returns the current out of memory handler. +=head1 PATH + +Libguestfs needs a kernel and initrd.img, which it finds by looking +along an internal path. + +By default it looks for these in the directory C<$libdir/guestfs> +(eg. C or C). + +Use C or set the environment variable +C to change the directories that libguestfs will +search in. The value is a colon-separated list of paths. The current +directory is I searched unless the path contains an empty element +or C<.>. For example C would +search the current directory and then C. + +=head2 guestfs_set_path + + void guestfs_set_path (guestfs_h *handle, const char *path); + +Set the path that libguestfs searches for kernel and initrd.img. + +The default is C<$libdir/guestfs> unless overridden by setting +C environment variable. + +The string C is stashed in the libguestfs handle, so the caller +must make sure it remains valid for the lifetime of the handle. + +Setting C to C restores the default path. + +=head2 guestfs_get_path + + const char *guestfs_get_path (guestfs_h *handle); + +Return the current search path. + +This is always non-NULL. If it wasn't set already, then this will +return the default path. + =head1 AUTOSYNC =head2 guestfs_set_autosync @@ -550,13 +588,28 @@ function, eg. C. In those cases, ignore this call. This isn't documented. Please see the libguestfs-select and libguestfs-glib implementations. -=head1 SEE ALSO +=head1 ENVIRONMENT VARIABLES + +=over 4 + +=item LIBGUESTFS_DEBUG -L +Set C to enable verbose messages. This +has the same effect as calling C. +=item LIBGUESTFS_PATH +Set the path that libguestfs uses to search for kernel and initrd.img. +See the discussion of paths in C above. +=back + +=head1 SEE ALSO +L, +L, +L, +L. =head1 AUTHORS diff --git a/libguestfs.spec.in b/libguestfs.spec.in index 7a33c62..c9bdde6 100644 --- a/libguestfs.spec.in +++ b/libguestfs.spec.in @@ -109,6 +109,8 @@ rm -rf $RPM_BUILD_ROOT %doc HACKING README examples %{_libdir}/libguestfs.so %{_mandir}/man3/guestfs.3* +%{_includedir}/guestfs.h +%{_includedir}/guestfs-actions.h %files -n guestfish diff --git a/src/Makefile.am b/src/Makefile.am index 7fa17e0..566fa41 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,6 +23,8 @@ EXTRA_DIST += guestfs_protocol.x \ guestfs_protocol.c \ guestfs_protocol.h +include_HEADERS = guestfs.h guestfs-actions.h + lib_LTLIBRARIES = libguestfs.la libguestfs_la_LDFLAGS = -version-info 0:0:0 @@ -33,7 +35,7 @@ libguestfs_la_SOURCES = \ guestfs_protocol.h \ guestfs-actions.h -libguestfs_la_CFLAGS = -Wall +libguestfs_la_CFLAGS = -Wall -DGUESTFS_DEFAULT_PATH='"$(libdir)/guestfs"' if RPCGEN guestfs_protocol.c: guestfs_protocol.x diff --git a/src/guestfs.c b/src/guestfs.c index 747aae5..84da8d6 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -19,7 +19,7 @@ #include #define _BSD_SOURCE /* for mkdtemp, usleep */ -#define _GNU_SOURCE /* for vasprintf, GNU strerror_r */ +#define _GNU_SOURCE /* for vasprintf, GNU strerror_r, strchrnul */ #include #include @@ -118,6 +118,8 @@ struct guestfs_h int verbose; int autosync; + const char *path; + /* Callbacks. */ guestfs_abort_cb abort_cb; guestfs_error_handler_cb error_cb; @@ -178,6 +180,10 @@ guestfs_create (void) str = getenv ("LIBGUESTFS_DEBUG"); g->verbose = str != NULL && strcmp (str, "1") == 0; + str = getenv ("LIBGUESTFS_PATH"); + g->path = str != NULL ? str : GUESTFS_DEFAULT_PATH; + /* XXX We should probably make QEMU configurable as well. */ + /* Start with large serial numbers so they are easy to spot * inside the protocol. */ @@ -395,6 +401,21 @@ guestfs_get_autosync (guestfs_h *g) return g->autosync; } +void +guestfs_set_path (guestfs_h *g, const char *path) +{ + if (path == NULL) + g->path = GUESTFS_DEFAULT_PATH; + else + g->path = path; +} + +const char * +guestfs_get_path (guestfs_h *g) +{ + return g->path; +} + /* Add a string to the current command line. */ static void incr_cmdline_size (guestfs_h *g) @@ -487,19 +508,16 @@ int guestfs_launch (guestfs_h *g) { static const char *dir_template = "/tmp/libguestfsXXXXXX"; - int r, i; + int r, i, len; int wfd[2], rfd[2]; int tries; - /*const char *qemu = QEMU;*/ /* XXX */ - const char *qemu = "/usr/bin/qemu-system-x86_64"; - const char *kernel = "vmlinuz.fedora-10.x86_64"; - const char *initrd = "initramfs.fedora-10.x86_64.img"; + const char *kernel_name = "vmlinuz." REPO "." host_cpu; + const char *initrd_name = "initramfs." REPO "." host_cpu ".img"; + char *path, *pelem, *pend; + char *kernel = NULL, *initrd = NULL; char unixsock[256]; struct sockaddr_un addr; - /* XXX Choose which qemu to run. */ - /* XXX Choose initrd, etc. */ - /* Configured? */ if (!g->cmdline) { error (g, "you must call guestfs_add_drive before guestfs_launch"); @@ -511,12 +529,58 @@ guestfs_launch (guestfs_h *g) return -1; } + /* Search g->path for the kernel and initrd. */ + pelem = path = safe_strdup (g, g->path); + do { + pend = strchrnul (pelem, ':'); + *pend = '\0'; + len = pend - pelem; + + /* Empty element or "." means cwd. */ + if (len == 0 || (len == 1 && *pelem == '.')) { + if (g->verbose) + fprintf (stderr, + "looking for kernel and initrd in current directory\n"); + if (access (kernel_name, F_OK) == 0 && access (initrd_name, F_OK) == 0) { + kernel = safe_strdup (g, kernel_name); + initrd = safe_strdup (g, initrd_name); + break; + } + } + /* Look at /kernel etc. */ + else { + kernel = safe_malloc (g, len + strlen (kernel_name) + 2); + initrd = safe_malloc (g, len + strlen (initrd_name) + 2); + sprintf (kernel, "%s/%s", pelem, kernel_name); + sprintf (initrd, "%s/%s", pelem, initrd_name); + + if (g->verbose) + fprintf (stderr, "looking for %s and %s\n", kernel, initrd); + + if (access (kernel, F_OK) == 0 && access (initrd, F_OK) == 0) + break; + free (kernel); + free (initrd); + kernel = initrd = NULL; + } + + pelem = pend; + } while (*pelem++ != '\0'); + + free (path); + + if (kernel == NULL || initrd == NULL) { + error (g, "cannot find %s or %s on LIBGUESTFS_PATH (current path = %s)", + kernel_name, initrd_name, g->path); + goto cleanup0; + } + /* Make the temporary directory containing the socket. */ if (!g->tmpdir) { g->tmpdir = safe_strdup (g, dir_template); if (mkdtemp (g->tmpdir) == NULL) { perrorf (g, "%s: cannot create temporary directory", dir_template); - return -1; + goto cleanup0; } } @@ -525,7 +589,7 @@ guestfs_launch (guestfs_h *g) if (pipe (wfd) == -1 || pipe (rfd) == -1) { perrorf (g, "pipe"); - return -1; + goto cleanup0; } r = fork (); @@ -535,7 +599,7 @@ guestfs_launch (guestfs_h *g) close (wfd[1]); close (rfd[0]); close (rfd[1]); - return -1; + goto cleanup0; } if (r == 0) { /* Child (qemu). */ @@ -545,7 +609,7 @@ guestfs_launch (guestfs_h *g) /* Set up the full command line. Do this in the subprocess so we * don't need to worry about cleaning up. */ - g->cmdline[0] = (char *) qemu; + g->cmdline[0] = (char *) QEMU; /* Construct the -net channel parameter for qemu. */ snprintf (vmchannel, sizeof vmchannel, @@ -578,7 +642,7 @@ guestfs_launch (guestfs_h *g) g->cmdline[g->cmdline_size-1] = NULL; if (g->verbose) { - fprintf (stderr, "%s", qemu); + fprintf (stderr, "%s", QEMU); for (i = 0; g->cmdline[i]; ++i) fprintf (stderr, " %s", g->cmdline[i]); fprintf (stderr, "\n"); @@ -601,8 +665,8 @@ guestfs_launch (guestfs_h *g) setpgid (0, 0); #endif - execv (qemu, g->cmdline); /* Run qemu. */ - perror (qemu); + execv (QEMU, g->cmdline); /* Run qemu. */ + perror (QEMU); _exit (1); } @@ -719,6 +783,10 @@ guestfs_launch (guestfs_h *g) g->start_t = 0; g->stdout_watch = -1; g->sock_watch = -1; + + cleanup0: + free (kernel); + free (initrd); return -1; } @@ -779,7 +847,7 @@ guestfs_kill_subprocess (guestfs_h *g) } if (g->verbose) - fprintf (stderr, "sending SIGTERM to process group %d\n", g->pid); + fprintf (stderr, "sending SIGTERM to process %d\n", g->pid); kill (g->pid, SIGTERM); diff --git a/src/guestfs.h b/src/guestfs.h index 3a09b95..575e0c7 100644 --- a/src/guestfs.h +++ b/src/guestfs.h @@ -56,6 +56,8 @@ extern void guestfs_set_verbose (guestfs_h *g, int verbose); extern int guestfs_get_verbose (guestfs_h *g); extern void guestfs_set_autosync (guestfs_h *g, int a); extern int guestfs_get_autosync (guestfs_h *g); +extern void guestfs_set_path (guestfs_h *g, const char *path); +extern const char *guestfs_get_path (guestfs_h *g); #include -- 1.8.3.1