1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin * *
3da2e3ebdSchin * This software is part of the ast package *
4*3e14f97fSRoger A. Faulkner * Copyright (c) 1985-2010 AT&T Intellectual Property *
5da2e3ebdSchin * and is licensed under the *
6da2e3ebdSchin * Common Public License, Version 1.0 *
77c2fbfb3SApril Chin * by AT&T Intellectual Property *
8da2e3ebdSchin * *
9da2e3ebdSchin * A copy of the License is available at *
10da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt *
11da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12da2e3ebdSchin * *
13da2e3ebdSchin * Information and Software Systems Research *
14da2e3ebdSchin * AT&T Research *
15da2e3ebdSchin * Florham Park NJ *
16da2e3ebdSchin * *
17da2e3ebdSchin * Glenn Fowler <gsf@research.att.com> *
18da2e3ebdSchin * David Korn <dgk@research.att.com> *
19da2e3ebdSchin * Phong Vo <kpv@research.att.com> *
20da2e3ebdSchin * *
21da2e3ebdSchin ***********************************************************************/
22da2e3ebdSchin #pragma prototyped
23da2e3ebdSchin /*
24da2e3ebdSchin * AT&T Research
25da2e3ebdSchin *
26da2e3ebdSchin * generate a temp file / name
27da2e3ebdSchin *
28da2e3ebdSchin * [<dir>/][<pfx>]<bas>.<suf>
29da2e3ebdSchin *
30da2e3ebdSchin * length(<pfx>)<=5
31da2e3ebdSchin * length(<bas>)==3
32da2e3ebdSchin * length(<suf>)==3
33da2e3ebdSchin *
34da2e3ebdSchin * pathtmp(a,b,c,d) pathtemp(a,L_tmpnam,b,c,0)
35da2e3ebdSchin * tmpfile() char*p=pathtemp(0,0,0,"tf",&sp);
36da2e3ebdSchin * remove(p);
37da2e3ebdSchin * free(p)
38da2e3ebdSchin * tmpnam(0) static char p[L_tmpnam];
39da2e3ebdSchin * pathtemp(p,sizeof(p),0,"tn",0)
40da2e3ebdSchin * tmpnam(p) pathtemp(p,L_tmpnam,0,"tn",0)
41da2e3ebdSchin * tempnam(d,p) pathtemp(0,d,p,0)
42*3e14f97fSRoger A. Faulkner * mktemp(p) pathtemp(0,0,p,0)
43da2e3ebdSchin *
44da2e3ebdSchin * if buf==0 then space is malloc'd
45da2e3ebdSchin * buf size is size
46da2e3ebdSchin * dir and pfx may be 0
47*3e14f97fSRoger A. Faulkner * if pfx contains trailing X's then it is a mktemp(3) template
48*3e14f97fSRoger A. Faulkner * otherwise only first 5 chars of pfx are used
49da2e3ebdSchin * if fdp!=0 then the path is opened O_EXCL and *fdp is the open fd
50da2e3ebdSchin * malloc'd space returned by successful pathtemp() calls
51da2e3ebdSchin * must be freed by the caller
52da2e3ebdSchin *
53da2e3ebdSchin * generated names are pseudo-randomized to avoid both
54da2e3ebdSchin * collisions and predictions (same alg in sfio/sftmp.c)
55da2e3ebdSchin *
56da2e3ebdSchin * / as first pfx char provides tmp file generation control
57da2e3ebdSchin * 0 returned for unknown ops
58da2e3ebdSchin *
59da2e3ebdSchin * /cycle dir specifies TMPPATH cycle control
60da2e3ebdSchin * automatic (default) cycled with each tmp file
61da2e3ebdSchin * manual cycled by application with dir=(nil)
62da2e3ebdSchin * (nil) cycle TMPPATH
63da2e3ebdSchin * /prefix dir specifies the default prefix (default ast)
64*3e14f97fSRoger A. Faulkner * /private private file/dir modes
65*3e14f97fSRoger A. Faulkner * /public public file/dir modes
66*3e14f97fSRoger A. Faulkner * /seed dir specifies pseudo-random generator seed
67*3e14f97fSRoger A. Faulkner * 0 or "0" to re-initialize
68da2e3ebdSchin * /TMPPATH dir overrides the env value
69da2e3ebdSchin * /TMPDIR dir overrides the env value
70da2e3ebdSchin */
71da2e3ebdSchin
72da2e3ebdSchin #include <ast.h>
73da2e3ebdSchin #include <ls.h>
74*3e14f97fSRoger A. Faulkner #include <tv.h>
75da2e3ebdSchin #include <tm.h>
76da2e3ebdSchin
77da2e3ebdSchin #define ATTEMPT 10
78da2e3ebdSchin
79da2e3ebdSchin #define TMP_ENV "TMPDIR"
80da2e3ebdSchin #define TMP_PATH_ENV "TMPPATH"
81da2e3ebdSchin #define TMP1 "/tmp"
82da2e3ebdSchin #define TMP2 "/usr/tmp"
83da2e3ebdSchin
84da2e3ebdSchin #define VALID(d) (*(d)&&!eaccess(d,W_OK|X_OK))
85da2e3ebdSchin
86da2e3ebdSchin static struct
87da2e3ebdSchin {
88da2e3ebdSchin mode_t mode;
89da2e3ebdSchin char** vec;
90da2e3ebdSchin char** dir;
91*3e14f97fSRoger A. Faulkner uint32_t key;
92*3e14f97fSRoger A. Faulkner uint32_t rng;
93da2e3ebdSchin pid_t pid;
94da2e3ebdSchin int manual;
95*3e14f97fSRoger A. Faulkner int seed;
96da2e3ebdSchin char* pfx;
97da2e3ebdSchin char* tmpdir;
98da2e3ebdSchin char* tmppath;
99da2e3ebdSchin } tmp = { S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH };
100da2e3ebdSchin
101da2e3ebdSchin char*
pathtemp(char * buf,size_t len,const char * dir,const char * pfx,int * fdp)102da2e3ebdSchin pathtemp(char* buf, size_t len, const char* dir, const char* pfx, int* fdp)
103da2e3ebdSchin {
104da2e3ebdSchin register char* d;
105da2e3ebdSchin register char* b;
106da2e3ebdSchin register char* s;
107da2e3ebdSchin register char* x;
108*3e14f97fSRoger A. Faulkner uint32_t key;
109da2e3ebdSchin int m;
110da2e3ebdSchin int n;
111*3e14f97fSRoger A. Faulkner int l;
112*3e14f97fSRoger A. Faulkner int r;
113da2e3ebdSchin int z;
114da2e3ebdSchin int attempt;
115*3e14f97fSRoger A. Faulkner Tv_t tv;
116*3e14f97fSRoger A. Faulkner char keybuf[16];
117da2e3ebdSchin
118da2e3ebdSchin if (pfx && *pfx == '/')
119da2e3ebdSchin {
120da2e3ebdSchin pfx++;
121da2e3ebdSchin if (streq(pfx, "cycle"))
122da2e3ebdSchin {
123da2e3ebdSchin if (!dir)
124da2e3ebdSchin {
125da2e3ebdSchin tmp.manual = 1;
126da2e3ebdSchin if (tmp.dir && !*tmp.dir++)
127da2e3ebdSchin tmp.dir = tmp.vec;
128da2e3ebdSchin }
129da2e3ebdSchin else
130da2e3ebdSchin tmp.manual = streq(dir, "manual");
131da2e3ebdSchin return (char*)pfx;
132da2e3ebdSchin }
133da2e3ebdSchin else if (streq(pfx, "prefix"))
134da2e3ebdSchin {
135da2e3ebdSchin if (tmp.pfx)
136da2e3ebdSchin free(tmp.pfx);
137da2e3ebdSchin tmp.pfx = dir ? strdup(dir) : (char*)0;
138da2e3ebdSchin return (char*)pfx;
139da2e3ebdSchin }
140da2e3ebdSchin else if (streq(pfx, "private"))
141*3e14f97fSRoger A. Faulkner {
142da2e3ebdSchin tmp.mode = S_IRUSR|S_IWUSR;
143*3e14f97fSRoger A. Faulkner return (char*)pfx;
144*3e14f97fSRoger A. Faulkner }
145da2e3ebdSchin else if (streq(pfx, "public"))
146*3e14f97fSRoger A. Faulkner {
147da2e3ebdSchin tmp.mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
148*3e14f97fSRoger A. Faulkner return (char*)pfx;
149*3e14f97fSRoger A. Faulkner }
150*3e14f97fSRoger A. Faulkner else if (streq(pfx, "seed"))
151*3e14f97fSRoger A. Faulkner {
152*3e14f97fSRoger A. Faulkner tmp.key = (tmp.seed = (tmp.rng = dir ? (uint32_t)strtoul(dir, NiL, 0) : (uint32_t)1) != 0)? (uint32_t)0x63c63cd9L : 0;
153*3e14f97fSRoger A. Faulkner return (char*)pfx;
154*3e14f97fSRoger A. Faulkner }
155da2e3ebdSchin else if (streq(pfx, TMP_ENV))
156da2e3ebdSchin {
157da2e3ebdSchin if (tmp.vec)
158da2e3ebdSchin {
159da2e3ebdSchin free(tmp.vec);
160da2e3ebdSchin tmp.vec = 0;
161da2e3ebdSchin }
162da2e3ebdSchin if (tmp.tmpdir)
163da2e3ebdSchin free(tmp.tmpdir);
164da2e3ebdSchin tmp.tmpdir = dir ? strdup(dir) : (char*)0;
165da2e3ebdSchin return (char*)pfx;
166da2e3ebdSchin }
167da2e3ebdSchin else if (streq(pfx, TMP_PATH_ENV))
168da2e3ebdSchin {
169da2e3ebdSchin if (tmp.vec)
170da2e3ebdSchin {
171da2e3ebdSchin free(tmp.vec);
172da2e3ebdSchin tmp.vec = 0;
173da2e3ebdSchin }
174da2e3ebdSchin if (tmp.tmppath)
175da2e3ebdSchin free(tmp.tmppath);
176da2e3ebdSchin tmp.tmppath = dir ? strdup(dir) : (char*)0;
177da2e3ebdSchin return (char*)pfx;
178da2e3ebdSchin }
179da2e3ebdSchin return 0;
180da2e3ebdSchin }
181*3e14f97fSRoger A. Faulkner if (tmp.seed)
182*3e14f97fSRoger A. Faulkner tv.tv_nsec = 0;
183*3e14f97fSRoger A. Faulkner else
184*3e14f97fSRoger A. Faulkner tvgettime(&tv);
185da2e3ebdSchin if (!(d = (char*)dir) || *d && eaccess(d, W_OK|X_OK))
186da2e3ebdSchin {
187da2e3ebdSchin if (!tmp.vec)
188da2e3ebdSchin {
189da2e3ebdSchin if ((x = tmp.tmppath) || (x = getenv(TMP_PATH_ENV)))
190da2e3ebdSchin {
191da2e3ebdSchin n = 2;
192da2e3ebdSchin s = x;
193da2e3ebdSchin while (s = strchr(s, ':'))
194da2e3ebdSchin {
195da2e3ebdSchin s++;
196da2e3ebdSchin n++;
197da2e3ebdSchin }
198da2e3ebdSchin if (!(tmp.vec = newof(0, char*, n, strlen(x) + 1)))
199da2e3ebdSchin return 0;
200da2e3ebdSchin tmp.dir = tmp.vec;
201da2e3ebdSchin x = strcpy((char*)(tmp.dir + n), x);
202da2e3ebdSchin *tmp.dir++ = x;
203da2e3ebdSchin while (x = strchr(x, ':'))
204da2e3ebdSchin {
205da2e3ebdSchin *x++ = 0;
206da2e3ebdSchin if (!VALID(*(tmp.dir - 1)))
207da2e3ebdSchin tmp.dir--;
208da2e3ebdSchin *tmp.dir++ = x;
209da2e3ebdSchin }
210da2e3ebdSchin if (!VALID(*(tmp.dir - 1)))
211da2e3ebdSchin tmp.dir--;
212da2e3ebdSchin *tmp.dir = 0;
213da2e3ebdSchin }
214da2e3ebdSchin else
215da2e3ebdSchin {
216da2e3ebdSchin if (((d = tmp.tmpdir) || (d = getenv(TMP_ENV))) && !VALID(d))
217da2e3ebdSchin d = 0;
218da2e3ebdSchin if (!(tmp.vec = newof(0, char*, 2, d ? (strlen(d) + 1) : 0)))
219da2e3ebdSchin return 0;
220da2e3ebdSchin if (d)
221da2e3ebdSchin *tmp.vec = strcpy((char*)(tmp.vec + 2), d);
222da2e3ebdSchin }
223da2e3ebdSchin tmp.dir = tmp.vec;
224da2e3ebdSchin }
225da2e3ebdSchin if (!(d = *tmp.dir++))
226da2e3ebdSchin {
227da2e3ebdSchin tmp.dir = tmp.vec;
228da2e3ebdSchin d = *tmp.dir++;
229da2e3ebdSchin }
230da2e3ebdSchin if (!d && (!*(d = astconf("TMP", NiL, NiL)) || eaccess(d, W_OK|X_OK)) && eaccess(d = TMP1, W_OK|X_OK) && eaccess(d = TMP2, W_OK|X_OK))
231da2e3ebdSchin return 0;
232da2e3ebdSchin }
233da2e3ebdSchin if (!len)
234da2e3ebdSchin len = PATH_MAX;
235da2e3ebdSchin len--;
236da2e3ebdSchin if (!(b = buf) && !(b = newof(0, char, len, 1)))
237da2e3ebdSchin return 0;
238da2e3ebdSchin z = 0;
239*3e14f97fSRoger A. Faulkner if (!pfx && !(pfx = tmp.pfx))
240*3e14f97fSRoger A. Faulkner pfx = "ast";
241*3e14f97fSRoger A. Faulkner m = strlen(pfx);
242*3e14f97fSRoger A. Faulkner if (buf && dir && (buf == (char*)dir && (buf + strlen(buf) + 1) == (char*)pfx || buf == (char*)pfx && !*dir) && !strcmp((char*)pfx + m + 1, "XXXXX"))
243*3e14f97fSRoger A. Faulkner {
244da2e3ebdSchin d = (char*)dir;
245*3e14f97fSRoger A. Faulkner len = m += strlen(d) + 8;
246*3e14f97fSRoger A. Faulkner l = 3;
247*3e14f97fSRoger A. Faulkner r = 3;
248*3e14f97fSRoger A. Faulkner }
249*3e14f97fSRoger A. Faulkner else if (*pfx && pfx[m - 1] == 'X')
250*3e14f97fSRoger A. Faulkner {
251*3e14f97fSRoger A. Faulkner for (l = m; l && pfx[l - 1] == 'X'; l--);
252*3e14f97fSRoger A. Faulkner r = m - l;
253*3e14f97fSRoger A. Faulkner m = l;
254*3e14f97fSRoger A. Faulkner l = r / 2;
255*3e14f97fSRoger A. Faulkner r -= l;
256*3e14f97fSRoger A. Faulkner }
257*3e14f97fSRoger A. Faulkner else if (strchr(pfx, '.'))
258*3e14f97fSRoger A. Faulkner {
259*3e14f97fSRoger A. Faulkner m = 5;
260*3e14f97fSRoger A. Faulkner l = 3;
261*3e14f97fSRoger A. Faulkner r = 3;
262da2e3ebdSchin }
263da2e3ebdSchin else
264da2e3ebdSchin {
265da2e3ebdSchin z = '.';
266da2e3ebdSchin m = 5;
267*3e14f97fSRoger A. Faulkner l = 2;
268*3e14f97fSRoger A. Faulkner r = 3;
269da2e3ebdSchin }
270da2e3ebdSchin x = b + len;
271da2e3ebdSchin s = b;
272da2e3ebdSchin if (d)
273da2e3ebdSchin {
274da2e3ebdSchin while (s < x && (n = *d++))
275da2e3ebdSchin *s++ = n;
276da2e3ebdSchin if (s < x && s > b && *(s - 1) != '/')
277da2e3ebdSchin *s++ = '/';
278da2e3ebdSchin }
279da2e3ebdSchin if ((x - s) > m)
280da2e3ebdSchin x = s + m;
281da2e3ebdSchin while (s < x && (n = *pfx++))
282da2e3ebdSchin {
283da2e3ebdSchin if (n == '/' || n == '\\' || n == z)
284da2e3ebdSchin n = '_';
285da2e3ebdSchin *s++ = n;
286da2e3ebdSchin }
287da2e3ebdSchin *s = 0;
288da2e3ebdSchin len -= (s - b);
289da2e3ebdSchin for (attempt = 0; attempt < ATTEMPT; attempt++)
290da2e3ebdSchin {
291*3e14f97fSRoger A. Faulkner if (!tmp.rng || !tmp.seed && (attempt || tmp.pid != getpid()))
292da2e3ebdSchin {
293da2e3ebdSchin register int r;
294da2e3ebdSchin
295da2e3ebdSchin /*
296da2e3ebdSchin * get a quasi-random coefficient
297da2e3ebdSchin */
298da2e3ebdSchin
299da2e3ebdSchin tmp.pid = getpid();
300*3e14f97fSRoger A. Faulkner tmp.rng = (uintptr_t)tmp.pid * ((uintptr_t)time(NiL) ^ (((uintptr_t)(&attempt)) >> 3) ^ (((uintptr_t)tmp.dir) >> 3));
301da2e3ebdSchin if (!tmp.key)
302da2e3ebdSchin tmp.key = (tmp.rng >> 16) | ((tmp.rng & 0xffff) << 16);
303da2e3ebdSchin tmp.rng ^= tmp.key;
304da2e3ebdSchin
305da2e3ebdSchin /*
306da2e3ebdSchin * Knuth vol.2, page.16, Thm.A
307da2e3ebdSchin */
308da2e3ebdSchin
309da2e3ebdSchin if ((r = (tmp.rng - 1) & 03))
310da2e3ebdSchin tmp.rng += 4 - r;
311da2e3ebdSchin }
312da2e3ebdSchin
313da2e3ebdSchin /*
314da2e3ebdSchin * generate a pseudo-random name
315da2e3ebdSchin */
316da2e3ebdSchin
317*3e14f97fSRoger A. Faulkner key = tmp.rng * tmp.key + tv.tv_nsec;
318*3e14f97fSRoger A. Faulkner if (!tmp.seed)
319*3e14f97fSRoger A. Faulkner tvgettime(&tv);
320*3e14f97fSRoger A. Faulkner tmp.key = tmp.rng * key + tv.tv_nsec;
321*3e14f97fSRoger A. Faulkner sfsprintf(keybuf, sizeof(keybuf), "%07.7.32I*u%07.7.32I*u", sizeof(key), key, sizeof(tmp.key), tmp.key);
322*3e14f97fSRoger A. Faulkner sfsprintf(s, len, "%-.*s%s%-.*s", l, keybuf, z ? "." : "", r, keybuf + sizeof(keybuf) / 2);
323da2e3ebdSchin if (fdp)
324da2e3ebdSchin {
325da2e3ebdSchin if ((n = open(b, O_CREAT|O_RDWR|O_EXCL|O_TEMPORARY, tmp.mode)) >= 0)
326da2e3ebdSchin {
327da2e3ebdSchin *fdp = n;
328da2e3ebdSchin return b;
329da2e3ebdSchin }
330da2e3ebdSchin }
331da2e3ebdSchin else if (access(b, F_OK))
332da2e3ebdSchin return b;
333da2e3ebdSchin }
334da2e3ebdSchin if (!buf)
335da2e3ebdSchin free(b);
336da2e3ebdSchin return 0;
337da2e3ebdSchin }
338