1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1985-2012 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                 Eclipse Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*          http://www.eclipse.org/org/documents/epl-v10.html           *
11*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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#define _AST_API_H	1
45
46#include <ast.h>
47#include <error.h>
48#include <ls.h>
49#include <proc.h>
50
51char*
52pathprobe(char* path, char* attr, const char* lang, const char* tool, const char* proc, int op)
53{
54	return pathprobe_20100601(lang, tool, proc, op, path, PATH_MAX, attr, PATH_MAX);
55}
56
57#undef	_AST_API_H
58
59#include <ast_api.h>
60
61#ifndef PROBE
62#define PROBE		"probe"
63#endif
64
65#if defined(ST_RDONLY) || defined(ST_NOSUID)
66
67/*
68 * return non-0 if path is in a readonly or non-setuid fs
69 */
70
71static int
72rofs(const char* path)
73{
74	struct statvfs	vfs;
75	struct stat	st;
76
77	if (!statvfs(path, &vfs))
78	{
79#if defined(ST_RDONLY)
80		if (vfs.f_flag & ST_RDONLY)
81			return 1;
82#endif
83#if defined(ST_NOSUID)
84		if ((vfs.f_flag & ST_NOSUID) && (stat(path, &st) || st.st_uid != getuid() && st.st_uid != geteuid()))
85			return 1;
86#endif
87	}
88	return 0;
89}
90
91#else
92
93#define rofs(p)		0
94
95#endif
96
97char*
98pathprobe_20100601(const char* lang, const char* tool, const char* aproc, int op, char* path, size_t pathsize, char* attr, size_t attrsize)
99{
100	char*		proc = (char*)aproc;
101	register char*	p;
102	register char*	k;
103	register char*	x;
104	register char**	ap;
105	int		n;
106	int		v;
107	int		force;
108	ssize_t		r;
109	char*		e;
110	char*		np;
111	char*		nx;
112	char*		probe;
113	const char*	dirs;
114	const char*	dir;
115	Proc_t*		pp;
116	Sfio_t*		sp;
117	char		buf[PATH_MAX];
118	char		cmd[PATH_MAX];
119	char		exe[PATH_MAX];
120	char		lib[PATH_MAX];
121	char		ver[PATH_MAX];
122	char		key[16];
123	char*		arg[8];
124	long		ops[2];
125	unsigned long	ptime;
126	struct stat	st;
127	struct stat	ps;
128
129	if (*proc != '/')
130	{
131		if (p = strchr(proc, ' '))
132		{
133			strncopy(buf, proc, p - proc + 1);
134			proc = buf;
135		}
136		if (!(proc = pathpath(proc, NiL, PATH_ABSOLUTE|PATH_REGULAR|PATH_EXECUTE, cmd, sizeof(cmd))))
137			proc = (char*)aproc;
138		else if (p)
139		{
140			n = strlen(proc);
141			strncopy(proc + n, p, PATH_MAX - n - 1);
142		}
143	}
144	if (!path)
145	{
146		path = buf;
147		pathsize = sizeof(buf);
148	}
149	probe = PROBE;
150	x = lib + sizeof(lib) - 1;
151	k = lib + sfsprintf(lib, x - lib, "lib/%s/", probe);
152	p = k + sfsprintf(k, x - k, "%s/%s/", lang, tool);
153	pathkey(lang, tool, proc, key, sizeof(key), attr, attrsize);
154	if (op >= -2)
155	{
156		strncopy(p, key, x - p);
157		if (pathpath(lib, "", PATH_ABSOLUTE, path, pathsize) && !stat(path, &st) && (st.st_mode & S_IWUSR))
158			return path == buf ? strdup(path) : path;
159	}
160	e = strncopy(p, probe, x - p);
161	if (!pathpath(lib, "", PATH_ABSOLUTE|PATH_EXECUTE, path, pathsize) || stat(path, &ps))
162		return 0;
163	for (;;)
164	{
165		ptime = ps.st_mtime;
166		n = strlen(path);
167		if (n < (PATH_MAX - 5))
168		{
169			strcpy(path + n, ".ini");
170			if (!stat(path, &st) && st.st_size && ptime < (unsigned long)st.st_mtime)
171				ptime = st.st_mtime;
172			path[n] = 0;
173		}
174		np = path + n - (e - k);
175		nx = path + PATH_MAX - 1;
176		strncopy(np, probe, nx - np);
177		if (!stat(path, &st))
178			break;
179
180		/*
181		 * yes lib/probe/<lang>/<proc>/probe
182		 *  no lib/probe/probe
183		 *
184		 * do a manual pathaccess() to find a dir with both
185		 */
186
187		sfsprintf(exe, sizeof(exe), "lib/%s/%s", probe, probe);
188		dirs = pathbin();
189		for (;;)
190		{
191			if (!(dir = dirs))
192				return 0;
193			dirs = pathcat(dir, ':', "..", exe, path, pathsize);
194			pathcanon(path, pathsize, 0);
195			if (*path == '/' && pathexists(path, PATH_REGULAR|PATH_EXECUTE))
196			{
197				pathcat(dir, ':', "..", lib, path, pathsize);
198				pathcanon(path, pathsize, 0);
199				if (*path == '/' && pathexists(path, PATH_REGULAR|PATH_EXECUTE) && !stat(path, &ps))
200					break;
201			}
202		}
203	}
204	strncopy(p, key, x - p);
205	p = np;
206	x = nx;
207	strcpy(exe, path);
208	if (op >= -1 && (!(st.st_mode & S_ISUID) && ps.st_uid != geteuid() || rofs(path)))
209	{
210		if (!(p = getenv("HOME")))
211			return 0;
212		p = path + sfsprintf(path, PATH_MAX - 1, "%s/.%s/%s/", p, probe, HOSTTYPE);
213	}
214	strncopy(p, k, x - p);
215	force = 0;
216	if (op >= 0 && !stat(path, &st))
217	{
218		if (ptime <= (unsigned long)st.st_mtime || ptime <= (unsigned long)st.st_ctime)
219		{
220			/*
221			 * verify (<sep><name><sep><option><sep><value>)* header
222			 */
223
224			if (sp = sfopen(NiL, path, "r"))
225			{
226				if (x = sfgetr(sp, '\n', 1))
227				{
228					while (*x && *x != ' ')
229						x++;
230					while (*x == ' ')
231						x++;
232					if (n = *x++)
233						for (;;)
234						{
235							for (k = x; *x && *x != n; x++);
236							if (!*x)
237								break;
238							*x++ = 0;
239							for (p = x; *x && *x != n; x++);
240							if (!*x)
241								break;
242							*x++ = 0;
243							for (e = x; *x && *x != n; x++);
244							if (!*x)
245								break;
246							*x++ = 0;
247							if (streq(k, "VERSION"))
248							{
249								ap = arg;
250								*ap++ = proc;
251								*ap++ = p;
252								*ap = 0;
253								ops[0] =  PROC_FD_DUP(1, 2, 0);
254								ops[1] = 0;
255								if (pp = procopen(proc, arg, NiL, ops, PROC_READ))
256								{
257									if ((v = x - e) >= sizeof(ver))
258										v = sizeof(ver) - 1;
259									k = p = ver;
260									for (;;)
261									{
262										if (k >= p)
263										{
264											if (v <= 0)
265												break;
266											if ((r = read(pp->rfd, k, v)) < 0)
267											{
268												if (errno == EINTR)
269													continue;
270												break;
271											}
272											if (r <= 0)
273												break;
274											v -= r;
275											p = k + r;
276										}
277										if (*k == '\n' || *k == '\r')
278											break;
279										if (*k == n)
280											*k = ' ';
281										k++;
282									}
283									*k = 0;
284									if (strcmp(ver, e))
285									{
286										force = 1;
287										error(0, "probe processor %s version \"%s\" changed -- expected \"%s\"", proc, ver, e);
288									}
289									procclose(pp);
290								}
291								break;
292							}
293						}
294				}
295				sfclose(sp);
296			}
297			if (!force)
298				op = -1;
299		}
300		if (op >= 0 && (st.st_mode & S_IWUSR))
301		{
302			if (op == 0)
303				error(0, "%s probe information for %s language processor %s must be manually regenerated", tool, lang, proc);
304			op = -1;
305			force = 0;
306		}
307	}
308	if (op >= 0)
309	{
310		ap = arg;
311		*ap++ = exe;
312		if (force)
313			*ap++ = "-f";
314		if (op > 0)
315			*ap++ = "-s";
316		*ap++ = (char*)lang;
317		*ap++ = (char*)tool;
318		*ap++ = proc;
319		*ap = 0;
320		if (procrun(exe, arg, 0))
321			return 0;
322		if (eaccess(path, R_OK))
323			return 0;
324	}
325	return path == buf ? strdup(path) : path;
326}
327