summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog29
-rw-r--r--lib/clean-temp-private.h82
-rw-r--r--lib/clean-temp-simple.c367
-rw-r--r--lib/clean-temp-simple.h51
-rw-r--r--lib/clean-temp.c393
-rw-r--r--lib/clean-temp.h25
-rw-r--r--modules/clean-temp6
-rw-r--r--modules/clean-temp-simple42
8 files changed, 599 insertions, 396 deletions
diff --git a/ChangeLog b/ChangeLog
index db793f8917..d16c61e692 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,34 @@
2021-03-22 Bruno Haible <bruno@clisp.org>
+ clean-temp-simple: New module.
+ * lib/clean-temp-simple.h: New file, based on lib/clean-temp.h.
+ * lib/clean-temp-private.h: New file, based on lib/clean-temp.c.
+ * lib/clean-temp-simple.c: New file, based on lib/clean-temp.c.
+ * lib/clean-temp.h: Include clean-temp-simple.h.
+ (register_temporary_file, unregister_temporary_file,
+ cleanup_temporary_file): Remove declarations.
+ * lib/clean-temp.c: Don't include <limits.h>, <stdint.h>. Include
+ <signal.h>, <stdio.h>, clean-temp-simple.h, clean-temp-private.h.
+ (file_cleanup_list_lock, file_cleanup_list): Moved to
+ clean-temp-simple.c.
+ (struct tempdir, dir_cleanup_list, struct closeable_fd, descriptors):
+ Moved to clean-temp-private.h.
+ (string_equals, SIZE_BITS, string_hash, fatal_signal_set,
+ init_fatal_signal_set, asyncsafe_close): Moved to clean-temp-simple.c.
+ (asyncsafe_fclose_variant): Use get_fatal_signal_set() here.
+ (cleanup_action, do_init_clean_temp, clean_temp_once, init_clean_temp,
+ register_temporary_file, unregister_temporary_file, do_unlink,
+ cleanup_temporary_file): Moved to clean-temp-simple.c.
+ (create_temp_dir, cleanup_temp_file, cleanup_temp_dir_contents,
+ gen_register_open_temp, close_temp): Update for changed function names.
+ (fclose_variant_temp): Don't call init_fatal_signal_set().
+ * modules/clean-temp-simple: New file, based on modules/clean-temp.
+ * modules/clean-temp (Depends-on): Add clean-temp-simple, list. Remove
+ stdint.
+ (configure.ac): Don't define SIGNAL_SAFE_LIST here.
+
+2021-03-22 Bruno Haible <bruno@clisp.org>
+
error: Relicense under LGPLv2+.
Pino Toscano's approval is in
<https://lists.gnu.org/archive/html/bug-gnulib/2021-03/msg00109.html>.
diff --git a/lib/clean-temp-private.h b/lib/clean-temp-private.h
new file mode 100644
index 0000000000..655eb3fbbf
--- /dev/null
+++ b/lib/clean-temp-private.h
@@ -0,0 +1,82 @@
+/* Private interface between modules 'clean-temp-simple' and 'clean-temp'.
+ Copyright (C) 2006-2021 Free Software Foundation, 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 3 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, see <https://www.gnu.org/licenses/>. */
+
+#ifndef _CLEAN_TEMP_PRIVATE_H
+#define _CLEAN_TEMP_PRIVATE_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include "gl_list.h"
+#include "asyncsafe-spin.h"
+
+/* The use of 'volatile' in the types below (and ISO C 99 section 5.1.2.3.(5))
+ ensure that while constructing or modifying the data structures, the field
+ values are written to memory in the order of the C statements. So the
+ signal handler can rely on these field values to be up to date. */
+
+/* Registry for a single temporary directory.
+ 'struct temp_dir' from the public header file overlaps with this. */
+struct tempdir
+{
+ /* The absolute pathname of the directory. */
+ char * volatile dirname;
+ /* Whether errors during explicit cleanup are reported to standard error. */
+ bool cleanup_verbose;
+ /* Absolute pathnames of subdirectories. */
+ gl_list_t /* <char *> */ volatile subdirs;
+ /* Absolute pathnames of files. */
+ gl_list_t /* <char *> */ volatile files;
+};
+
+/* List of all temporary directories. */
+struct all_tempdirs
+{
+ struct tempdir * volatile * volatile tempdir_list;
+ size_t volatile tempdir_count;
+ size_t tempdir_allocated;
+};
+#define dir_cleanup_list clean_temp_dir_cleanup_list
+extern struct all_tempdirs dir_cleanup_list;
+
+/* A file descriptor to be closed.
+ In multithreaded programs, it is forbidden to close the same fd twice,
+ because you never know what unrelated open() calls are being executed in
+ other threads. So, the 'close (fd)' must be guarded by a once-only guard. */
+struct closeable_fd
+{
+ /* The file descriptor to close. */
+ int volatile fd;
+ /* Set to true when it has been closed. */
+ bool volatile closed;
+ /* Lock that protects the fd from being closed twice. */
+ asyncsafe_spinlock_t lock;
+ /* Tells whether this list element has been done and can be freed. */
+ bool volatile done;
+};
+#define descriptors clean_temp_descriptors
+extern gl_list_t /* <closeable_fd *> */ volatile descriptors;
+
+extern bool clean_temp_string_equals (const void *x1, const void *x2);
+extern size_t clean_temp_string_hash (const void *x);
+
+extern _GL_ASYNC_SAFE int clean_temp_asyncsafe_close (struct closeable_fd *element);
+extern void clean_temp_init_asyncsafe_close (void);
+
+extern void clean_temp_init (void);
+
+extern int clean_temp_unlink (const char *absolute_file_name, bool cleanup_verbose);
+
+#endif /* _CLEAN_TEMP_PRIVATE_H */
diff --git a/lib/clean-temp-simple.c b/lib/clean-temp-simple.c
new file mode 100644
index 0000000000..e4fd3e8e0d
--- /dev/null
+++ b/lib/clean-temp-simple.c
@@ -0,0 +1,367 @@
+/* Temporary files with automatic cleanup.
+ Copyright (C) 2006-2021 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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 3 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, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include "clean-temp-simple.h"
+#include "clean-temp-private.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "error.h"
+#include "fatal-signal.h"
+#include "asyncsafe-spin.h"
+#include "xalloc.h"
+#include "glthread/lock.h"
+#include "thread-optim.h"
+#include "gl_xlist.h"
+#include "gl_linkedhash_list.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+
+/* Lock that protects the file_cleanup_list from concurrent modification in
+ different threads. */
+gl_lock_define_initialized (static, file_cleanup_list_lock)
+
+/* List of all temporary files without temporary directories. */
+static gl_list_t /* <char *> */ volatile file_cleanup_list;
+
+
+/* List of all temporary directories. */
+struct all_tempdirs dir_cleanup_list /* = { NULL, 0, 0 } */;
+
+
+/* List of all open file descriptors to temporary files. */
+gl_list_t /* <closeable_fd *> */ volatile descriptors;
+
+
+/* For the subdirs and for the files, we use a gl_list_t of type LINKEDHASH.
+ Why? We need a data structure that
+
+ 1) Can contain an arbitrary number of 'char *' values. The strings
+ are compared via strcmp, not pointer comparison.
+ 2) Has insertion and deletion operations that are fast: ideally O(1),
+ or possibly O(log n). This is important for GNU sort, which may
+ create a large number of temporary files.
+ 3) Allows iteration through all elements from within a signal handler.
+ 4) May or may not allow duplicates. It doesn't matter here, since
+ any file or subdir can only be removed once.
+
+ Criterion 1) would allow any gl_list_t or gl_oset_t implementation.
+
+ Criterion 2) leaves only GL_LINKEDHASH_LIST, GL_TREEHASH_LIST, or
+ GL_TREE_OSET.
+
+ Criterion 3) puts at disadvantage GL_TREEHASH_LIST and GL_TREE_OSET.
+ Namely, iteration through the elements of a binary tree requires access
+ to many ->left, ->right, ->parent pointers. However, the rebalancing
+ code for insertion and deletion in an AVL or red-black tree is so
+ complicated that we cannot assume that >left, ->right, ->parent pointers
+ are in a consistent state throughout these operations. Therefore, to
+ avoid a crash in the signal handler, all destructive operations to the
+ lists would have to be protected by a
+ block_fatal_signals ();
+ ...
+ unblock_fatal_signals ();
+ pair. Which causes extra system calls.
+
+ Criterion 3) would also discourage GL_ARRAY_LIST and GL_CARRAY_LIST,
+ if they were not already excluded. Namely, these implementations use
+ xrealloc(), leaving a time window in which in the list->elements pointer
+ points to already deallocated memory. To avoid a crash in the signal
+ handler at such a moment, all destructive operations would have to
+ protected by block/unblock_fatal_signals (), in this case too.
+
+ A list of type GL_LINKEDHASH_LIST without duplicates fulfills all
+ requirements:
+ 2) Insertion and deletion are O(1) on average.
+ 3) The gl_list_iterator, gl_list_iterator_next implementations do
+ not trigger memory allocations, nor other system calls, and are
+ therefore safe to be called from a signal handler.
+ Furthermore, since SIGNAL_SAFE_LIST is defined, the implementation
+ of the destructive functions ensures that the list structure is
+ safe to be traversed at any moment, even when interrupted by an
+ asynchronous signal.
+ */
+
+/* String equality and hash code functions used by the lists. */
+
+bool
+clean_temp_string_equals (const void *x1, const void *x2)
+{
+ const char *s1 = (const char *) x1;
+ const char *s2 = (const char *) x2;
+ return strcmp (s1, s2) == 0;
+}
+
+#define SIZE_BITS (sizeof (size_t) * CHAR_BIT)
+
+/* A hash function for NUL-terminated char* strings using
+ the method described by Bruno Haible.
+ See https://www.haible.de/bruno/hashfunc.html. */
+size_t
+clean_temp_string_hash (const void *x)
+{
+ const char *s = (const char *) x;
+ size_t h = 0;
+
+ for (; *s; s++)
+ h = *s + ((h << 9) | (h >> (SIZE_BITS - 9)));
+
+ return h;
+}
+
+
+/* The set of fatal signal handlers.
+ Cached here because we are not allowed to call get_fatal_signal_set ()
+ from a signal handler. */
+static const sigset_t *fatal_signal_set /* = NULL */;
+
+static void
+init_fatal_signal_set (void)
+{
+ if (fatal_signal_set == NULL)
+ fatal_signal_set = get_fatal_signal_set ();
+}
+
+
+/* Close a file descriptor.
+ Avoids race conditions with normal thread code or signal-handler code that
+ might want to close the same file descriptor. */
+_GL_ASYNC_SAFE int
+clean_temp_asyncsafe_close (struct closeable_fd *element)
+{
+ sigset_t saved_mask;
+ int ret;
+ int saved_errno;
+
+ asyncsafe_spin_lock (&element->lock, fatal_signal_set, &saved_mask);
+ if (!element->closed)
+ {
+ ret = close (element->fd);
+ saved_errno = errno;
+ element->closed = true;
+ }
+ else
+ {
+ ret = 0;
+ saved_errno = 0;
+ }
+ asyncsafe_spin_unlock (&element->lock, &saved_mask);
+ element->done = true;
+
+ errno = saved_errno;
+ return ret;
+}
+/* Initializations for use of this function. */
+void
+clean_temp_init_asyncsafe_close (void)
+{
+ init_fatal_signal_set ();
+}
+
+/* The signal handler. It gets called asynchronously. */
+static _GL_ASYNC_SAFE void
+cleanup_action (int sig _GL_UNUSED)
+{
+ size_t i;
+
+ /* First close all file descriptors to temporary files. */
+ {
+ gl_list_t fds = descriptors;
+
+ if (fds != NULL)
+ {
+ gl_list_iterator_t iter;
+ const void *element;
+
+ iter = gl_list_iterator (fds);
+ while (gl_list_iterator_next (&iter, &element, NULL))
+ {
+ clean_temp_asyncsafe_close ((struct closeable_fd *) element);
+ }
+ gl_list_iterator_free (&iter);
+ }
+ }
+
+ {
+ gl_list_t files = file_cleanup_list;
+
+ if (files != NULL)
+ {
+ gl_list_iterator_t iter;
+ const void *element;
+
+ iter = gl_list_iterator (files);
+ while (gl_list_iterator_next (&iter, &element, NULL))
+ {
+ const char *file = (const char *) element;
+ unlink (file);
+ }
+ gl_list_iterator_free (&iter);
+ }
+ }
+
+ for (i = 0; i < dir_cleanup_list.tempdir_count; i++)
+ {
+ struct tempdir *dir = dir_cleanup_list.tempdir_list[i];
+
+ if (dir != NULL)
+ {
+ gl_list_iterator_t iter;
+ const void *element;
+
+ /* First cleanup the files in the subdirectories. */
+ iter = gl_list_iterator (dir->files);
+ while (gl_list_iterator_next (&iter, &element, NULL))
+ {
+ const char *file = (const char *) element;
+ unlink (file);
+ }
+ gl_list_iterator_free (&iter);
+
+ /* Then cleanup the subdirectories. */
+ iter = gl_list_iterator (dir->subdirs);
+ while (gl_list_iterator_next (&iter, &element, NULL))
+ {
+ const char *subdir = (const char *) element;
+ rmdir (subdir);
+ }
+ gl_list_iterator_free (&iter);
+
+ /* Then cleanup the temporary directory itself. */
+ rmdir (dir->dirname);
+ }
+ }
+}
+
+
+/* Initializes this facility. */
+static void
+do_clean_temp_init (void)
+{
+ /* Initialize the data used by the cleanup handler. */
+ init_fatal_signal_set ();
+ /* Register the cleanup handler. */
+ if (at_fatal_signal (&cleanup_action) < 0)
+ xalloc_die ();
+}
+
+/* Ensure that do_clean_temp_init is called once only. */
+gl_once_define(static, clean_temp_once)
+
+/* Initializes this facility upon first use. */
+void
+clean_temp_init (void)
+{
+ gl_once (clean_temp_once, do_clean_temp_init);
+}
+
+
+/* Remove a file, with optional error message.
+ Return 0 upon success, or -1 if there was some problem. */
+int
+clean_temp_unlink (const char *absolute_file_name, bool cleanup_verbose)
+{
+ if (unlink (absolute_file_name) < 0 && cleanup_verbose
+ && errno != ENOENT)
+ {
+ error (0, errno,
+ _("cannot remove temporary file %s"), absolute_file_name);
+ return -1;
+ }
+ return 0;
+}
+
+
+/* ============= Temporary files without temporary directories ============= */
+
+/* Register the given ABSOLUTE_FILE_NAME as being a file that needs to be
+ removed.
+ Should be called before the file ABSOLUTE_FILE_NAME is created. */
+void
+register_temporary_file (const char *absolute_file_name)
+{
+ bool mt = gl_multithreaded ();
+
+ if (mt) gl_lock_lock (file_cleanup_list_lock);
+
+ /* Make sure that this facility and the file_cleanup_list are initialized. */
+ if (file_cleanup_list == NULL)
+ {
+ clean_temp_init ();
+ file_cleanup_list =
+ gl_list_create_empty (GL_LINKEDHASH_LIST,
+ clean_temp_string_equals, clean_temp_string_hash,
+ NULL, false);
+ }
+
+ /* Add absolute_file_name to file_cleanup_list, without duplicates. */
+ if (gl_list_search (file_cleanup_list, absolute_file_name) == NULL)
+ gl_list_add_first (file_cleanup_list, xstrdup (absolute_file_name));
+
+ if (mt) gl_lock_unlock (file_cleanup_list_lock);
+}
+
+/* Unregister the given ABSOLUTE_FILE_NAME as being a file that needs to be
+ removed.
+ Should be called when the file ABSOLUTE_FILE_NAME could not be created. */
+void
+unregister_temporary_file (const char *absolute_file_name)
+{
+ bool mt = gl_multithreaded ();
+
+ if (mt) gl_lock_lock (file_cleanup_list_lock);
+
+ gl_list_t list = file_cleanup_list;
+ if (list != NULL)
+ {
+ gl_list_node_t node = gl_list_search (list, absolute_file_name);
+ if (node != NULL)
+ {
+ char *old_string = (char *) gl_list_node_value (list, node);
+
+ gl_list_remove_node (list, node);
+ free (old_string);
+ }
+ }
+
+ if (mt) gl_lock_unlock (file_cleanup_list_lock);
+}
+
+/* Remove the given ABSOLUTE_FILE_NAME and unregister it.
+ CLEANUP_VERBOSE determines whether errors are reported to standard error.
+ Return 0 upon success, or -1 if there was some problem. */
+int
+cleanup_temporary_file (const char *absolute_file_name, bool cleanup_verbose)
+{
+ int err;
+
+ err = clean_temp_unlink (absolute_file_name, cleanup_verbose);
+ unregister_temporary_file (absolute_file_name);
+
+ return err;
+}
diff --git a/lib/clean-temp-simple.h b/lib/clean-temp-simple.h
new file mode 100644
index 0000000000..dce6812047
--- /dev/null
+++ b/lib/clean-temp-simple.h
@@ -0,0 +1,51 @@
+/* Temporary files with automatic cleanup.
+ Copyright (C) 2006-2021 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+ 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 3 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, see <https://www.gnu.org/licenses/>. */
+
+#ifndef _CLEAN_TEMP_SIMPLE_H
+#define _CLEAN_TEMP_SIMPLE_H
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* See clean-temp.h for a general discussion of this module. */
+
+/* Register the given ABSOLUTE_FILE_NAME as being a file that needs to be
+ removed.
+ Should be called before the file ABSOLUTE_FILE_NAME is created. */
+extern void register_temporary_file (const char *absolute_file_name);
+
+/* Unregister the given ABSOLUTE_FILE_NAME as being a file that needs to be
+ removed.
+ Should be called when the file ABSOLUTE_FILE_NAME could not be created. */
+extern void unregister_temporary_file (const char *absolute_file_name);
+
+/* Remove the given ABSOLUTE_FILE_NAME and unregister it.
+ CLEANUP_VERBOSE determines whether errors are reported to standard error.
+ Return 0 upon success, or -1 if there was some problem. */
+extern int cleanup_temporary_file (const char *absolute_file_name,
+ bool cleanup_verbose);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLEAN_TEMP_SIMPLE_H */
diff --git a/lib/clean-temp.c b/lib/clean-temp.c
index 268aa48208..4091d93273 100644
--- a/lib/clean-temp.c
+++ b/lib/clean-temp.c
@@ -16,7 +16,6 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
-
#include <config.h>
/* Specification. */
@@ -24,9 +23,9 @@
#include <errno.h>
#include <fcntl.h>
-#include <limits.h>
+#include <signal.h>
#include <stdbool.h>
-#include <stdint.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -36,6 +35,8 @@
# include <windows.h>
#endif
+#include "clean-temp-simple.h"
+#include "clean-temp-private.h"
#include "error.h"
#include "fatal-signal.h"
#include "asyncsafe-spin.h"
@@ -82,189 +83,14 @@
#endif
-/* The use of 'volatile' in the types below (and ISO C 99 section 5.1.2.3.(5))
- ensure that while constructing or modifying the data structures, the field
- values are written to memory in the order of the C statements. So the
- signal handler can rely on these field values to be up to date. */
-
-
-/* Lock that protects the file_cleanup_list from concurrent modification in
- different threads. */
-gl_lock_define_initialized (static, file_cleanup_list_lock)
-
-/* List of all temporary files without temporary directories. */
-static gl_list_t /* <char *> */ volatile file_cleanup_list;
-
-
-/* Registry for a single temporary directory.
- 'struct temp_dir' from the public header file overlaps with this. */
-struct tempdir
-{
- /* The absolute pathname of the directory. */
- char * volatile dirname;
- /* Whether errors during explicit cleanup are reported to standard error. */
- bool cleanup_verbose;
- /* Absolute pathnames of subdirectories. */
- gl_list_t /* <char *> */ volatile subdirs;
- /* Absolute pathnames of files. */
- gl_list_t /* <char *> */ volatile files;
-};
-
/* Lock that protects the dir_cleanup_list from concurrent modification in
different threads. */
gl_lock_define_initialized (static, dir_cleanup_list_lock)
-/* List of all temporary directories. */
-static struct
-{
- struct tempdir * volatile * volatile tempdir_list;
- size_t volatile tempdir_count;
- size_t tempdir_allocated;
-} dir_cleanup_list /* = { NULL, 0, 0 } */;
-
-
-/* A file descriptor to be closed.
- In multithreaded programs, it is forbidden to close the same fd twice,
- because you never know what unrelated open() calls are being executed in
- other threads. So, the 'close (fd)' must be guarded by a once-only guard. */
-struct closeable_fd
-{
- /* The file descriptor to close. */
- int volatile fd;
- /* Set to true when it has been closed. */
- bool volatile closed;
- /* Lock that protects the fd from being closed twice. */
- asyncsafe_spinlock_t lock;
- /* Tells whether this list element has been done and can be freed. */
- bool volatile done;
-};
-
/* Lock that protects the descriptors list from concurrent modification in
different threads. */
gl_lock_define_initialized (static, descriptors_lock)
-/* List of all open file descriptors to temporary files. */
-static gl_list_t /* <closeable_fd *> */ volatile descriptors;
-
-
-/* For the subdirs and for the files, we use a gl_list_t of type LINKEDHASH.
- Why? We need a data structure that
-
- 1) Can contain an arbitrary number of 'char *' values. The strings
- are compared via strcmp, not pointer comparison.
- 2) Has insertion and deletion operations that are fast: ideally O(1),
- or possibly O(log n). This is important for GNU sort, which may
- create a large number of temporary files.
- 3) Allows iteration through all elements from within a signal handler.
- 4) May or may not allow duplicates. It doesn't matter here, since
- any file or subdir can only be removed once.
-
- Criterion 1) would allow any gl_list_t or gl_oset_t implementation.
-
- Criterion 2) leaves only GL_LINKEDHASH_LIST, GL_TREEHASH_LIST, or
- GL_TREE_OSET.
-
- Criterion 3) puts at disadvantage GL_TREEHASH_LIST and GL_TREE_OSET.
- Namely, iteration through the elements of a binary tree requires access
- to many ->left, ->right, ->parent pointers. However, the rebalancing
- code for insertion and deletion in an AVL or red-black tree is so
- complicated that we cannot assume that >left, ->right, ->parent pointers
- are in a consistent state throughout these operations. Therefore, to
- avoid a crash in the signal handler, all destructive operations to the
- lists would have to be protected by a
- block_fatal_signals ();
- ...
- unblock_fatal_signals ();
- pair. Which causes extra system calls.
-
- Criterion 3) would also discourage GL_ARRAY_LIST and GL_CARRAY_LIST,
- if they were not already excluded. Namely, these implementations use
- xrealloc(), leaving a time window in which in the list->elements pointer
- points to already deallocated memory. To avoid a crash in the signal
- handler at such a moment, all destructive operations would have to
- protected by block/unblock_fatal_signals (), in this case too.
-
- A list of type GL_LINKEDHASH_LIST without duplicates fulfills all
- requirements:
- 2) Insertion and deletion are O(1) on average.
- 3) The gl_list_iterator, gl_list_iterator_next implementations do
- not trigger memory allocations, nor other system calls, and are
- therefore safe to be called from a signal handler.
- Furthermore, since SIGNAL_SAFE_LIST is defined, the implementation
- of the destructive functions ensures that the list structure is
- safe to be traversed at any moment, even when interrupted by an
- asynchronous signal.
- */
-
-/* String equality and hash code functions used by the lists. */
-
-static bool
-string_equals (const void *x1, const void *x2)
-{
- const char *s1 = (const char *) x1;
- const char *s2 = (const char *) x2;
- return strcmp (s1, s2) == 0;
-}
-
-#define SIZE_BITS (sizeof (size_t) * CHAR_BIT)
-
-/* A hash function for NUL-terminated char* strings using
- the method described by Bruno Haible.
- See https://www.haible.de/bruno/hashfunc.html. */
-static size_t
-string_hash (const void *x)
-{
- const char *s = (const char *) x;
- size_t h = 0;
-
- for (; *s; s++)
- h = *s + ((h << 9) | (h >> (SIZE_BITS - 9)));
-
- return h;
-}
-
-
-/* The set of fatal signal handlers.
- Cached here because we are not allowed to call get_fatal_signal_set ()
- from a signal handler. */
-static const sigset_t *fatal_signal_set /* = NULL */;
-
-static void
-init_fatal_signal_set (void)
-{
- if (fatal_signal_set == NULL)
- fatal_signal_set = get_fatal_signal_set ();
-}
-
-
-/* Close a file descriptor.
- Avoids race conditions with normal thread code or signal-handler code that
- might want to close the same file descriptor. */
-static _GL_ASYNC_SAFE int
-asyncsafe_close (struct closeable_fd *element)
-{
- sigset_t saved_mask;
- int ret;
- int saved_errno;
-
- asyncsafe_spin_lock (&element->lock, fatal_signal_set, &saved_mask);
- if (!element->closed)
- {
- ret = close (element->fd);
- saved_errno = errno;
- element->closed = true;
- }
- else
- {
- ret = 0;
- saved_errno = 0;
- }
- asyncsafe_spin_unlock (&element->lock, &saved_mask);
- element->done = true;
-
- errno = saved_errno;
- return ret;
-}
/* Close a file descriptor and the stream that contains it.
Avoids race conditions with signal-handler code that might want to close the
@@ -283,7 +109,7 @@ asyncsafe_fclose_variant (struct closeable_fd *element, FILE *fp,
int ret;
int saved_errno;
- asyncsafe_spin_lock (&element->lock, fatal_signal_set, &saved_mask);
+ asyncsafe_spin_lock (&element->lock, get_fatal_signal_set (), &saved_mask);
if (!element->closed)
{
ret = fclose_variant (fp); /* invokes close (element->fd) */
@@ -302,187 +128,6 @@ asyncsafe_fclose_variant (struct closeable_fd *element, FILE *fp,
return ret;
}
-/* The signal handler. It gets called asynchronously. */
-static _GL_ASYNC_SAFE void
-cleanup_action (int sig _GL_UNUSED)
-{
- size_t i;
-
- /* First close all file descriptors to temporary files. */
- {
- gl_list_t fds = descriptors;
-
- if (fds != NULL)
- {
- gl_list_iterator_t iter;
- const void *element;
-
- iter = gl_list_iterator (fds);
- while (gl_list_iterator_next (&iter, &element, NULL))
- {
- asyncsafe_close ((struct closeable_fd *) element);
- }
- gl_list_iterator_free (&iter);
- }
- }
-
- {
- gl_list_t files = file_cleanup_list;
-
- if (files != NULL)
- {
- gl_list_iterator_t iter;
- const void *element;
-
- iter = gl_list_iterator (files);
- while (gl_list_iterator_next (&iter, &element, NULL))
- {
- const char *file = (const char *) element;
- unlink (file);
- }
- gl_list_iterator_free (&iter);
- }
- }
-
- for (i = 0; i < dir_cleanup_list.tempdir_count; i++)
- {
- struct tempdir *dir = dir_cleanup_list.tempdir_list[i];
-
- if (dir != NULL)
- {
- gl_list_iterator_t iter;
- const void *element;
-
- /* First cleanup the files in the subdirectories. */
- iter = gl_list_iterator (dir->files);
- while (gl_list_iterator_next (&iter, &element, NULL))
- {
- const char *file = (const char *) element;
- unlink (file);
- }
- gl_list_iterator_free (&iter);
-
- /* Then cleanup the subdirectories. */
- iter = gl_list_iterator (dir->subdirs);
- while (gl_list_iterator_next (&iter, &element, NULL))
- {
- const char *subdir = (const char *) element;
- rmdir (subdir);
- }
- gl_list_iterator_free (&iter);
-
- /* Then cleanup the temporary directory itself. */
- rmdir (dir->dirname);
- }
- }
-}
-
-
-/* Initializes this facility. */
-static void
-do_init_clean_temp (void)
-{
- /* Initialize the data used by the cleanup handler. */
- init_fatal_signal_set ();
- /* Register the cleanup handler. */
- if (at_fatal_signal (&cleanup_action) < 0)
- xalloc_die ();
-}
-
-/* Ensure that do_init_clean_temp is called once only. */
-gl_once_define(static, clean_temp_once)
-
-/* Initializes this facility upon first use. */
-static void
-init_clean_temp (void)
-{
- gl_once (clean_temp_once, do_init_clean_temp);
-}
-
-
-/* ============= Temporary files without temporary directories ============= */
-
-/* Register the given ABSOLUTE_FILE_NAME as being a file that needs to be
- removed.
- Should be called before the file ABSOLUTE_FILE_NAME is created. */
-void
-register_temporary_file (const char *absolute_file_name)
-{
- bool mt = gl_multithreaded ();
-
- if (mt) gl_lock_lock (file_cleanup_list_lock);
-
- /* Make sure that this facility and the file_cleanup_list are initialized. */
- if (file_cleanup_list == NULL)
- {
- init_clean_temp ();
- file_cleanup_list =
- gl_list_create_empty (GL_LINKEDHASH_LIST,
- string_equals, string_hash, NULL, false);
- }
-
- /* Add absolute_file_name to file_cleanup_list, without duplicates. */
- if (gl_list_search (file_cleanup_list, absolute_file_name) == NULL)
- gl_list_add_first (file_cleanup_list, xstrdup (absolute_file_name));
-
- if (mt) gl_lock_unlock (file_cleanup_list_lock);
-}
-
-/* Unregister the given ABSOLUTE_FILE_NAME as being a file that needs to be
- removed.
- Should be called when the file ABSOLUTE_FILE_NAME could not be created. */
-void
-unregister_temporary_file (const char *absolute_file_name)
-{
- bool mt = gl_multithreaded ();
-
- if (mt) gl_lock_lock (file_cleanup_list_lock);
-
- gl_list_t list = file_cleanup_list;
- if (list != NULL)
- {
- gl_list_node_t node = gl_list_search (list, absolute_file_name);
- if (node != NULL)
- {
- char *old_string = (char *) gl_list_node_value (list, node);
-
- gl_list_remove_node (list, node);
- free (old_string);
- }
- }
-
- if (mt) gl_lock_unlock (file_cleanup_list_lock);
-}
-
-/* Remove a file, with optional error message.
- Return 0 upon success, or -1 if there was some problem. */
-static int
-do_unlink (const char *absolute_file_name, bool cleanup_verbose)
-{
- if (unlink (absolute_file_name) < 0 && cleanup_verbose
- && errno != ENOENT)
- {
- error (0, errno,
- _("cannot remove temporary file %s"), absolute_file_name);
- return -1;
- }
- return 0;
-}
-
-/* Remove the given ABSOLUTE_FILE_NAME and unregister it.
- CLEANUP_VERBOSE determines whether errors are reported to standard error.
- Return 0 upon success, or -1 if there was some problem. */
-int
-cleanup_temporary_file (const char *absolute_file_name, bool cleanup_verbose)
-{
- int err;
-
- err = do_unlink (absolute_file_name, cleanup_verbose);
- unregister_temporary_file (absolute_file_name);
-
- return err;
-}
-
/* ========= Temporary directories and temporary files inside them ========= */
@@ -533,7 +178,7 @@ create_temp_dir (const char *prefix, const char *parentdir,
if (old_allocated == 0)
{
/* First use of this facility. */
- init_clean_temp ();
+ clean_temp_init ();
}
else
{
@@ -572,12 +217,14 @@ create_temp_dir (const char *prefix, const char *parentdir,
tmpdir = XMALLOC (struct tempdir);
tmpdir->dirname = NULL;
tmpdir->cleanup_verbose = cleanup_verbose;
- tmpdir->subdirs = gl_list_create_empty (GL_LINKEDHASH_LIST,
- string_equals, string_hash, NULL,
- false);
- tmpdir->files = gl_list_create_empty (GL_LINKEDHASH_LIST,
- string_equals, string_hash, NULL,
- false);
+ tmpdir->subdirs =
+ gl_list_create_empty (GL_LINKEDHASH_LIST,
+ clean_temp_string_equals, clean_temp_string_hash,
+ NULL, false);
+ tmpdir->files =
+ gl_list_create_empty (GL_LINKEDHASH_LIST,
+ clean_temp_string_equals, clean_temp_string_hash,
+ NULL, false);
/* Create the temporary directory. */
xtemplate = (char *) xmalloca (PATH_MAX);
@@ -734,7 +381,7 @@ cleanup_temp_file (struct temp_dir *dir,
{
int err;
- err = do_unlink (absolute_file_name, dir->cleanup_verbose);
+ err = clean_temp_unlink (absolute_file_name, dir->cleanup_verbose);
unregister_temp_file (dir, absolute_file_name);
return err;
@@ -774,7 +421,7 @@ cleanup_temp_dir_contents (struct temp_dir *dir)
{
char *file = (char *) element;
- err |= do_unlink (file, dir->cleanup_verbose);
+ err |= clean_temp_unlink (file, dir->cleanup_verbose);
gl_list_remove_node (list, node);
/* Now only we can free file. */
free (file);
@@ -1017,7 +664,7 @@ gen_register_open_temp (char *file_name_tmpl, int suffixlen,
int saved_errno = errno;
if (fd >= 0)
{
- init_clean_temp ();
+ clean_temp_init ();
register_fd (fd);
register_temporary_file (file_name_tmpl);
}
@@ -1037,7 +684,7 @@ close_temp (int fd)
if (fd < 0)
return close (fd);
- init_fatal_signal_set ();
+ clean_temp_init_asyncsafe_close ();
int result = 0;
int saved_errno = 0;
@@ -1066,7 +713,7 @@ close_temp (int fd)
if (element->fd == fd)
{
found = true;
- result = asyncsafe_close (element);
+ result = clean_temp_asyncsafe_close (element);
saved_errno = errno;
}
@@ -1101,8 +748,6 @@ fclose_variant_temp (FILE *fp, int (*fclose_variant) (FILE *))
{
int fd = fileno (fp);
- init_fatal_signal_set ();
-
int result = 0;
int saved_errno = 0;
diff --git a/lib/clean-temp.h b/lib/clean-temp.h
index 72e3e26b50..db4d7fea26 100644
--- a/lib/clean-temp.h
+++ b/lib/clean-temp.h
@@ -37,10 +37,11 @@ extern "C" {
and the temporary directories can be removed, because only on Unix
(excluding Cygwin) can one remove directories containing open files.
- This module provides support for
- - temporary directories and temporary files inside these temporary
- directories,
- - temporary files without temporary directories.
+ There are two modules:
+ - 'clean-temp' provides support for temporary directories and temporary
+ files inside these temporary directories,
+ - 'clean-temp-simple' provides support for temporary files without
+ temporary directories.
The temporary directories and files are automatically cleaned up (at the
latest) when the program exits or dies from a fatal signal such as SIGINT,
SIGTERM, SIGHUP, but not if it dies from a fatal signal such as SIGQUIT,
@@ -64,21 +65,7 @@ extern "C" {
/* ============= Temporary files without temporary directories ============= */
-/* Register the given ABSOLUTE_FILE_NAME as being a file that needs to be
- removed.
- Should be called before the file ABSOLUTE_FILE_NAME is created. */
-extern void register_temporary_file (const char *absolute_file_name);
-
-/* Unregister the given ABSOLUTE_FILE_NAME as being a file that needs to be
- removed.
- Should be called when the file ABSOLUTE_FILE_NAME could not be created. */
-extern void unregister_temporary_file (const char *absolute_file_name);
-
-/* Remove the given ABSOLUTE_FILE_NAME and unregister it.
- CLEANUP_VERBOSE determines whether errors are reported to standard error.
- Return 0 upon success, or -1 if there was some problem. */
-extern int cleanup_temporary_file (const char *absolute_file_name,
- bool cleanup_verbose);
+#include "clean-temp-simple.h"
/* ========= Temporary directories and temporary files inside them ========= */
diff --git a/modules/clean-temp b/modules/clean-temp
index 2eca0060a7..a83f249903 100644
--- a/modules/clean-temp
+++ b/modules/clean-temp
@@ -8,13 +8,14 @@ lib/clean-temp.c
Depends-on:
c99
stdbool
-stdint
+clean-temp-simple
+list
+asyncsafe-spin
unistd
lock
thread-optim
error
fatal-signal
-asyncsafe-spin
open
pathmax
tmpdir
@@ -29,7 +30,6 @@ xlist
gettext-h
configure.ac:
-AC_DEFINE([SIGNAL_SAFE_LIST], [1], [Define if lists must be signal-safe.])
Makefile.am:
lib_SOURCES += clean-temp.h clean-temp.c
diff --git a/modules/clean-temp-simple b/modules/clean-temp-simple
new file mode 100644
index 0000000000..4187ee62ab
--- /dev/null
+++ b/modules/clean-temp-simple
@@ -0,0 +1,42 @@
+Description:
+Temporary files with automatic cleanup.
+
+Files:
+lib/clean-temp-simple.h
+lib/clean-temp-private.h
+lib/clean-temp-simple.c
+
+Depends-on:
+c99
+stdbool
+list
+asyncsafe-spin
+unistd
+lock
+thread-optim
+error
+fatal-signal
+rmdir
+xalloc
+xalloc-die
+linkedhash-list
+xlist
+gettext-h
+
+configure.ac:
+AC_DEFINE([SIGNAL_SAFE_LIST], [1], [Define if lists must be signal-safe.])
+
+Makefile.am:
+lib_SOURCES += clean-temp-simple.h clean-temp-simple.c
+
+Include:
+"clean-temp-simple.h"
+
+Link:
+$(LIBTHREAD)
+
+License:
+GPL
+
+Maintainer:
+all