1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1985-2010 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                  David Korn <dgk@research.att.com>                   *
19*                   Phong Vo <kpv@research.att.com>                    *
20*                                                                      *
21***********************************************************************/
22#pragma prototyped
23/*
24 * Glenn Fowler
25 * AT&T Research
26 *
27 * return in path the full path name of the probe(1)
28 * information for lang and tool using proc
29 * if attr != 0 then path attribute assignments placed here
30 *
31 * if path==0 then the space is malloc'd
32 *
33 * op:
34 *
35 *	-3	return non-writable path name with no generation
36 *	-2	return path name with no generation
37 *	-1	return no $HOME path name with no generation
38 *	0	verbose probe
39 *	1	silent probe
40 *
41 * 0 returned if the info does not exist and cannot be generated
42 */
43
44#include <ast.h>
45#include <error.h>
46#include <ls.h>
47#include <proc.h>
48
49#ifndef PROBE
50#define PROBE		"probe"
51#endif
52
53#if defined(ST_RDONLY) || defined(ST_NOSUID)
54
55/*
56 * return non-0 if path is in a readonly or non-setuid fs
57 */
58
59static int
60rofs(const char* path)
61{
62	struct statvfs	vfs;
63	struct stat	st;
64
65	if (!statvfs(path, &vfs))
66	{
67#if defined(ST_RDONLY)
68		if (vfs.f_flag & ST_RDONLY)
69			return 1;
70#endif
71#if defined(ST_NOSUID)
72		if ((vfs.f_flag & ST_NOSUID) && (stat(path, &st) || st.st_uid != getuid() && st.st_uid != geteuid()))
73			return 1;
74#endif
75	}
76	return 0;
77}
78
79#else
80
81#define rofs(p)		0
82
83#endif
84
85char*
86pathprobe(char* path, char* attr, const char* lang, const char* tool, const char* aproc, int op)
87{
88	char*		proc = (char*)aproc;
89	register char*	p;
90	register char*	k;
91	register char*	x;
92	register char**	ap;
93	int		n;
94	int		v;
95	int		force;
96	ssize_t		r;
97	char*		e;
98	char*		np;
99	char*		nx;
100	char*		probe;
101	const char*	dirs;
102	const char*	dir;
103	Proc_t*		pp;
104	Sfio_t*		sp;
105	char		buf[PATH_MAX];
106	char		cmd[PATH_MAX];
107	char		exe[PATH_MAX];
108	char		lib[PATH_MAX];
109	char		ver[PATH_MAX];
110	char		key[16];
111	char*		arg[8];
112	long		ops[2];
113	unsigned long	ptime;
114	struct stat	st;
115	struct stat	ps;
116
117	if (*proc != '/')
118	{
119		if (p = strchr(proc, ' '))
120		{
121			strncopy(buf, proc, p - proc + 1);
122			proc = buf;
123		}
124		if (!(proc = pathpath(cmd, proc, NiL, PATH_ABSOLUTE|PATH_REGULAR|PATH_EXECUTE)))
125			proc = (char*)aproc;
126		else if (p)
127		{
128			n = strlen(proc);
129			strncopy(proc + n, p, PATH_MAX - n - 1);
130		}
131	}
132	if (!path)
133		path = buf;
134	probe = PROBE;
135	x = lib + sizeof(lib) - 1;
136	k = lib + sfsprintf(lib, x - lib, "lib/%s/", probe);
137	p = k + sfsprintf(k, x - k, "%s/%s/", lang, tool);
138	pathkey(key, attr, lang, tool, proc);
139	if (op >= -2)
140	{
141		strncopy(p, key, x - p);
142		if (pathpath(path, lib, "", PATH_ABSOLUTE) && !stat(path, &st) && (st.st_mode & S_IWUSR))
143			return path == buf ? strdup(path) : path;
144	}
145	e = strncopy(p, probe, x - p);
146	if (!pathpath(path, lib, "", PATH_ABSOLUTE|PATH_EXECUTE) || stat(path, &ps))
147		return 0;
148	for (;;)
149	{
150		ptime = ps.st_mtime;
151		n = strlen(path);
152		if (n < (PATH_MAX - 5))
153		{
154			strcpy(path + n, ".ini");
155			if (!stat(path, &st) && st.st_size && ptime < (unsigned long)st.st_mtime)
156				ptime = st.st_mtime;
157			path[n] = 0;
158		}
159		np = path + n - (e - k);
160		nx = path + PATH_MAX - 1;
161		strncopy(np, probe, nx - np);
162		if (!stat(path, &st))
163			break;
164
165		/*
166		 * yes lib/probe/<lang>/<proc>/probe
167		 *  no lib/probe/probe
168		 *
169		 * do a manual pathaccess() to find a dir with both
170		 */
171
172		sfsprintf(exe, sizeof(exe), "lib/%s/%s", probe, probe);
173		dirs = pathbin();
174		for (;;)
175		{
176			if (!(dir = dirs))
177				return 0;
178			dirs = pathcat(path, dir, ':', "..", exe);
179			pathcanon(path, 0);
180			if (*path == '/' && pathexists(path, PATH_REGULAR|PATH_EXECUTE))
181			{
182				pathcat(path, dir, ':', "..", lib);
183				pathcanon(path, 0);
184				if (*path == '/' && pathexists(path, PATH_REGULAR|PATH_EXECUTE) && !stat(path, &ps))
185					break;
186			}
187		}
188	}
189	strncopy(p, key, x - p);
190	p = np;
191	x = nx;
192	strcpy(exe, path);
193	if (op >= -1 && (!(st.st_mode & S_ISUID) && ps.st_uid != geteuid() || rofs(path)))
194	{
195		if (!(p = getenv("HOME")))
196			return 0;
197		p = path + sfsprintf(path, PATH_MAX - 1, "%s/.%s/%s/", p, probe, HOSTTYPE);
198	}
199	strncopy(p, k, x - p);
200	force = 0;
201	if (op >= 0 && !stat(path, &st))
202	{
203		if (ptime <= (unsigned long)st.st_mtime || ptime <= (unsigned long)st.st_ctime)
204		{
205			/*
206			 * verify (<sep><name><sep><option><sep><value>)* header
207			 */
208
209			if (sp = sfopen(NiL, path, "r"))
210			{
211				if (x = sfgetr(sp, '\n', 1))
212				{
213					while (*x && *x != ' ')
214						x++;
215					while (*x == ' ')
216						x++;
217					if (n = *x++)
218						for (;;)
219						{
220							for (k = x; *x && *x != n; x++);
221							if (!*x)
222								break;
223							*x++ = 0;
224							for (p = x; *x && *x != n; x++);
225							if (!*x)
226								break;
227							*x++ = 0;
228							for (e = x; *x && *x != n; x++);
229							if (!*x)
230								break;
231							*x++ = 0;
232							if (streq(k, "VERSION"))
233							{
234								ap = arg;
235								*ap++ = proc;
236								*ap++ = p;
237								*ap = 0;
238								ops[0] =  PROC_FD_DUP(1, 2, 0);
239								ops[1] = 0;
240								if (pp = procopen(proc, arg, NiL, ops, PROC_READ))
241								{
242									if ((v = x - e) >= sizeof(ver))
243										v = sizeof(ver) - 1;
244									for (k = p = ver;; k++)
245									{
246										if (k >= p)
247										{
248											if (v <= 0 || (r = read(pp->rfd, k, v)) <= 0)
249												break;
250											v -= r;
251											p = k + r;
252										}
253										if (*k == '\n' || *k == '\r')
254											break;
255										if (*k == n)
256											*k = ' ';
257									}
258									*k = 0;
259									if (strcmp(ver, e))
260									{
261										force = 1;
262										error(0, "probe processor %s version \"%s\" changed -- expected \"%s\"", proc, ver, e);
263									}
264									procclose(pp);
265								}
266								break;
267							}
268						}
269				}
270				sfclose(sp);
271			}
272			if (!force)
273				op = -1;
274		}
275		if (op >= 0 && (st.st_mode & S_IWUSR))
276		{
277			if (op == 0)
278				error(0, "%s probe information for %s language processor %s must be manually regenerated", tool, lang, proc);
279			op = -1;
280			force = 0;
281		}
282	}
283	if (op >= 0)
284	{
285		ap = arg;
286		*ap++ = exe;
287		if (force)
288			*ap++ = "-f";
289		if (op > 0)
290			*ap++ = "-s";
291		*ap++ = (char*)lang;
292		*ap++ = (char*)tool;
293		*ap++ = proc;
294		*ap = 0;
295		if (procrun(exe, arg, 0))
296			return 0;
297		if (eaccess(path, R_OK))
298			return 0;
299	}
300	return path == buf ? strdup(path) : path;
301}
302