]> diplodocus.org Git - nmh/blob - sbr/m_mktemp.c
ac05306a96cb33b4c9c40ef947a3157ab0ed3700
[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 * TMP envvar
36 * User's mail directory.
37 *
38 * NOTE: One will probably use m_mktemp2() instead of this function.
39 * For example, if you want to create a temp file in the defined
40 * temporary directory, but with a custom basename prefix, do
41 * something like the following:
42 *
43 * char *tmp_pathname = m_mktemp2(NULL, "mypre", ...);
44 */
45 char *
46 m_mktemp (
47 const char *pfx_in, /* Pathname prefix for temporary file. */
48 int *fd_ret, /* (return,optional) File descriptor to temp file. */
49 FILE **fp_ret /* (return,optional) FILE pointer to temp file. */
50 )
51 {
52 static char tmpfil[BUFSIZ];
53 int fd = -1;
54 int keep_open = 0;
55 mode_t oldmode = umask(077);
56
57 if (pfx_in == NULL) {
58 snprintf(tmpfil, sizeof(tmpfil), "%s/nmhXXXXXX", get_temp_dir());
59 } else {
60 snprintf(tmpfil, sizeof(tmpfil), "%sXXXXXX", pfx_in);
61 }
62
63 fd = mkstemp(tmpfil);
64
65 if (fd < 0) {
66 umask(oldmode);
67 return NULL;
68 }
69
70 register_for_removal(tmpfil);
71
72 if (fd_ret != NULL) {
73 *fd_ret = fd;
74 keep_open = 1;
75 }
76 if (fp_ret != NULL) {
77 FILE *fp = fdopen(fd, "w+");
78 if (fp == NULL) {
79 int olderr = errno;
80 (void) m_unlink(tmpfil);
81 close(fd);
82 errno = olderr;
83 umask(oldmode);
84 return NULL;
85 }
86 *fp_ret = fp;
87 keep_open = 1;
88 }
89 if (!keep_open) {
90 close(fd);
91 }
92 umask(oldmode);
93 return tmpfil;
94 }
95
96 /* This version allows one to specify the directory the temp file should
97 * by created based on a given pathname. Although m_mktemp() technically
98 * supports this, this version is when the directory is defined by
99 * a separate variable from the prefix, eliminating the caller from having
100 * to do string manipulation to generate the desired pathname prefix.
101 *
102 * The pfx_in parameter specifies a basename prefix for the file. If dir_in
103 * is NULL, then the defined temporary directory (see comments to m_mktemp()
104 * above) is used to create the temp file.
105 */
106 char *
107 m_mktemp2 (
108 const char *dir_in, /* Directory to place temp file. */
109 const char *pfx_in, /* Basename prefix for temp file. */
110 int *fd_ret, /* (return,optional) File descriptor to temp file. */
111 FILE **fp_ret /* (return,optional) FILE pointer to temp file. */
112 )
113 {
114 static char buffer[BUFSIZ];
115 char *cp;
116 int n;
117
118 if (dir_in == NULL) {
119 if (pfx_in == NULL) {
120 return m_mktemp(NULL, fd_ret, fp_ret);
121 }
122 snprintf(buffer, sizeof(buffer), "%s/%s", get_temp_dir(), pfx_in);
123 return m_mktemp(buffer, fd_ret, fp_ret);
124 }
125
126 if ((cp = r1bindex ((char *)dir_in, '/')) == dir_in) {
127 /* No directory component */
128 return m_mktemp(pfx_in, fd_ret, fp_ret);
129 }
130 n = (int)(cp-dir_in); /* Length of dir component */
131 snprintf(buffer, sizeof(buffer), "%.*s%s", n, dir_in, pfx_in);
132 return m_mktemp(buffer, fd_ret, fp_ret);
133 }
134
135
136 char *
137 get_temp_dir()
138 {
139 /* Ignore envvars if we are setuid */
140 if ((getuid()==geteuid()) && (getgid()==getegid())) {
141 char *tmpdir = NULL;
142 tmpdir = getenv("MHTMPDIR");
143 if (tmpdir != NULL && *tmpdir != '\0') return tmpdir;
144
145 tmpdir = getenv("TMPDIR");
146 if (tmpdir != NULL && *tmpdir != '\0') return tmpdir;
147
148 tmpdir = getenv("TMP");
149 if (tmpdir != NULL && *tmpdir != '\0') return tmpdir;
150 }
151 return m_maildir("");
152 }
153
154
155 /*
156 * Array of files (full pathnames) to remove on process exit.
157 * Instead of removing slots from the array, just set to NULL
158 * to indicate that the file should no longer be removed.
159 */
160 static svector_t exit_filelist = NULL;
161
162 /*
163 * Register a file for removal at program termination.
164 */
165 static void
166 register_for_removal(const char *pathname) {
167 if (exit_filelist == NULL) exit_filelist = svector_create(20);
168 (void) svector_push_back(exit_filelist, add(pathname, NULL));
169 }
170
171 /*
172 * Unregister all files that were registered to be removed at program
173 * termination. When called with remove_files of 0, this function is
174 * intended for use by one of the programs after a fork(3) without a
175 * subsequent call to one of the exec(3) functions or _exit(3). When
176 * called with non-0 remove_files argument, it is intended for use by
177 * an atexit() function.
178 *
179 * Right after a fork() and before calling exec() or _exit(), if the
180 * child catches one of the appropriate signals, it will remove
181 * all the registered temporary files. Some of those may be needed by
182 * the parent. Some nmh programs ignore or block some of the signals
183 * in the child right after fork(). For the other programs, rather
184 * than complicate things by preventing that, just take the chance
185 * that it might happen. It is harmless to attempt to unlink a
186 * temporary file twice, assuming another one wasn't created too
187 * quickly created with the same name.
188 */
189 void
190 unregister_for_removal(int remove_files) {
191 if (exit_filelist) {
192 size_t i;
193
194 for (i = 0; i < svector_size(exit_filelist); ++i) {
195 char *filename = svector_at(exit_filelist, i);
196
197 if (filename) {
198 if (remove_files) (void) unlink(filename);
199 free(filename);
200 }
201 }
202
203 svector_free(exit_filelist);
204 exit_filelist = NULL;
205 }
206 }
207
208 /*
209 * If the file was registered for removal, deregister it. In
210 * any case, unlink it.
211 */
212 int
213 m_unlink(const char *pathname) {
214 if (exit_filelist) {
215 char **slot = svector_find(exit_filelist, pathname);
216
217 if (slot && *slot) {
218 free(*slot);
219 *slot = NULL;
220 }
221 }
222
223 return unlink(pathname);
224 /* errno should be set to ENOENT if file was not found */
225 }
226
227 /*
228 * Remove all registered temporary files.
229 */
230 void
231 remove_registered_files_atexit() {
232 unregister_for_removal(1);
233 }
234
235 /*
236 * Remove all registered temporary files. Suitable for use as a
237 * signal handler. If the signal is one of the usual process
238 * termination signals, calls exit(). Otherwise, disables itself
239 * by restoring the default action and then re-raises the signal,
240 * in case the use was expecting a core dump.
241 */
242 void
243 remove_registered_files(int sig) {
244 struct sigaction act;
245
246 /*
247 * Ignore this signal for the duration. And we either exit() or
248 * disable this signal handler below, so don't restore this handler.
249 */
250 act.sa_handler = SIG_IGN;
251 (void) sigemptyset(&act.sa_mask);
252 act.sa_flags = 0;
253 (void) sigaction(sig, &act, 0);
254
255 if (sig == SIGHUP || sig == SIGINT || sig == SIGQUIT || sig == SIGTERM) {
256 /*
257 * We don't need to call remove_registered_files_atexit() if
258 * it was registered with atexit(), but let's not assume that.
259 * It's harmless to call it twice.
260 */
261 remove_registered_files_atexit();
262
263 exit(1);
264 } else {
265 act.sa_handler = SIG_DFL;
266 (void) sigemptyset(&act.sa_mask);
267 act.sa_flags = 0;
268 (void) sigaction(sig, &act, 0);
269
270 remove_registered_files_atexit();
271
272 (void) raise(sig);
273 }
274 }