Track gratuitous change in mdwopt interface.
[checkpath] / tmpdir.c
CommitLineData
efa7a97b 1/* -*-c-*-
2 *
cef4f797 3 * $Id: tmpdir.c,v 1.2 1999/05/19 20:37:28 mdw Exp $
efa7a97b 4 *
5 * Choose and check temporary directories
6 *
7 * (c) 1999 Mark Wooding
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of chkpath.
13 *
14 * chkpath is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * chkpath is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with chkpath; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: tmpdir.c,v $
cef4f797 32 * Revision 1.2 1999/05/19 20:37:28 mdw
33 * Track gratuitous change in mdwopt interface.
34 *
35 * Revision 1.1.1.1 1999/04/06 20:12:07 mdw
36 * Import new project.
efa7a97b 37 *
38 */
39
40/*----- Header files ------------------------------------------------------*/
41
42#include <errno.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46
47#include <sys/types.h>
48#include <sys/stat.h>
49#include <unistd.h>
50#include <pwd.h>
51
52#include <mLib/alloc.h>
53#include <mLib/dstr.h>
54#include <mLib/mdwopt.h>
55#include <mLib/quis.h>
56#include <mLib/report.h>
57
58#include "path.h"
59
60/*----- Static variables --------------------------------------------------*/
61
62static uid_t me;
63static struct chkpath cp;
64static struct passwd *pw;
65
66/*----- Main code ---------------------------------------------------------*/
67
68/* --- @ok@ --- *
69 *
70 * Arguments: @const char *p@ = pathname to check
71 * @int *f@ = try-to-create flag
72 *
73 * Returns: Nonzero if the directory is OK
74 *
75 * Use: Ensures that a directory is OK. If @f@ is a real pointer,
76 * and @*f@ is set, then try to create the directory.
77 */
78
79static int ok(const char *p, int *f)
80{
81 struct stat st;
82
83 /* --- Read the directory status --- */
84
85 if (lstat(p, &st)) {
86
87 /* --- Maybe create it if it doesn't exist --- */
88
89 if (errno != ENOENT || !f || !*f)
90 return (0);
91 if (mkdir(p, 0700)) {
92 *f = 0;
93 return (0);
94 }
95
96 /* --- Now read the new status --- *
97 *
98 * This fixes a race condition between the previous @lstat@ call and
99 * the @mkdir@.
100 */
101
102 if (lstat(p, &st))
103 return (0);
104 }
105
106 /* --- Make sure the directory is good --- *
107 *
108 * It must be a genuine directory (not a link); it must be readable
109 * and writable only by its owner, and that owner must be me.
110 */
111
112 if (S_ISDIR(st.st_mode) && (st.st_mode & 0777) == 0700 && st.st_uid == me)
113 return (1);
114 return (0);
115}
116
117/* --- @trytmp@ --- *
118 *
119 * Arguments: @const char *parent@ = parent directory name
120 * @const char *base@ = subdirectory base name
121 *
122 * Returns: Pointer to directory name, or null. (String needs freeing.)
123 *
124 * Use: Tries to find or create an appropriate temporary directory.
125 */
126
127static char *trytmp(const char *parent, const char *base)
128{
129 static char c[] = { "0123456789"
130 "abcdefghijklmnopqrstuvwxyz"
131 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" };
132 char *p, *q;
133 char *qq;
134 dstr d;
135 int createflag = 1;
136
137 /* --- Make sure the parent directory is sane --- *
138 *
139 * Must make sure that all the lead-up to the temporary directory is safe.
140 * Otherwise other people can play with symlinks or rename directories
141 * after I've done all this careful work to make the endpoint directory
142 * safe.
143 */
144
145 if (path_check(parent, &cp))
146 return (0);
147
148 /* --- See whether the trivial version will work --- */
149
150 dstr_create(&d);
151 dstr_putf(&d, "%s/%s", parent, base);
152 if (ok(d.buf, &createflag))
153 goto good;
154
155 /* --- Now try with various suffixes --- */
156
157 DENSURE(&d, 4);
158 qq = d.buf + d.len;
159 *qq++ = '-';
160
161 for (p = c; *p; p++) {
162 qq[0] = *p;
163 qq[1] = 0;
164 if (ok(d.buf, &createflag))
165 goto good;
166 for (q = c; *q; q++) {
167 qq[1] = *q;
168 qq[2] = 0;
169 if (ok(d.buf, &createflag))
170 goto good;
171 }
172 }
173
174 /* --- No joy --- */
175
176 dstr_destroy(&d);
177 return (0);
178
179good:
180 p = xstrdup(d.buf);
181 dstr_destroy(&d);
182 return (p);
183}
184
185/* --- @fullcheck@ --- *
186 *
187 * Arguments: @const char *p@ = pointer to path to check
188 *
189 * Returns: Zero if it's a bad directory.
190 *
191 * Use: Runs a thorough check on a directory.
192 */
193
194static int fullcheck(const char *p)
195{
196 return (path_check(p, &cp) == 0 && ok(p, 0));
197}
198
199/* --- @goodtmp@ --- *
200 *
201 * Arguments: ---
202 *
203 * Returns: Pointer to a known-good secure temporary directory, or
204 * null.
205 *
206 * Use: Finds a good place to store temporary files.
207 */
208
209static char *goodtmp(void)
210{
211 char *p, *q;
212
213 /* --- First of all, try the user's current choice --- */
214
215 if ((p = getenv("TMPDIR")) != 0 && fullcheck(p))
216 return (p);
217
218 /* --- Try making a directory in `/tmp' --- */
219
220 if (!(q = getenv("USER")) && !(q = getenv("LOGNAME")))
221 q = pw->pw_name;
222 if ((q = trytmp("/tmp", q)) != 0)
223 return (q);
224
225 /* --- That failed: try a directory in the user's home --- */
226
227 if (!(q = getenv("HOME")))
228 q = pw->pw_dir;
229 if ((q = trytmp(q, "tmp")) != 0)
230 return (q);
231
232 /* --- Still no joy: give up --- *
233 *
234 * To be fair, if the user can't find a safe place in his own home
235 * directory then he's pretty stuffed.
236 */
237
238 return (0);
239}
240
241/* --- @usage@ --- */
242
243static void usage(FILE *fp)
244{
245 fprintf(fp, "Usage: %s [-bc] [-v PATH]\n", QUIS);
246}
247
248/* --- @version@ --- */
249
250static void version(FILE *fp)
251{
252 fprintf(fp, "%s version %s\n", QUIS, VERSION);
253}
254
255/* --- @help@ --- */
256
257static void help(FILE *fp)
258{
259 version(fp);
260 putc('\n', fp);
261 usage(fp);
262 fputs("\n\
263Sets a suitable and secure temporary directory, or checks that a given\n\
264directory is suitable for use with temporary files. A directory is\n\
265considered good for a particular user if it's readable and writable only\n\
266by that user, and if all its parents are modifiable only by the user or\n\
267root.\n\
268\n\
269Options supported:\n\
270\n\
271-h, --help Display this help text.\n\
272-V, --version Display the program's version number.\n\
273-u, --usage Display a terse usage summary.\n\
274\n\
275-b, --bourne Output a `TMPDIR' setting for Bourne shell users.\n\
276-c, --cshell Output a `TMPDIR' setting for C shell users.\n\
277-v, --verify PATH Check whether PATH is good, setting exit status.\n\
278\n\
279The default action is to examine the caller's shell and output a suitable\n\
280setting for that shell type.\n\
281",
282 fp);
283}
284
285/* --- @main@ --- *
286 *
287 * Arguments: @int argc@ = number of command line arguments
288 * @char *argv[]@ = the actual argument strings
289 *
290 * Returns: Zero if all went well, else nonzero.
291 *
292 * Use: Performs helpful operations on temporary directories.
293 */
294
295int main(int argc, char *argv[])
296{
297 int shell = 0;
298 int duff = 0;
299
300 enum {
301 sh_unknown,
302 sh_bourne,
303 sh_csh
304 };
305
306 /* --- Initialize variables --- */
307
308 ego(argv[0]);
309 me = getuid();
310 cp.cp_what = CP_WRWORLD | CP_WRGRP | CP_WROTHUSR | CP_STICKYOK;
311 cp.cp_verbose = 0;
312 cp.cp_report = 0;
313 path_setids(&cp);
314 pw = getpwuid(me);
315 if (!pw)
316 die(1, "you don't exist");
317
318 /* --- Parse arguments --- */
319
320 for (;;) {
321 static struct option opts[] = {
322 { "help", 0, 0, 'h' },
323 { "version", 0, 0, 'V' },
324 { "usage", 0, 0, 'u' },
325 { "bourne", 0, 0, 'b' },
326 { "cshell", 0, 0, 'c' },
cef4f797 327 { "verify", OPTF_ARGREQ, 0, 'v' },
efa7a97b 328 { 0, 0, 0, 0 }
329 };
330 int i = mdwopt(argc, argv, "hVu bcv:", opts, 0, 0, 0);
331
332 if (i < 0)
333 break;
334 switch (i) {
335 case 'h':
336 help(stdout);
337 exit(0);
338 case 'V':
339 version(stdout);
340 exit(0);
341 case 'u':
342 usage(stdout);
343 exit(0);
344 case 'b':
345 shell = sh_bourne;
346 break;
347 case 'c':
348 shell = sh_csh;
349 break;
350 case 'v':
351 return (!fullcheck(optarg));
352 break;
353 default:
354 duff = 1;
355 break;
356 }
357 }
358
359 if (duff || optind != argc) {
360 usage(stderr);
361 exit(1);
362 }
363
364 /* --- Choose a shell --- */
365
366 if (!shell) {
367 char *p;
368 if (!(p = getenv("SHELL")))
369 p = pw->pw_shell;
370 if (strstr(p, "csh"))
371 shell = sh_csh;
372 else
373 shell = sh_bourne;
374 }
375
376 /* --- Start the checking --- */
377
378 {
379 char *p = goodtmp();
380 if (!p)
381 die(1, "no good tmp directory");
382 switch (shell) {
383 case sh_bourne:
384 printf("TMPDIR=\"%s\"; export TMPDIR\n", p);
385 break;
386 case sh_csh:
387 printf("setenv TMPDIR \"%s\"\n", p);
388 break;
389 }
390 }
391
392 return (0);
393}
394
395/*----- That's all, folks -------------------------------------------------*/