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