[Libguestfs] [PATCH 2/2] lib: Rework temporary and cache directory code.

Richard W.M. Jones rjones at redhat.com
Fri Nov 9 12:17:19 UTC 2012


-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
New in Fedora 11: Fedora Windows cross-compiler. Compile Windows
programs, test, and build Windows installers. Over 70 libraries supprt'd
http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw
-------------- next part --------------
>From dc951e981cd71d98c7c00a1b764c3e32bb8971f6 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones at redhat.com>
Date: Thu, 8 Nov 2012 11:36:35 +0000
Subject: [PATCH 2/2] lib: Rework temporary and cache directory code.

The current code has evolved over time and has a number of problems:

(a) A single environment variable ($TMPDIR) controls the
location of several directories.

(b) It's hard for the library user to predict which directory
libguestfs will use, unless the user simulates the same internal steps
that libguestfs performs.

This commit fixes these issues.

(a) Now three environment variables control the location of all small
temporary files, and the appliance cache:

  For temporary files: $LIBGUESTFS_TMPDIR or $TMPDIR or /tmp.

  For the appliance cache: $LIBGUESTFS_CACHEDIR or $TMPDIR or /var/tmp.

The user can also set these directories explicitly through API calls
(guestfs_set_tmpdir and guestfs_set_cachedir).

(b) The user can also retrieve the actual directories that libguestfs
will use, by calling guestfs_get_tmpdir and guestfs_get_cachedir.
These functions are also used internally.

This commit also:

 - reworks the internal tmpdir code

 - removes the internal (undocumented) guestfs_tmpdir call (replacing
   it with calls to the documented guestfs_get_tmpdir API instead)

 - changes the ./run script to set LIBGUESTFS_TMPDIR and
   LIBGUESTFS_CACHEDIR

 - adds a test

 - fixes a few places like libguestfs-make-fixed-appliance which
   depended on $TMPDIR
---
 Makefile.am                                  |   1 +
 appliance/libguestfs-make-fixed-appliance.in |   8 +-
 configure.ac                                 |   1 +
 edit/virt-edit.c                             |   4 +-
 fish/fish.h                                  |   5 +-
 generator/actions.ml                         |  47 +++++++++++
 generator/c.ml                               |   2 -
 po/POTFILES                                  |   1 +
 run.in                                       |   7 +-
 src/Makefile.am                              |   1 +
 src/appliance.c                              |  35 +++++---
 src/filearch.c                               |   4 +-
 src/guestfs-internal.h                       |  27 ++++--
 src/guestfs.c                                |  19 ++++-
 src/guestfs.pod                              |  32 +++++--
 src/launch-libvirt.c                         |  13 +--
 src/launch.c                                 |  75 -----------------
 src/tmpdirs.c                                | 119 +++++++++++++++++++++++++++
 tests/tmpdirs/Makefile.am                    |  26 ++++++
 tests/tmpdirs/test-tmpdirs.pl                |  61 ++++++++++++++
 20 files changed, 358 insertions(+), 130 deletions(-)
 create mode 100644 src/tmpdirs.c
 create mode 100644 tests/tmpdirs/Makefile.am
 create mode 100755 tests/tmpdirs/test-tmpdirs.pl

diff --git a/Makefile.am b/Makefile.am
index 153a7e3..6a32d69 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -39,6 +39,7 @@ if ENABLE_APPLIANCE
 SUBDIRS += tests/qemu
 SUBDIRS += tests/guests
 SUBDIRS += tests/c-api
+SUBDIRS += tests/tmpdirs
 SUBDIRS += tests/protocol
 SUBDIRS += tests/disks
 SUBDIRS += tests/lvm
diff --git a/appliance/libguestfs-make-fixed-appliance.in b/appliance/libguestfs-make-fixed-appliance.in
index f427a51..08b882d 100644
--- a/appliance/libguestfs-make-fixed-appliance.in
+++ b/appliance/libguestfs-make-fixed-appliance.in
@@ -103,11 +103,9 @@ mkdir -p "$outputdir"
 guestfish -a /dev/null run
 
 # Find the location of the appliance.
-if [ -n "$TMPDIR" ]; then
-    appliancedir="$TMPDIR/.guestfs-$(id -u)"
-else
-    appliancedir="/var/tmp/.guestfs-$(id -u)"
-fi
+cachedir="$(guestfish get-cachedir)"
+euid="$(id -u)"
+appliancedir="$cachedir/.guestfs-$euid"
 
 cp "$appliancedir/kernel" "$outputdir/kernel"
 cp "$appliancedir/initrd" "$outputdir/initrd"
diff --git a/configure.ac b/configure.ac
index 13b05b2..087d0e6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1400,6 +1400,7 @@ AC_CONFIG_FILES([Makefile
                  tests/regressions/Makefile
                  tests/rsync/Makefile
                  tests/selinux/Makefile
+                 tests/tmpdirs/Makefile
                  tests/xml/Makefile
                  tools/Makefile])
 AC_OUTPUT
diff --git a/edit/virt-edit.c b/edit/virt-edit.c
index f763926..db48025 100644
--- a/edit/virt-edit.c
+++ b/edit/virt-edit.c
@@ -307,9 +307,11 @@ static void
 edit (const char *filename, const char *root)
 {
   char *filename_to_free = NULL;
-  const char *tmpdir = guestfs_tmpdir ();
+  char *tmpdir = guestfs_get_tmpdir (g);
   char tmpfile[strlen (tmpdir) + 32];
   sprintf (tmpfile, "%s/virteditXXXXXX", tmpdir);
+  free (tmpdir);
+
   int fd;
   char fdbuf[32];
   char *upload_from = NULL;
diff --git a/fish/fish.h b/fish/fish.h
index e14053a..4bccdf7 100644
--- a/fish/fish.h
+++ b/fish/fish.h
@@ -41,9 +41,10 @@
 #define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0)
 
 #define TMP_TEMPLATE_ON_STACK(var)                        \
-  const char *ttos_tmpdir = guestfs_tmpdir ();            \
+  char *ttos_tmpdir = guestfs_get_tmpdir (g);             \
   char var[strlen (ttos_tmpdir) + 32];                    \
-  sprintf (var, "%s/guestfishXXXXXX", ttos_tmpdir)        \
+  sprintf (var, "%s/guestfishXXXXXX", ttos_tmpdir);       \
+  free (ttos_tmpdir)
 
 /* in fish.c */
 extern guestfs_h *g;
diff --git a/generator/actions.ml b/generator/actions.ml
index 65d2785..e4f8dd4 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -2677,6 +2677,53 @@ This is the same as C<guestfs_parse_environment> except that
 it parses an explicit list of strings instead of the program's
 environment." };
 
+  { defaults with
+    name = "set_tmpdir";
+    style = RErr, [OptString "tmpdir"], [];
+    fish_alias = ["tmpdir"]; config_only = true;
+    blocking = false;
+    shortdesc = "set the temporary directory";
+    longdesc = "\
+Set the directory used by the handle to store temporary files.
+
+The environment variables C<LIBGUESTFS_TMPDIR> and C<TMPDIR>
+control the default value: If C<LIBGUESTFS_TMPDIR> is set, then
+that is the default.  Else if C<TMPDIR> is set, then that is
+the default.  Else C</tmp> is the default." };
+
+  { defaults with
+    name = "get_tmpdir";
+    style = RString "tmpdir", [], [];
+    blocking = false;
+    shortdesc = "get the temporary directory";
+    longdesc = "\
+Get the directory used by the handle to store temporary files." };
+
+  { defaults with
+    name = "set_cachedir";
+    style = RErr, [OptString "cachedir"], [];
+    fish_alias = ["cachedir"]; config_only = true;
+    blocking = false;
+    shortdesc = "set the appliance cache directory";
+    longdesc = "\
+Set the directory used by the handle to store the appliance
+cache, when using a supermin appliance.  The appliance is
+cached and shared between all handles which have the same
+effective user ID.
+
+The environment variables C<LIBGUESTFS_CACHEDIR> and C<TMPDIR>
+control the default value: If C<LIBGUESTFS_CACHEDIR> is set, then
+that is the default.  Else if C<TMPDIR> is set, then that is
+the default.  Else C</var/tmp> is the default." };
+
+  { defaults with
+    name = "get_cachedir";
+    style = RString "cachedir", [], [];
+    blocking = false;
+    shortdesc = "get the appliance cache directory";
+    longdesc = "\
+Get the directory used by the handle to store the appliance cache." };
+
 ]
 
 (* daemon_functions are any functions which cause some action
diff --git a/generator/c.ml b/generator/c.ml
index 76c7422..923758e 100644
--- a/generator/c.ml
+++ b/generator/c.ml
@@ -674,7 +674,6 @@ extern GUESTFS_DLL_PUBLIC void *guestfs_safe_malloc (guestfs_h *g, size_t nbytes
 extern GUESTFS_DLL_PUBLIC void *guestfs_safe_calloc (guestfs_h *g, size_t n, size_t s);
 extern GUESTFS_DLL_PUBLIC char *guestfs_safe_strdup (guestfs_h *g, const char *str);
 extern GUESTFS_DLL_PUBLIC void *guestfs_safe_memdup (guestfs_h *g, const void *ptr, size_t size);
-extern GUESTFS_DLL_PUBLIC const char *guestfs_tmpdir (void);
 #ifdef GUESTFS_PRIVATE_FOR_EACH_DISK
 extern GUESTFS_DLL_PUBLIC int guestfs___for_each_disk (guestfs_h *g, virDomainPtr dom, int (*)(guestfs_h *g, const char *filename, const char *format, int readonly, void *data), void *data);
 #endif
@@ -1584,7 +1583,6 @@ and generate_linker_script () =
     "guestfs_safe_malloc";
     "guestfs_safe_strdup";
     "guestfs_safe_memdup";
-    "guestfs_tmpdir";
     "guestfs___for_each_disk";
   ] in
   let functions =
diff --git a/po/POTFILES b/po/POTFILES
index 3b2c89c..fcaa77c 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -254,6 +254,7 @@ src/libvirt-domain.c
 src/listfs.c
 src/match.c
 src/proto.c
+src/tmpdirs.c
 test-tool/test-tool.c
 tools/virt-list-filesystems.pl
 tools/virt-list-partitions.pl
diff --git a/run.in b/run.in
index 334694f..f0357b2 100755
--- a/run.in
+++ b/run.in
@@ -43,12 +43,13 @@ fi
 # Find this script.
 b=@abs_builddir@
 
-# Set TMPDIR so the appliance doesn't conflict with globally
-# installed libguestfs.
+# Set tmpdir and cachedir so the appliance doesn't conflict with
+# globally installed libguestfs.
 #
 # We set it to a subdirectory ('tmp') so that we can label this
 # subdirectory to make libvirt + sVirt + SELinux enforcing work.
-export TMPDIR="$b/tmp"
+export LIBGUESTFS_TMPDIR="$b/tmp"
+export LIBGUESTFS_CACHEDIR="$b/tmp"
 mkdir -p "$b/tmp"
 chcon --reference=/tmp tmp 2>/dev/null ||:
 
diff --git a/src/Makefile.am b/src/Makefile.am
index d3755be..7b7f606 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -165,6 +165,7 @@ libguestfs_la_SOURCES = \
 	listfs.c \
 	match.c \
 	proto.c \
+	tmpdirs.c \
 	libguestfs.syms
 
 libguestfs_la_LIBADD = \
diff --git a/src/appliance.c b/src/appliance.c
index 2241e18..8a22064 100644
--- a/src/appliance.c
+++ b/src/appliance.c
@@ -354,7 +354,7 @@ check_for_cached_appliance (guestfs_h *g,
                             uid_t uid,
                             char **kernel, char **initrd, char **appliance)
 {
-  const char *tmpdir = guestfs___persistent_tmpdir ();
+  char *tmpdir = guestfs_get_cachedir (g);
 
   /* len must be longer than the length of any pathname we can
    * generate in this function.
@@ -365,6 +365,8 @@ check_for_cached_appliance (guestfs_h *g,
   char filename[len];
   snprintf (filename, len, "%s/checksum", cachedir);
 
+  free (tmpdir);
+
   (void) mkdir (cachedir, 0755);
 
   /* See if the cache directory exists and passes some simple checks
@@ -472,15 +474,18 @@ build_supermin_appliance (guestfs_h *g,
                           uid_t uid,
                           char **kernel, char **initrd, char **appliance)
 {
+  char *tmpdir;
+  size_t len;
+
   if (g->verbose)
     guestfs___print_timestamped_message (g, "begin building supermin appliance");
 
-  const char *tmpdir = guestfs___persistent_tmpdir ();
+  tmpdir = guestfs_get_cachedir (g);
 
   /* len must be longer than the length of any pathname we can
    * generate in this function.
    */
-  size_t len = strlen (tmpdir) + 128;
+  len = strlen (tmpdir) + 128;
 
   /* Build the appliance into a temporary directory. */
   char tmpcd[len];
@@ -488,6 +493,7 @@ build_supermin_appliance (guestfs_h *g,
 
   if (mkdtemp (tmpcd) == NULL) {
     perrorf (g, "mkdtemp");
+    free (tmpdir);
     return -1;
   }
 
@@ -496,7 +502,8 @@ build_supermin_appliance (guestfs_h *g,
 
   int r = run_supermin_helper (g, supermin_path, tmpcd);
   if (r == -1) {
-    guestfs___remove_tmpdir (g, tmpcd);
+    guestfs___recursive_remove_dir (g, tmpcd);
+    free (tmpdir);
     return -1;
   }
 
@@ -509,13 +516,15 @@ build_supermin_appliance (guestfs_h *g,
   char filename2[len];
   snprintf (filename, len, "%s/checksum", cachedir);
 
+  free (tmpdir);
+
   /* Open and acquire write lock on checksum file.  The file might
    * not exist, in which case we want to create it.
    */
   int fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0755);
   if (fd == -1) {
     perrorf (g, "open: %s", filename);
-    guestfs___remove_tmpdir (g, tmpcd);
+    guestfs___recursive_remove_dir (g, tmpcd);
     return -1;
   }
   struct flock fl;
@@ -529,7 +538,7 @@ build_supermin_appliance (guestfs_h *g,
       goto again;
     perrorf (g, "fcntl: F_SETLKW: %s", filename);
     close (fd);
-    guestfs___remove_tmpdir (g, tmpcd);
+    guestfs___recursive_remove_dir (g, tmpcd);
     return -1;
   }
 
@@ -541,7 +550,7 @@ build_supermin_appliance (guestfs_h *g,
   if (ftruncate (fd, clen) == -1) {
     perrorf (g, "ftruncate: %s", filename);
     close (fd);
-    guestfs___remove_tmpdir (g, tmpcd);
+    guestfs___recursive_remove_dir (g, tmpcd);
     return -1;
   }
 
@@ -549,13 +558,13 @@ build_supermin_appliance (guestfs_h *g,
   if (rr == -1) {
     perrorf (g, "write: %s", filename);
     close (fd);
-    guestfs___remove_tmpdir (g, tmpcd);
+    guestfs___recursive_remove_dir (g, tmpcd);
     return -1;
   }
   if ((size_t) rr != clen) {
     error (g, "partial write: %s", filename);
     close (fd);
-    guestfs___remove_tmpdir (g, tmpcd);
+    guestfs___recursive_remove_dir (g, tmpcd);
     return -1;
   }
 
@@ -565,7 +574,7 @@ build_supermin_appliance (guestfs_h *g,
   if (rename (filename, filename2) == -1) {
     perrorf (g, "rename: %s %s", filename, filename2);
     close (fd);
-    guestfs___remove_tmpdir (g, tmpcd);
+    guestfs___recursive_remove_dir (g, tmpcd);
     return -1;
   }
 
@@ -575,7 +584,7 @@ build_supermin_appliance (guestfs_h *g,
   if (rename (filename, filename2) == -1) {
     perrorf (g, "rename: %s %s", filename, filename2);
     close (fd);
-    guestfs___remove_tmpdir (g, tmpcd);
+    guestfs___recursive_remove_dir (g, tmpcd);
     return -1;
   }
 
@@ -585,11 +594,11 @@ build_supermin_appliance (guestfs_h *g,
   if (rename (filename, filename2) == -1) {
     perrorf (g, "rename: %s %s", filename, filename2);
     close (fd);
-    guestfs___remove_tmpdir (g, tmpcd);
+    guestfs___recursive_remove_dir (g, tmpcd);
     return -1;
   }
 
-  guestfs___remove_tmpdir (g, tmpcd);
+  guestfs___recursive_remove_dir (g, tmpcd);
 
   /* Now finish off by linking to the cached appliance and returning it. */
   if (hard_link_to_cached_appliance (g, cachedir,
diff --git a/src/filearch.c b/src/filearch.c
index 93a697f..1ad6af6 100644
--- a/src/filearch.c
+++ b/src/filearch.c
@@ -127,7 +127,7 @@ is_regular_file (const char *filename)
 static char *
 cpio_arch (guestfs_h *g, const char *file, const char *path)
 {
-  TMP_TEMPLATE_ON_STACK (dir);
+  TMP_TEMPLATE_ON_STACK (g, dir);
 #define dir_len (strlen (dir))
 #define initrd_len (dir_len + 16)
   char initrd[initrd_len];
@@ -221,7 +221,7 @@ cpio_arch (guestfs_h *g, const char *file, const char *path)
   if (cmd)
     guestfs___cmd_close (cmd);
 
-  guestfs___remove_tmpdir (g, dir);
+  guestfs___recursive_remove_dir (g, dir);
 
   return ret;
 #undef dir_len
diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h
index aed4182..17903d3 100644
--- a/src/guestfs-internal.h
+++ b/src/guestfs-internal.h
@@ -71,10 +71,11 @@
 #define TRACE4(name, arg1, arg2, arg3, arg4)
 #endif
 
-#define TMP_TEMPLATE_ON_STACK(var)                        \
-  const char *ttos_tmpdir = guestfs_tmpdir ();            \
+#define TMP_TEMPLATE_ON_STACK(g,var)                      \
+  char *ttos_tmpdir = guestfs_get_tmpdir (g);             \
   char var[strlen (ttos_tmpdir) + 32];                    \
-  sprintf (var, "%s/libguestfsXXXXXX", ttos_tmpdir)       \
+  sprintf (var, "%s/libguestfsXXXXXX", ttos_tmpdir);      \
+  free (ttos_tmpdir)
 
 #ifdef __APPLE__
 #define UNIX_PATH_MAX 104
@@ -234,11 +235,19 @@ struct guestfs_h
   const struct attach_ops *attach_ops;
 
   /**** Runtime information. ****/
-  char *tmpdir;			/* Temporary directory containing socket. */
-
   char *last_error;             /* Last error on handle. */
   int last_errnum;              /* errno, or 0 if there was no errno */
 
+  /* Temporary and cache directories. */
+  /* The actual temporary directory - this is not created with the
+   * handle, you have to call guestfs___lazy_make_tmpdir.
+   */
+  char *tmpdir;
+  /* Environment variables that affect tmpdir/cachedir locations. */
+  char *env_tmpdir;             /* $TMPDIR (NULL if not set) */
+  char *int_tmpdir;   /* $LIBGUESTFS_TMPDIR or guestfs_set_tmpdir or NULL */
+  char *int_cachedir; /* $LIBGUESTFS_CACHEDIR or guestfs_set_cachedir or NULL */
+
   /* Callbacks. */
   guestfs_abort_cb           abort_cb;
   guestfs_error_handler_cb   error_cb;
@@ -526,13 +535,15 @@ extern void guestfs___call_callbacks_void (guestfs_h *g, uint64_t event);
 extern void guestfs___call_callbacks_message (guestfs_h *g, uint64_t event, const char *buf, size_t buf_len);
 extern void guestfs___call_callbacks_array (guestfs_h *g, uint64_t event, const uint64_t *array, size_t array_len);
 
+/* tmpdirs.c */
+extern int guestfs___lazy_make_tmpdir (guestfs_h *g);
+extern void guestfs___remove_tmpdir (guestfs_h *g);
+extern void guestfs___recursive_remove_dir (guestfs_h *g, const char *dir);
+
 /* appliance.c */
 extern int guestfs___build_appliance (guestfs_h *g, char **kernel, char **initrd, char **appliance);
 
 /* launch.c */
-extern const char *guestfs___persistent_tmpdir (void);
-extern int guestfs___lazy_make_tmpdir (guestfs_h *g);
-extern void guestfs___remove_tmpdir (guestfs_h *g, const char *dir);
 extern int64_t guestfs___timeval_diff (const struct timeval *x, const struct timeval *y);
 extern void guestfs___print_timestamped_message (guestfs_h *g, const char *fs, ...);
 extern void guestfs___launch_send_progress (guestfs_h *g, int perdozen);
diff --git a/src/guestfs.c b/src/guestfs.c
index 5428b28..4a1ee93 100644
--- a/src/guestfs.c
+++ b/src/guestfs.c
@@ -195,6 +195,18 @@ parse_environment (guestfs_h *g,
   if (str != NULL && STREQ (str, "1"))
     guestfs_set_verbose (g, 1);
 
+  str = do_getenv (data, "LIBGUESTFS_TMPDIR");
+  if (str)
+    guestfs_set_tmpdir (g, str);
+
+  str = do_getenv (data, "LIBGUESTFS_CACHEDIR");
+  if (str)
+    guestfs_set_cachedir (g, str);
+
+  free (g->env_tmpdir);
+  str = do_getenv (data, "TMPDIR");
+  g->env_tmpdir = str ? safe_strdup (g, str) : NULL;
+
   str = do_getenv (data, "LIBGUESTFS_PATH");
   if (str)
     guestfs_set_path (g, str);
@@ -300,14 +312,13 @@ guestfs_close (guestfs_h *g)
   /* Run user close callbacks. */
   guestfs___call_callbacks_void (g, GUESTFS_EVENT_CLOSE);
 
-  /* Remove whole temporary directory. */
-  if (g->tmpdir)
-    guestfs___remove_tmpdir (g, g->tmpdir);
-
   /* Test output file used by bindtests. */
   if (g->test_fp != NULL)
     fclose (g->test_fp);
 
+  /* Remove temporary directory. */
+  guestfs___remove_tmpdir (g);
+
   /* Mark the handle as dead and then free up all memory. */
   g->state = NO_HANDLE;
 
diff --git a/src/guestfs.pod b/src/guestfs.pod
index f243c2c..bac81d8 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -2934,7 +2934,7 @@ C<febootstrap-supermin-helper> is invoked to create the kernel, a
 small initrd and the appliance.
 
 The appliance is cached in C</var/tmp/.guestfs-E<lt>UIDE<gt>> (or in
-another directory if C<TMPDIR> is set).
+another directory if C<LIBGUESTFS_CACHEDIR> or C<TMPDIR> are set).
 
 For a complete description of how the appliance is created and cached,
 read the L<febootstrap(8)> and L<febootstrap-supermin-helper(8)> man
@@ -3890,6 +3890,17 @@ Pass additional options to the guest kernel.
 Choose the default way to create the appliance.  See
 L</guestfs_set_attach_method>.
 
+=item LIBGUESTFS_CACHEDIR
+
+The location where libguestfs will cache its appliance, when
+using a supermin appliance.  The appliance is cached and shared
+between all handles which have the same effective user ID.
+
+If C<LIBGUESTFS_CACHEDIR> is not set, then C<TMPDIR> is used.  If
+C<TMPDIR> is not set, then C</var/tmp> is used.
+
+See also L</LIBGUESTFS_TMPDIR>, L</guestfs_set_cachedir>.
+
 =item LIBGUESTFS_DEBUG
 
 Set C<LIBGUESTFS_DEBUG=1> to enable verbose messages.  This
@@ -3915,6 +3926,16 @@ used.
 
 See also L</QEMU WRAPPERS> above.
 
+=item LIBGUESTFS_TMPDIR
+
+The location where libguestfs will store temporary files used
+by each handle.
+
+If C<LIBGUESTFS_TMPDIR> is not set, then C<TMPDIR> is used.  If
+C<TMPDIR> is not set, then C</tmp> is used.
+
+See also L</LIBGUESTFS_CACHEDIR>, L</guestfs_set_tmpdir>.
+
 =item LIBGUESTFS_TRACE
 
 Set C<LIBGUESTFS_TRACE=1> to enable command traces.  This
@@ -3922,14 +3943,7 @@ has the same effect as calling C<guestfs_set_trace (g, 1)>.
 
 =item TMPDIR
 
-Location of temporary directory, defaults to C</tmp> except for the
-cached supermin appliance which defaults to C</var/tmp>.
-
-If libguestfs was compiled to use the supermin appliance then the
-real appliance is cached in this directory, shared between all
-handles belonging to the same EUID.  You can use C<$TMPDIR> to
-configure another directory to use in case C</var/tmp> is not large
-enough.
+See L</LIBGUESTFS_CACHEDIR>, L</LIBGUESTFS_TMPDIR>.
 
 =back
 
diff --git a/src/launch-libvirt.c b/src/launch-libvirt.c
index c243fcb..e4df2a6 100644
--- a/src/launch-libvirt.c
+++ b/src/launch-libvirt.c
@@ -1043,16 +1043,15 @@ static int
 construct_libvirt_xml_qemu_cmdline (guestfs_h *g, xmlTextWriterPtr xo)
 {
   struct qemu_param *qp;
-  const char *tmpdir;
+  char *tmpdir;
 
   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "qemu:commandline"));
 
-  /* We need to ensure the snapshots are created in $TMPDIR (RHBZ#856619).
-   * If TMPDIR is not set, we must choose one, because otherwise libvirt
-   * will use a random TMPDIR (RHBZ#865464).  Luckily the
-   * guestfs___persistent_tmpdir function does both of these tasks.
+  /* We need to ensure the snapshots are created in the persistent
+   * temporary directory (RHBZ#856619).  We must set one, because
+   * otherwise libvirt will use a random TMPDIR (RHBZ#865464).
    */
-  tmpdir = guestfs___persistent_tmpdir ();
+  tmpdir = guestfs_get_cachedir (g);
 
   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "qemu:env"));
   XMLERROR (-1,
@@ -1063,6 +1062,8 @@ construct_libvirt_xml_qemu_cmdline (guestfs_h *g, xmlTextWriterPtr xo)
                                          BAD_CAST tmpdir));
   XMLERROR (-1, xmlTextWriterEndElement (xo));
 
+  free (tmpdir);
+
   /* Workaround because libvirt user networking cannot specify "net="
    * parameter.
    */
diff --git a/src/launch.c b/src/launch.c
index 4681707..d2c1a42 100644
--- a/src/launch.c
+++ b/src/launch.c
@@ -701,81 +701,6 @@ guestfs___launch_failed_error (guestfs_h *g)
                 "and/or run 'libguestfs-test-tool'."));
 }
 
-/* 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)
-{
-  const char *tmpdir;
-
-#ifdef P_tmpdir
-  tmpdir = P_tmpdir;
-#else
-  tmpdir = "/tmp";
-#endif
-
-  const char *t = getenv ("TMPDIR");
-  if (t) tmpdir = t;
-
-  return tmpdir;
-}
-
-/* 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
- */
-const char *
-guestfs___persistent_tmpdir (void)
-{
-  const char *tmpdir;
-
-  tmpdir = "/var/tmp";
-
-  const char *t = getenv ("TMPDIR");
-  if (t) tmpdir = t;
-
-  return tmpdir;
-}
-
-/* The g->tmpdir (per-handle temporary directory) is not created when
- * the handle is created.  Instead we create it lazily before the
- * first time it is used, or during launch.
- */
-int
-guestfs___lazy_make_tmpdir (guestfs_h *g)
-{
-  if (!g->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);
-      return -1;
-    }
-  }
-  return 0;
-}
-
-/* Recursively remove a temporary directory.  If removal fails, just
- * return (it's a temporary directory so it'll eventually be cleaned
- * up by a temp cleaner).  This is done using "rm -rf" because that's
- * simpler and safer.
- */
-void
-guestfs___remove_tmpdir (guestfs_h *g, const char *dir)
-{
-  struct command *cmd;
-
-  cmd = guestfs___new_command (g);
-  guestfs___cmd_add_arg (cmd, "rm");
-  guestfs___cmd_add_arg (cmd, "-rf");
-  guestfs___cmd_add_arg (cmd, dir);
-  /* Ignore failures. */
-  guestfs___cmd_run (cmd);
-  guestfs___cmd_close (cmd);
-}
-
 int
 guestfs__get_pid (guestfs_h *g)
 {
diff --git a/src/tmpdirs.c b/src/tmpdirs.c
new file mode 100644
index 0000000..b7167a9
--- /dev/null
+++ b/src/tmpdirs.c
@@ -0,0 +1,119 @@
+/* libguestfs
+ * Copyright (C) 2012 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "guestfs.h"
+#include "guestfs-internal.h"
+#include "guestfs-internal-actions.h"
+
+int
+guestfs__set_tmpdir (guestfs_h *g, const char *tmpdir)
+{
+  free (g->int_tmpdir);
+  g->int_tmpdir = tmpdir ? safe_strdup (g, tmpdir) : NULL;
+  return 0;
+}
+
+/* Note this actually calculates the tmpdir, so it never returns NULL. */
+char *
+guestfs__get_tmpdir (guestfs_h *g)
+{
+  const char *str;
+
+  if (g->int_tmpdir)
+    str = g->int_tmpdir;
+  else if (g->env_tmpdir)
+    str = g->env_tmpdir;
+  else
+    str = "/tmp";
+
+  return safe_strdup (g, str);
+}
+
+int
+guestfs__set_cachedir (guestfs_h *g, const char *cachedir)
+{
+  free (g->int_cachedir);
+  g->int_cachedir = cachedir ? safe_strdup (g, cachedir) : NULL;
+  return 0;
+}
+
+/* Note this actually calculates the cachedir, so it never returns NULL. */
+char *
+guestfs__get_cachedir (guestfs_h *g)
+{
+  const char *str;
+
+  if (g->int_cachedir)
+    str = g->int_cachedir;
+  else if (g->env_tmpdir)
+    str = g->env_tmpdir;
+  else
+    str = "/var/tmp";
+
+  return safe_strdup (g, str);
+}
+
+/* The g->tmpdir (per-handle temporary directory) is not created when
+ * the handle is created.  Instead we create it lazily before the
+ * first time it is used, or during launch.
+ */
+int
+guestfs___lazy_make_tmpdir (guestfs_h *g)
+{
+  if (!g->tmpdir) {
+    TMP_TEMPLATE_ON_STACK (g, dir_template);
+    g->tmpdir = safe_strdup (g, dir_template);
+    if (mkdtemp (g->tmpdir) == NULL) {
+      perrorf (g, _("%s: cannot create temporary directory"), dir_template);
+      return -1;
+    }
+  }
+  return 0;
+}
+
+/* Recursively remove a temporary directory.  If removal fails, just
+ * return (it's a temporary directory so it'll eventually be cleaned
+ * up by a temp cleaner).  This is done using "rm -rf" because that's
+ * simpler and safer.
+ */
+void
+guestfs___recursive_remove_dir (guestfs_h *g, const char *dir)
+{
+  struct command *cmd;
+
+  cmd = guestfs___new_command (g);
+  guestfs___cmd_add_arg (cmd, "rm");
+  guestfs___cmd_add_arg (cmd, "-rf");
+  guestfs___cmd_add_arg (cmd, dir);
+  /* Ignore failures. */
+  guestfs___cmd_run (cmd);
+  guestfs___cmd_close (cmd);
+}
+
+void
+guestfs___remove_tmpdir (guestfs_h *g)
+{
+  if (g->tmpdir)
+    guestfs___recursive_remove_dir (g, g->tmpdir);
+}
diff --git a/tests/tmpdirs/Makefile.am b/tests/tmpdirs/Makefile.am
new file mode 100644
index 0000000..32fd632
--- /dev/null
+++ b/tests/tmpdirs/Makefile.am
@@ -0,0 +1,26 @@
+# libguestfs
+# Copyright (C) 2012 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+include $(top_srcdir)/subdir-rules.mk
+
+TESTS = \
+	test-tmpdirs.pl
+
+TESTS_ENVIRONMENT = $(top_builddir)/run --test
+
+EXTRA_DIST = \
+	$(TESTS)
diff --git a/tests/tmpdirs/test-tmpdirs.pl b/tests/tmpdirs/test-tmpdirs.pl
new file mode 100755
index 0000000..1d68c64
--- /dev/null
+++ b/tests/tmpdirs/test-tmpdirs.pl
@@ -0,0 +1,61 @@
+#!/usr/bin/perl
+# Copyright (C) 2012 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+# Test logic for setting location of tmpdir and cachedir.
+
+use strict;
+use warnings;
+
+use Sys::Guestfs;
+
+# Remove any environment variables that may have been set by the
+# user or the ./run script which could affect this test.
+delete $ENV{LIBGUESTFS_TMPDIR};
+delete $ENV{LIBGUESTFS_CACHEDIR};
+delete $ENV{TMPDIR};
+
+my $g;
+
+# Defaults with no environment variables set.
+$g = Sys::Guestfs->new ();
+die unless $g->get_tmpdir () eq "/tmp";
+die unless $g->get_cachedir () eq "/var/tmp";
+
+# Setting environment variables.
+$ENV{LIBGUESTFS_TMPDIR} = "a";
+$ENV{LIBGUESTFS_CACHEDIR} = "b";
+$ENV{TMPDIR} = "c";
+
+$g = Sys::Guestfs->new ();
+die unless $g->get_tmpdir () eq "a";
+die unless $g->get_cachedir () eq "b";
+
+# Creating a handle which isn't affected by environment variables.
+$g = Sys::Guestfs->new (environment => 0);
+die unless $g->get_tmpdir () eq "/tmp";
+die unless $g->get_cachedir () eq "/var/tmp";
+
+# Uses TMPDIR if the others are not set.
+delete $ENV{LIBGUESTFS_TMPDIR};
+$g = Sys::Guestfs->new ();
+die unless $g->get_tmpdir () eq "c";
+die unless $g->get_cachedir () eq "b";
+
+delete $ENV{LIBGUESTFS_CACHEDIR};
+$g = Sys::Guestfs->new ();
+die unless $g->get_tmpdir () eq "c";
+die unless $g->get_cachedir () eq "c";
-- 
1.7.11.7



More information about the Libguestfs mailing list