pathprobe.c revision 7c2fbfb345896881c631598ee3852ce9ce33fb07
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1985-2008 AT&T Intellectual Property *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* http://www.opensource.org/licenses/cpl1.0.txt *
* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* David Korn <dgk@research.att.com> *
* Phong Vo <kpv@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* Glenn Fowler
* AT&T Research
*
* return in path the full path name of the probe(1)
* information for lang and tool using proc
* if attr != 0 then path attribute assignments placed here
*
* if path==0 then the space is malloc'd
*
* op:
*
* -3 return non-writable path name with no generation
* -2 return path name with no generation
* -1 return no $HOME path name with no generation
* 0 verbose probe
* 1 silent probe
*
* 0 returned if the info does not exist and cannot be generated
*/
#include <ast.h>
#include <error.h>
#include <ls.h>
#include <proc.h>
#ifndef PROBE
#define PROBE "probe"
#endif
#if defined(ST_RDONLY) || defined(ST_NOSUID)
/*
* return non-0 if path is in a readonly or non-setuid fs
*/
static int
rofs(const char* path)
{
struct statvfs vfs;
struct stat st;
if (!statvfs(path, &vfs))
{
#if defined(ST_RDONLY)
if (vfs.f_flag & ST_RDONLY)
return 1;
#endif
#if defined(ST_NOSUID)
if ((vfs.f_flag & ST_NOSUID) && (stat(path, &st) || st.st_uid != getuid() && st.st_uid != geteuid()))
return 1;
#endif
}
return 0;
}
#else
#define rofs(p) 0
#endif
char*
pathprobe(char* path, char* attr, const char* lang, const char* tool, const char* aproc, int op)
{
char* proc = (char*)aproc;
register char* p;
register char* k;
register char* x;
register char** ap;
int n;
int v;
int force;
ssize_t r;
char* e;
char* np;
char* nx;
char* probe;
const char* dirs;
const char* dir;
Proc_t* pp;
Sfio_t* sp;
char buf[PATH_MAX];
char cmd[PATH_MAX];
char exe[PATH_MAX];
char lib[PATH_MAX];
char ver[PATH_MAX];
char key[16];
char* arg[8];
long ops[2];
unsigned long ptime;
struct stat st;
struct stat ps;
if (*proc != '/')
{
if (p = strchr(proc, ' '))
{
strncopy(buf, proc, p - proc + 1);
proc = buf;
}
if (!(proc = pathpath(cmd, proc, NiL, PATH_ABSOLUTE|PATH_REGULAR|PATH_EXECUTE)))
proc = (char*)aproc;
else if (p)
{
n = strlen(proc);
strncopy(proc + n, p, PATH_MAX - n - 1);
}
}
if (!path)
path = buf;
probe = PROBE;
x = lib + sizeof(lib) - 1;
k = lib + sfsprintf(lib, x - lib, "lib/%s/", probe);
p = k + sfsprintf(k, x - k, "%s/%s/", lang, tool);
pathkey(key, attr, lang, tool, proc);
if (op >= -2)
{
strncopy(p, key, x - p);
if (pathpath(path, lib, "", PATH_ABSOLUTE) && !stat(path, &st) && (st.st_mode & S_IWUSR))
return path == buf ? strdup(path) : path;
}
e = strncopy(p, probe, x - p);
if (!pathpath(path, lib, "", PATH_ABSOLUTE|PATH_EXECUTE) || stat(path, &ps))
return 0;
for (;;)
{
ptime = ps.st_mtime;
n = strlen(path);
if (n < (PATH_MAX - 5))
{
strcpy(path + n, ".ini");
if (!stat(path, &st) && st.st_size && ptime < (unsigned long)st.st_mtime)
ptime = st.st_mtime;
path[n] = 0;
}
np = path + n - (e - k);
nx = path + PATH_MAX - 1;
strncopy(np, probe, nx - np);
if (!stat(path, &st))
break;
/*
* yes lib/probe/<lang>/<proc>/probe
* no lib/probe/probe
*
* do a manual pathaccess() to find a dir with both
*/
sfsprintf(exe, sizeof(exe), "lib/%s/%s", probe, probe);
dirs = pathbin();
for (;;)
{
if (!(dir = dirs))
return 0;
dirs = pathcat(path, dir, ':', "..", exe);
pathcanon(path, 0);
if (*path == '/' && pathexists(path, PATH_REGULAR|PATH_EXECUTE))
{
pathcat(path, dir, ':', "..", lib);
pathcanon(path, 0);
if (*path == '/' && pathexists(path, PATH_REGULAR|PATH_EXECUTE) && !stat(path, &ps))
break;
}
}
}
strncopy(p, key, x - p);
p = np;
x = nx;
strcpy(exe, path);
if (op >= -1 && (!(st.st_mode & S_ISUID) && ps.st_uid != geteuid() || rofs(path)))
{
if (!(p = getenv("HOME")))
return 0;
p = path + sfsprintf(path, PATH_MAX - 1, "%s/.%s/%s/", p, probe, HOSTTYPE);
}
strncopy(p, k, x - p);
force = 0;
if (op >= 0 && !stat(path, &st))
{
if (ptime <= (unsigned long)st.st_mtime || ptime <= (unsigned long)st.st_ctime)
{
/*
* verify (<sep><name><sep><option><sep><value>)* header
*/
if (sp = sfopen(NiL, path, "r"))
{
if (x = sfgetr(sp, '\n', 1))
{
while (*x && *x != ' ')
x++;
while (*x == ' ')
x++;
if (n = *x++)
for (;;)
{
for (k = x; *x && *x != n; x++);
if (!*x)
break;
*x++ = 0;
for (p = x; *x && *x != n; x++);
if (!*x)
break;
*x++ = 0;
for (e = x; *x && *x != n; x++);
if (!*x)
break;
*x++ = 0;
if (streq(k, "VERSION"))
{
ap = arg;
*ap++ = proc;
*ap++ = p;
*ap = 0;
ops[0] = PROC_FD_DUP(1, 2, 0);
ops[1] = 0;
if (pp = procopen(proc, arg, NiL, ops, PROC_READ))
{
if ((v = x - e) >= sizeof(ver))
v = sizeof(ver) - 1;
for (k = p = ver;; k++)
{
if (k >= p)
{
if (v <= 0 || (r = read(pp->rfd, k, v)) <= 0)
break;
v -= r;
p = k + r;
}
if (*k == '\n' || *k == '\r')
break;
if (*k == n)
*k = ' ';
}
*k = 0;
if (strcmp(ver, e))
{
force = 1;
error(0, "probe processor %s version \"%s\" changed -- expected \"%s\"", proc, ver, e);
}
procclose(pp);
}
break;
}
}
}
sfclose(sp);
}
if (!force)
op = -1;
}
if (op >= 0 && (st.st_mode & S_IWUSR))
{
if (op == 0)
error(0, "%s probe information for %s language processor %s must be manually regenerated", tool, lang, proc);
op = -1;
force = 0;
}
}
if (op >= 0)
{
ap = arg;
*ap++ = exe;
if (force)
*ap++ = "-f";
if (op > 0)
*ap++ = "-s";
*ap++ = (char*)lang;
*ap++ = (char*)tool;
*ap++ = proc;
*ap = 0;
if (procrun(exe, arg, 0))
return 0;
if (eaccess(path, R_OK))
return 0;
}
return path == buf ? strdup(path) : path;
}