]> diplodocus.org Git - nmh/blob - sbr/m_mktemp.c
uip/flist.c: Make locally defined and used functions static.
[nmh] / sbr / m_mktemp.c
1 /* m_mktemp.c -- Construct a temporary file.
2 *
3 * This code is Copyright (c) 2010, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include <h/mh.h>
9 #include <h/utils.h>
10 #include <h/signals.h>
11
12 static void register_for_removal(const char *);
13
14
15 /* Create a temporary file. If pfx_in is null, the temporary file
16 * will be created in the temporary directory (more on that later).
17 * If pfx_in is not null, then the temporary file location will be
18 * defined by the value pfx_in.
19 *
20 * The file created will be at the pathname specified appended with
21 * 6 random (we hope :) characters.
22 *
23 * The return value will be the pathname to the file created.
24 *
25 * CAUTION: The return pointer references static data. If
26 * you need to modify, or save, the return string, make a copy of it
27 * first.
28 *
29 * When pfx_in is null, the temporary directory is determined as
30 * follows, in order:
31 *
32 * MHTMPDIR envvar
33 * TMPDIR envvar
34 * User's mail directory.
35 *
36 * NOTE: One will probably use m_mktemp2() instead of this function.
37 * For example, if you want to create a temp file in the defined
38 * temporary directory, but with a custom basename prefix, do
39 * something like the following:
40 *
41 * char *tmp_pathname = m_mktemp2(NULL, "mypre", ...);
42 */
43 char *
44 m_mktemp (
45 const char *pfx_in, /* Pathname prefix for temporary file. */
46 int *fd_ret, /* (return,optional) File descriptor to temp file. */
47 FILE **fp_ret /* (return,optional) FILE pointer to temp file. */
48 )
49 {
50 static char tmpfil[BUFSIZ];
51 int fd = -1;
52 int keep_open = 0;
53 mode_t oldmode = umask(077);
54
55 if (pfx_in == NULL) {
56 snprintf(tmpfil, sizeof(tmpfil), "%s/nmhXXXXXX", get_temp_dir());
57 } else {
58 snprintf(tmpfil, sizeof(tmpfil), "%sXXXXXX", pfx_in);
59 }
60
61 fd = mkstemp(tmpfil);
62
63 if (fd < 0) {
64 umask(oldmode);
65 return NULL;
66 }
67
68 register_for_removal(tmpfil);
69
70 if (fd_ret != NULL) {
71 *fd_ret = fd;
72 keep_open = 1;
73 }
74 if (fp_ret != NULL) {
75 FILE *fp = fdopen(fd, "w+");
76 if (fp == NULL) {
77 int olderr = errno;
78 (void) m_unlink(tmpfil);
79 close(fd);
80 errno = olderr;
81 umask(oldmode);
82 return NULL;
83 }
84 *fp_ret = fp;
85 keep_open = 1;
86 }
87 if (!keep_open) {
88 close(fd);
89 }
90 umask(oldmode);
91 return tmpfil;
92 }
93
94 /* This version allows one to specify the directory the temp file should
95 * by created based on a given pathname. Although m_mktemp() technically
96 * supports this, this version is when the directory is defined by
97 * a separate variable from the prefix, eliminating the caller from having
98 * to do string manipulation to generate the desired pathname prefix.
99 *
100 * The pfx_in parameter specifies a basename prefix for the file. If dir_in
101 * is NULL, then the defined temporary directory (see comments to m_mktemp()
102 * above) is used to create the temp file.
103 */
104 char *
105 m_mktemp2 (
106 const char *dir_in, /* Directory to place temp file. */
107 const char *pfx_in, /* Basename prefix for temp file. */
108 int *fd_ret, /* (return,optional) File descriptor to temp file. */
109 FILE **fp_ret /* (return,optional) FILE pointer to temp file. */
110 )
111 {
112 static char buffer[BUFSIZ];
113 char *cp;
114 int n;
115
116 if (dir_in == NULL) {
117 if (pfx_in == NULL) {
118 return m_mktemp(NULL, fd_ret, fp_ret);
119 }
120 snprintf(buffer, sizeof(buffer), "%s/%s", get_temp_dir(), pfx_in);
121 return m_mktemp(buffer, fd_ret, fp_ret);
122 }
123
124 if ((cp = r1bindex ((char *)dir_in, '/')) == dir_in) {
125 /* No directory component */
126 return m_mktemp(pfx_in, fd_ret, fp_ret);
127 }
128 n = (int)(cp-dir_in); /* Length of dir component */
129 snprintf(buffer, sizeof(buffer), "%.*s%s", n, dir_in, pfx_in);
130 return m_mktemp(buffer, fd_ret, fp_ret);
131 }
132
133
134 /*
135 * This version supports a suffix for the temp filename.
136 * It has two other differences from m_mktemp() and m_mktemp2():
137 * 1) It does not support specification of a directory for the temp
138 * file. Instead it always uses get_temp_dir().
139 * 2) It returns a dynamically allocated string. The caller is
140 * responsible for freeing the allocated memory.
141 */
142 char *
143 m_mktemps(
144 const char *pfx_in, /* Basename prefix for temp file. */
145 const char *suffix, /* Suffix, including any '.', for temp file. */
146 int *fd_ret, /* (return,optional) File descriptor to temp file. */
147 FILE **fp_ret /* (return,optional) FILE pointer to temp file. */
148 )
149 {
150 char *tmpfil;
151 int fd = -1;
152 int keep_open = 0;
153 mode_t oldmode = umask(077);
154
155 if (suffix == NULL) {
156 if ((tmpfil = m_mktemp2(NULL, pfx_in, fd_ret, fp_ret))) {
157 return mh_xstrdup(tmpfil);
158 }
159 return NULL;
160 }
161
162 #if HAVE_MKSTEMPS
163 if (pfx_in == NULL) {
164 tmpfil = concat(get_temp_dir(), "/nmhXXXXXX", suffix, NULL);
165 } else {
166 tmpfil = concat(get_temp_dir(), "/", pfx_in, "XXXXXX", suffix, NULL);
167 }
168
169 fd = mkstemps(tmpfil, (int) strlen(suffix));
170 #else /* ! HAVE_MKSTEMPS */
171 /* NetBSD, Solaris 10 */
172
173 if (pfx_in == NULL) {
174 tmpfil = concat(get_temp_dir(), "/nmhXXXXXX", NULL);
175 } else {
176 tmpfil = concat(get_temp_dir(), "/", pfx_in, "XXXXXX", NULL);
177 }
178
179 fd = mkstemp(tmpfil);
180 {
181 char *oldfilename = tmpfil;
182 tmpfil = concat(oldfilename, suffix, NULL);
183
184 if (rename(oldfilename, tmpfil) != 0) {
185 (void) unlink(oldfilename);
186 free(oldfilename);
187 free(tmpfil);
188 return NULL;
189 }
190
191 free(oldfilename);
192 }
193 #endif /* ! HAVE_MKSTEMPS */
194
195 if (fd < 0) {
196 umask(oldmode);
197 free(tmpfil);
198 return NULL;
199 }
200
201 register_for_removal(tmpfil);
202
203 if (fd_ret != NULL) {
204 *fd_ret = fd;
205 keep_open = 1;
206 }
207 if (fp_ret != NULL) {
208 FILE *fp = fdopen(fd, "w+");
209 if (fp == NULL) {
210 int olderr = errno;
211 (void) m_unlink(tmpfil);
212 close(fd);
213 errno = olderr;
214 umask(oldmode);
215 free(tmpfil);
216 return NULL;
217 }
218 *fp_ret = fp;
219 keep_open = 1;
220 }
221 if (!keep_open) {
222 close(fd);
223 }
224 umask(oldmode);
225
226 return tmpfil;
227 }
228
229
230 char *
231 get_temp_dir()
232 {
233 /* Ignore envvars if we are setuid */
234 if ((getuid()==geteuid()) && (getgid()==getegid())) {
235 char *tmpdir = NULL;
236 tmpdir = getenv("MHTMPDIR");
237 if (tmpdir != NULL && *tmpdir != '\0') return tmpdir;
238
239 tmpdir = getenv("TMPDIR");
240 if (tmpdir != NULL && *tmpdir != '\0') return tmpdir;
241 }
242 return m_maildir("");
243 }
244
245
246 /*
247 * Array of files (full pathnames) to remove on process exit.
248 * Instead of removing slots from the array, just set to NULL
249 * to indicate that the file should no longer be removed.
250 */
251 static svector_t exit_filelist = NULL;
252
253 /*
254 * Register a file for removal at program termination.
255 */
256 static void
257 register_for_removal(const char *pathname) {
258 if (exit_filelist == NULL) exit_filelist = svector_create(20);
259 (void) svector_push_back(exit_filelist, add(pathname, NULL));
260 }
261
262 /*
263 * Unregister all files that were registered to be removed at program
264 * termination. When called with remove_files of 0, this function is
265 * intended for use by one of the programs after a fork(3) without a
266 * subsequent call to one of the exec(3) functions or _exit(3). When
267 * called with non-0 remove_files argument, it is intended for use by
268 * an atexit() function.
269 *
270 * Right after a fork() and before calling exec() or _exit(), if the
271 * child catches one of the appropriate signals, it will remove
272 * all the registered temporary files. Some of those may be needed by
273 * the parent. Some nmh programs ignore or block some of the signals
274 * in the child right after fork(). For the other programs, rather
275 * than complicate things by preventing that, just take the chance
276 * that it might happen. It is harmless to attempt to unlink a
277 * temporary file twice, assuming another one wasn't created too
278 * quickly created with the same name.
279 */
280 void
281 unregister_for_removal(int remove_files) {
282 if (exit_filelist) {
283 size_t i;
284
285 for (i = 0; i < svector_size(exit_filelist); ++i) {
286 char *filename = svector_at(exit_filelist, i);
287
288 if (filename) {
289 if (remove_files) (void) unlink(filename);
290 free(filename);
291 }
292 }
293
294 svector_free(exit_filelist);
295 exit_filelist = NULL;
296 }
297 }
298
299 /*
300 * If the file was registered for removal, deregister it. In
301 * any case, unlink it.
302 */
303 int
304 m_unlink(const char *pathname) {
305 if (exit_filelist) {
306 char **slot = svector_find(exit_filelist, pathname);
307
308 if (slot && *slot) {
309 free(*slot);
310 *slot = NULL;
311 }
312 }
313
314 return unlink(pathname);
315 /* errno should be set to ENOENT if file was not found */
316 }
317
318 /*
319 * Remove all registered temporary files.
320 */
321 void
322 remove_registered_files_atexit() {
323 unregister_for_removal(1);
324 }
325
326 /*
327 * Remove all registered temporary files. Suitable for use as a
328 * signal handler. If the signal is one of the usual process
329 * termination signals, calls exit(). Otherwise, disables itself
330 * by restoring the default action and then re-raises the signal,
331 * in case the use was expecting a core dump.
332 */
333 void
334 remove_registered_files(int sig) {
335 struct sigaction act;
336
337 /*
338 * Ignore this signal for the duration. And we either exit() or
339 * disable this signal handler below, so don't restore this handler.
340 */
341 act.sa_handler = SIG_IGN;
342 (void) sigemptyset(&act.sa_mask);
343 act.sa_flags = 0;
344 (void) sigaction(sig, &act, 0);
345
346 if (sig == SIGHUP || sig == SIGINT || sig == SIGQUIT || sig == SIGTERM) {
347 /*
348 * We don't need to call remove_registered_files_atexit() if
349 * it was registered with atexit(), but let's not assume that.
350 * It's harmless to call it twice.
351 */
352 remove_registered_files_atexit();
353
354 exit(1);
355 } else {
356 remove_registered_files_atexit();
357
358 act.sa_handler = SIG_DFL;
359 (void) sigemptyset(&act.sa_mask);
360 act.sa_flags = 0;
361 (void) sigaction(sig, &act, 0);
362
363 (void) raise(sig);
364 }
365 }