LIBGUESTFS_PATH implementation.
authorRichard Jones <rjones@redhat.com>
Sat, 4 Apr 2009 11:54:47 +0000 (12:54 +0100)
committerRichard Jones <rjones@redhat.com>
Sat, 4 Apr 2009 11:54:47 +0000 (12:54 +0100)
configure.ac
fish/Makefile.am
fish/fish.c
guestfs.pod
libguestfs.spec.in
src/Makefile.am
src/guestfs.c
src/guestfs.h

index dc1d81f..ff5d1b9 100644 (file)
@@ -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],
index b8f07ed..76a2894 100644 (file)
@@ -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
index 5de84bc..0fc14ce 100644 (file)
@@ -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;
index 129c681..3c28b04 100644 (file)
@@ -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</usr/local/lib/guestfs> or C</usr/lib64/guestfs>).
+
+Use C<guestfs_set_path> or set the environment variable
+C<LIBGUESTFS_PATH> to change the directories that libguestfs will
+search in.  The value is a colon-separated list of paths.  The current
+directory is I<not> searched unless the path contains an empty element
+or C<.>.  For example C<LIBGUESTFS_PATH=:/usr/lib/guestfs> would
+search the current directory and then C</usr/lib/guestfs>.
+
+=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<LIBGUESTFS_PATH> environment variable.
+
+The string C<path> is stashed in the libguestfs handle, so the caller
+must make sure it remains valid for the lifetime of the handle.
+
+Setting C<path> to C<NULL> 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<g_main_loop_quit>.  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<qemu(1)>
+Set C<LIBGUESTFS_DEBUG=1> to enable verbose messages.  This
+has the same effect as calling C<guestfs_set_verbose (handle, 1)>.
 
+=item LIBGUESTFS_PATH
 
+Set the path that libguestfs uses to search for kernel and initrd.img.
+See the discussion of paths in C<guestfs_set_path> above.
 
+=back
+
+=head1 SEE ALSO
 
+L<guestfish(1)>,
+L<qemu(1)>,
+L<febootstrap(1)>,
+L<http://et.redhat.com/~rjones/libguestfs>.
 
 =head1 AUTHORS
 
index 7a33c62..c9bdde6 100644 (file)
@@ -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
index 7fa17e0..566fa41 100644 (file)
@@ -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
index 747aae5..84da8d6 100644 (file)
@@ -19,7 +19,7 @@
 #include <config.h>
 
 #define _BSD_SOURCE /* for mkdtemp, usleep */
-#define _GNU_SOURCE /* for vasprintf, GNU strerror_r */
+#define _GNU_SOURCE /* for vasprintf, GNU strerror_r, strchrnul */
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -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 <path>/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);
 
index 3a09b95..575e0c7 100644 (file)
@@ -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 <guestfs-actions.h>