1 /* Temporary files with automatic cleanup.
2 Copyright (C) 2006-2024 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2006.
5 This file is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as
7 published by the Free Software Foundation; either version 2.1 of the
8 License, or (at your option) any later version.
10 This file is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
21 #include "clean-temp-simple.h"
22 #include "clean-temp-private.h"
32 #include "fatal-signal.h"
33 #include "asyncsafe-spin.h"
34 #include "glthread/lock.h"
35 #include "thread-optim.h"
37 #include "gl_linkedhash_list.h"
40 #define _(str) gettext (str)
43 /* Lock that protects the file_cleanup_list from concurrent modification in
45 gl_lock_define_initialized (static, file_cleanup_list_lock
)
47 /* List of all temporary files without temporary directories. */
48 static gl_list_t
/* <char *> */ volatile file_cleanup_list
;
51 /* List of all temporary directories. */
52 struct all_tempdirs dir_cleanup_list
/* = { NULL, 0, 0 } */;
55 /* List of all open file descriptors to temporary files. */
56 gl_list_t
/* <closeable_fd *> */ volatile descriptors
;
59 /* For the subdirs and for the files, we use a gl_list_t of type LINKEDHASH.
60 Why? We need a data structure that
62 1) Can contain an arbitrary number of 'char *' values. The strings
63 are compared via strcmp, not pointer comparison.
64 2) Has insertion and deletion operations that are fast: ideally O(1),
65 or possibly O(log n). This is important for GNU sort, which may
66 create a large number of temporary files.
67 3) Allows iteration through all elements from within a signal handler.
68 4) May or may not allow duplicates. It doesn't matter here, since
69 any file or subdir can only be removed once.
71 Criterion 1) would allow any gl_list_t or gl_oset_t implementation.
73 Criterion 2) leaves only GL_LINKEDHASH_LIST, GL_TREEHASH_LIST, or
76 Criterion 3) puts at disadvantage GL_TREEHASH_LIST and GL_TREE_OSET.
77 Namely, iteration through the elements of a binary tree requires access
78 to many ->left, ->right, ->parent pointers. However, the rebalancing
79 code for insertion and deletion in an AVL or red-black tree is so
80 complicated that we cannot assume that >left, ->right, ->parent pointers
81 are in a consistent state throughout these operations. Therefore, to
82 avoid a crash in the signal handler, all destructive operations to the
83 lists would have to be protected by a
84 block_fatal_signals ();
86 unblock_fatal_signals ();
87 pair. Which causes extra system calls.
89 Criterion 3) would also discourage GL_ARRAY_LIST and GL_CARRAY_LIST,
90 if they were not already excluded. Namely, these implementations use
91 xrealloc(), leaving a time window in which in the list->elements pointer
92 points to already deallocated memory. To avoid a crash in the signal
93 handler at such a moment, all destructive operations would have to
94 protected by block/unblock_fatal_signals (), in this case too.
96 A list of type GL_LINKEDHASH_LIST without duplicates fulfills all
98 2) Insertion and deletion are O(1) on average.
99 3) The gl_list_iterator, gl_list_iterator_next implementations do
100 not trigger memory allocations, nor other system calls, and are
101 therefore safe to be called from a signal handler.
102 Furthermore, since SIGNAL_SAFE_LIST is defined, the implementation
103 of the destructive functions ensures that the list structure is
104 safe to be traversed at any moment, even when interrupted by an
108 /* String equality and hash code functions used by the lists. */
111 clean_temp_string_equals (const void *x1
, const void *x2
)
113 const char *s1
= (const char *) x1
;
114 const char *s2
= (const char *) x2
;
115 return strcmp (s1
, s2
) == 0;
118 #define SIZE_BITS (sizeof (size_t) * CHAR_BIT)
120 /* A hash function for NUL-terminated char* strings using
121 the method described by Bruno Haible.
122 See https://www.haible.de/bruno/hashfunc.html. */
124 clean_temp_string_hash (const void *x
)
126 const char *s
= (const char *) x
;
130 h
= *s
+ ((h
<< 9) | (h
>> (SIZE_BITS
- 9)));
136 /* The set of fatal signal handlers.
137 Cached here because we are not allowed to call get_fatal_signal_set ()
138 from a signal handler. */
139 static const sigset_t
*fatal_signal_set
/* = NULL */;
142 init_fatal_signal_set (void)
144 if (fatal_signal_set
== NULL
)
145 fatal_signal_set
= get_fatal_signal_set ();
149 /* Close a file descriptor.
150 Avoids race conditions with normal thread code or signal-handler code that
151 might want to close the same file descriptor. */
153 clean_temp_asyncsafe_close (struct closeable_fd
*element
)
159 asyncsafe_spin_lock (&element
->lock
, fatal_signal_set
, &saved_mask
);
160 if (!element
->closed
)
162 ret
= close (element
->fd
);
164 element
->closed
= true;
171 asyncsafe_spin_unlock (&element
->lock
, &saved_mask
);
172 element
->done
= true;
177 /* Initializations for use of this function. */
179 clean_temp_init_asyncsafe_close (void)
181 init_fatal_signal_set ();
184 /* The signal handler. It gets called asynchronously. */
185 static _GL_ASYNC_SAFE
void
186 cleanup_action (_GL_UNUSED
int sig
)
190 /* First close all file descriptors to temporary files. */
192 gl_list_t fds
= descriptors
;
196 gl_list_iterator_t iter
;
199 iter
= gl_list_iterator (fds
);
200 while (gl_list_iterator_next (&iter
, &element
, NULL
))
202 clean_temp_asyncsafe_close ((struct closeable_fd
*) element
);
204 gl_list_iterator_free (&iter
);
209 gl_list_t files
= file_cleanup_list
;
213 gl_list_iterator_t iter
;
216 iter
= gl_list_iterator (files
);
217 while (gl_list_iterator_next (&iter
, &element
, NULL
))
219 const char *file
= (const char *) element
;
222 gl_list_iterator_free (&iter
);
226 for (i
= 0; i
< dir_cleanup_list
.tempdir_count
; i
++)
228 struct tempdir
*dir
= dir_cleanup_list
.tempdir_list
[i
];
232 gl_list_iterator_t iter
;
235 /* First cleanup the files in the subdirectories. */
236 iter
= gl_list_iterator (dir
->files
);
237 while (gl_list_iterator_next (&iter
, &element
, NULL
))
239 const char *file
= (const char *) element
;
242 gl_list_iterator_free (&iter
);
244 /* Then cleanup the subdirectories. */
245 iter
= gl_list_iterator (dir
->subdirs
);
246 while (gl_list_iterator_next (&iter
, &element
, NULL
))
248 const char *subdir
= (const char *) element
;
251 gl_list_iterator_free (&iter
);
253 /* Then cleanup the temporary directory itself. */
254 rmdir (dir
->dirname
);
260 /* Set to -1 if initialization of this facility failed. */
261 static int volatile init_failed
/* = 0 */;
263 /* Initializes this facility. */
265 do_clean_temp_init (void)
267 /* Initialize the data used by the cleanup handler. */
268 init_fatal_signal_set ();
269 /* Register the cleanup handler. */
270 if (at_fatal_signal (&cleanup_action
) < 0)
274 /* Ensure that do_clean_temp_init is called once only. */
275 gl_once_define(static, clean_temp_once
)
277 /* Initializes this facility upon first use.
278 Return 0 upon success, or -1 if there was a memory allocation problem. */
280 clean_temp_init (void)
282 gl_once (clean_temp_once
, do_clean_temp_init
);
287 /* Remove a file, with optional error message.
288 Return 0 upon success, or -1 if there was some problem. */
290 clean_temp_unlink (const char *absolute_file_name
, bool cleanup_verbose
)
292 if (unlink (absolute_file_name
) < 0 && cleanup_verbose
296 _("cannot remove temporary file %s"), absolute_file_name
);
303 /* ============= Temporary files without temporary directories ============= */
305 /* Register the given ABSOLUTE_FILE_NAME as being a file that needs to be
307 Should be called before the file ABSOLUTE_FILE_NAME is created.
308 Return 0 upon success, or -1 if there was a memory allocation problem. */
310 register_temporary_file (const char *absolute_file_name
)
312 bool mt
= gl_multithreaded ();
314 if (mt
) gl_lock_lock (file_cleanup_list_lock
);
318 /* Make sure that this facility and the file_cleanup_list are initialized. */
319 if (file_cleanup_list
== NULL
)
321 if (clean_temp_init () < 0)
327 gl_list_nx_create_empty (GL_LINKEDHASH_LIST
,
328 clean_temp_string_equals
,
329 clean_temp_string_hash
,
331 if (file_cleanup_list
== NULL
)
338 /* Add absolute_file_name to file_cleanup_list, without duplicates. */
339 if (gl_list_search (file_cleanup_list
, absolute_file_name
) == NULL
)
341 char *absolute_file_name_copy
= strdup (absolute_file_name
);
342 if (absolute_file_name_copy
== NULL
)
347 if (gl_list_nx_add_first (file_cleanup_list
, absolute_file_name_copy
)
350 free (absolute_file_name_copy
);
357 if (mt
) gl_lock_unlock (file_cleanup_list_lock
);
362 /* Unregister the given ABSOLUTE_FILE_NAME as being a file that needs to be
364 Should be called when the file ABSOLUTE_FILE_NAME could not be created. */
366 unregister_temporary_file (const char *absolute_file_name
)
368 bool mt
= gl_multithreaded ();
370 if (mt
) gl_lock_lock (file_cleanup_list_lock
);
372 gl_list_t list
= file_cleanup_list
;
375 gl_list_node_t node
= gl_list_search (list
, absolute_file_name
);
378 char *old_string
= (char *) gl_list_node_value (list
, node
);
380 gl_list_remove_node (list
, node
);
385 if (mt
) gl_lock_unlock (file_cleanup_list_lock
);
388 /* Remove the given ABSOLUTE_FILE_NAME and unregister it.
389 CLEANUP_VERBOSE determines whether errors are reported to standard error.
390 Return 0 upon success, or -1 if there was some problem. */
392 cleanup_temporary_file (const char *absolute_file_name
, bool cleanup_verbose
)
396 err
= clean_temp_unlink (absolute_file_name
, cleanup_verbose
);
397 unregister_temporary_file (absolute_file_name
);