summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rw-r--r--appliance/libguestfs-make-fixed-appliance.in8
-rw-r--r--configure.ac1
-rw-r--r--edit/virt-edit.c4
-rw-r--r--fish/fish.h5
-rw-r--r--generator/actions.ml47
-rw-r--r--generator/c.ml2
-rw-r--r--po/POTFILES1
-rwxr-xr-xrun.in7
-rw-r--r--src/Makefile.am1
-rw-r--r--src/appliance.c35
-rw-r--r--src/filearch.c4
-rw-r--r--src/guestfs-internal.h27
-rw-r--r--src/guestfs.c19
-rw-r--r--src/guestfs.pod32
-rw-r--r--src/launch-libvirt.c13
-rw-r--r--src/launch.c75
-rw-r--r--src/tmpdirs.c119
-rw-r--r--tests/tmpdirs/Makefile.am26
-rwxr-xr-xtests/tmpdirs/test-tmpdirs.pl61
20 files changed, 358 insertions, 130 deletions
diff --git a/Makefile.am b/Makefile.am
index 153a7e37..6a32d69f 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 f427a51d..08b882d6 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 13b05b23..087d0e62 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 f7639264..db480255 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 e14053ad..4bccdf7d 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 65d27852..e4f8dd41 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 76c74226..923758e4 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 3b2c89c5..fcaa77ce 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 334694f3..f0357b2e 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 d3755beb..7b7f6065 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 2241e181..8a22064b 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 93a697f5..1ad6af68 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 aed4182c..17903d30 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 5428b28a..4a1ee930 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 f243c2c4..bac81d86 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 c243fcbb..e4df2a6b 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 46817073..d2c1a42a 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 00000000..b7167a9d
--- /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 00000000..32fd6326
--- /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 00000000..1d68c640
--- /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";