char* pathfind(const char* name, const char* lib, const char* type, char* buf, size_t size) { register Dir_t* dp; register char* s; char tmp[PATH_MAX]; if (access(name, R_OK) >= 0) return strncpy(buf, name, size); if (type) { sfsprintf(buf, size, "%s.%s", name, type); if (access(buf, R_OK) >= 0) return buf; } if (*name != '/') { if (strchr(name, '.')) type = 0; for (dp = state.head; dp; dp = dp->next) { sfsprintf(tmp, sizeof(tmp), "%s/%s", dp->dir, name); if (pathpath(buf, tmp, "", PATH_REGULAR)) return buf; if (type) { sfsprintf(tmp, sizeof(tmp), "%s/%s.%s", dp->dir, name, type); if (pathpath(buf, tmp, "", PATH_REGULAR)) return buf; } } if (lib) { if ((s = strrchr((char*)lib, ':'))) lib = (const char*)s + 1; sfsprintf(tmp, sizeof(tmp), "lib/%s/%s", lib, name); if (pathpath(buf, tmp, "", PATH_REGULAR)) return buf; if (type) { sfsprintf(tmp, sizeof(tmp), "lib/%s/%s.%s", lib, name, type); if (pathpath(buf, tmp, "", PATH_REGULAR)) return buf; } } } return 0; }
int procrun(const char* path, char** argv, int flags) { #if __OBSOLETE__ < 20090101 flags &= argv ? PROC_ARGMOD : PROC_CHECK; #endif if (flags & PROC_CHECK) { char buf[PATH_MAX]; return pathpath(path, NiL, PATH_REGULAR|PATH_EXECUTE, buf, sizeof(buf)) ? 0 : -1; } return procclose(procopen(path, argv, NiL, NiL, flags|PROC_FOREGROUND|PROC_GID|PROC_UID)); }
int b_dirname(int argc, char** argv, Shbltin_t* context) { int mode = 0; char buf[PATH_MAX]; cmdinit(argc, argv, context, ERROR_CATALOG, 0); for (;;) { switch (optget(argv, usage)) { case 'f': mode |= PATH_REGULAR; continue; case 'r': mode &= ~PATH_REGULAR; mode |= PATH_READ; continue; case 'x': mode |= PATH_EXECUTE; continue; case ':': error(2, "%s", opt_info.arg); break; case '?': error(ERROR_usage(2), "%s", opt_info.arg); break; } break; } argv += opt_info.index; argc -= opt_info.index; if(error_info.errors || argc != 1) error(ERROR_usage(2),"%s", optusage(NiL)); if(!mode) l_dirname(sfstdout,argv[0]); else if(pathpath(argv[0], "", mode, buf, sizeof(buf))) sfputr(sfstdout, buf, '\n'); else error(1|ERROR_WARNING, "%s: relative path not found", argv[0]); return 0; }
int main(int argc, char** argv) { register int n; register int i; register long v; char* s; char* e; char* data; int uf; int wf; int idlecmd; int usercount; unsigned long t; unsigned long toss; unsigned long usertime; unsigned long now; unsigned long then; Proc_t* proc; CSSTAT ss; struct stat st; char cmd[PATH_MAX]; char buf[PATH_MAX]; char tmp[PATH_MAX / 4]; char* av[4]; char* iv[3]; #if NAMELIST DIR* root; struct dirent* entry; int kf; #endif NoP(argc); error_info.id = CS_STAT_DAEMON; if (!pathpath(error_info.id, argv[0], PATH_ABSOLUTE|PATH_REGULAR|PATH_EXECUTE, cmd, sizeof(cmd))) error(ERROR_SYSTEM|3, "cannot locate daemon executable"); if (!pathpath(CS_STAT_DIR, argv[0], PATH_EXECUTE, buf, sizeof(buf))) error(3, "%s: cannot locate data directory", CS_STAT_DIR); if (stat(buf, &st)) error(ERROR_SYSTEM|3, "%s: stat error", buf); if (st.st_uid != geteuid()) error(3, "%s: effective uid mismatch", buf); if (chdir(buf)) error(ERROR_SYSTEM|3, "%s: chdir error", buf); data = csname(0); if (argv[1] && strcmp(argv[1], data)) { /* * start remote status daemon */ data = argv[1]; if (!csaddr(data)) error(3, "%s: unknown host", data); if (!stat(data, &st) && (long)(CSTIME() - (unsigned long)st.st_ctime) < CS_STAT_DOWN) exit(0); sfsprintf(buf, sizeof(buf), "./%s", data); csstat(buf, &ss); if (s = csattr(CS_HOST_LOCAL, "type")) { strcpy(tmp, s); if (s = csattr(data, "type")) pathrepl(cmd, sizeof(cmd), tmp, s); } /* * loop until remote status daemon starts * check for competing startup daemon */ if (csdaemon(0)) exit(1); umask(S_IRWXU|S_IRWXG|S_IRWXO); av[0] = CS_REMOTE_SHELL; av[1] = data; av[2] = cmd; av[3] = 0; for (;;) { update(data, 0, 0, &ss); if (!(remote = procopen(av[0], av, NiL, NiL, PROC_UID|PROC_GID))) break; while (!kill(remote->pid, 0)) update(data, 0, CS_STAT_FREQ + (CS_STAT_DOWN - CS_STAT_FREQ) / 2, &ss); procclose(remote); remote = 0; if (ss.up > 0) ss.up = -ss.up; } for (;;) update(data, 0, CS_STAT_FREQ + (CS_STAT_DOWN - CS_STAT_FREQ) / 2, &ss); } remove(data); if ((n = open(data, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0)) < 0) error(ERROR_SYSTEM|3, "%s: cannot update", data); for (i = 0; i < elementsof(usrfiles); i++) if ((uf = open(usrfile = usrfiles[i], O_RDONLY)) >= 0) break; if (uf < 0) error(ERROR_SYSTEM|3, "%s: cannot read", usrfiles[0]); /* * final initialization */ if (csdaemon((1<<2)|(1<<n)|(1<<uf))) error(ERROR_SYSTEM|3, "cannot dive into background"); umask(S_IRWXU|S_IRWXG|S_IRWXO); close(2); dup(n); close(n); error_info.id = data; av[0] = "uptime"; av[1] = 0; toss = getpid(); for (s = data; *s; s++) CSTOSS(toss, *s); usertime = 0; #if NAMELIST for (n = 0; n < elementsof(symbols); n++) names[n].n_name = symbols[n].name; if ((kf = open(memfile, O_RDONLY)) >= 0) { if (chdir("/")) error(ERROR_SYSTEM|3, "/: chdir error"); s = 0; for (i = 0; i < elementsof(sysfiles); i++) if (!access(sysfiles[i], F_OK)) { s = sysfiles[i]; break; } if (!s) { if (!(root = opendir("."))) error(ERROR_SYSTEM|3, "/: cannot read"); while (entry = readdir(root)) { if ((i = strlen(entry->d_name) - 2) > 0 && entry->d_name[i] == 'i' && entry->d_name[i + 1] == 'x' && !stat(entry->d_name, &st) && (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) { s = entry->d_name; break; } } closedir(root); } nlist(s, names); for (n = 0; n < elementsof(symbols); n++) if (!names[n].n_type) { error(1, "%s: %s not in nlist", s, names[n].n_name); close(kf); kf = -1; } if (chdir(buf)) error(ERROR_SYSTEM|3, "%s: chdir error", buf); } if (kf < 0) #endif { sfsprintf(buf, sizeof(buf), "%s/%s%s%s", WHODIR, WHOPRE, data, WHOSUF); if ((wf = open(buf, O_RDONLY)) >= 0) { if (read(wf, &who, sizeof(who)) != sizeof(who) || who.wd_vers != WHOVERS || who.wd_type != WHOTYPE) { error(1, "%s: rwhod protocol mismatch", buf); close(wf); wf = -1; } else whofile = strdup(buf); } } strcpy(cmd + strlen(cmd), ".idle"); if (eaccess(cmd, X_OK)) idlecmd = 0; else { idlecmd = 1; iv[0] = cmd; iv[1] = data; iv[2] = 0; } /* * the daemon loop */ ss.idle = 4 * 60 * 60; now = CSTIME(); for (;;) { then = now; now = CSTIME(); /* * update logged in user stats */ if (fstat(uf, &st)) error(ERROR_SYSTEM|3, "%s: stat error", usrfile); if (usertime != (unsigned long)st.st_mtime) { usertime = st.st_mtime; if (lseek(uf, 0L, 0)) error(ERROR_SYSTEM|3, "%s: seek error", usrfile); if ((n = read(uf, usrs, sizeof(usrs))) < 0) error(ERROR_SYSTEM|3, "%s: read error", usrfile); usercount = n / sizeof(struct utmp); } /* * count the interesting users * find the min user idle time */ if (idlecmd) { /* * check idle command */ if (!(proc = procopen(iv[0], iv, NiL, NiL, PROC_READ|PROC_UID|PROC_GID))) idlecmd = 0; else { idlecmd = 1; n = read(proc->rfd, buf, sizeof(buf)); if (procclose(proc) || n < 0) idlecmd = 0; else { if (n > 0) n--; buf[n] = 0; if (isdigit(buf[0])) ss.idle = strtol(buf, NiL, 10); else if (streq(buf, "busy")) ss.idle = 0; else if (streq(buf, "free")) ss.idle = ~0; else if (streq(buf, "idle")) { n = since(then); if ((ss.idle + n) < ss.idle) ss.idle = ~0; else ss.idle += n; } else idlecmd = -1; } } } if (idlecmd <= 0) ss.idle = ~0; ss.users = 0; for (i = 0; i < usercount; i++) if (usrs[i].ut_name[0] && usrs[i].ut_line[0]) { sfsprintf(buf, sizeof(buf), "/dev/%s", usrs[i].ut_line); if (stat(buf, &st)) usrs[i].ut_name[0] = 0; else { v = since(st.st_atime); if (v < CS_STAT_IGNORE) ss.users++; if (idlecmd <= 0 && v < ss.idle) ss.idle = v; } } if (idlecmd <= 0 || !ss.users) { /* * check devices for min idle time */ for (i = 0; i < elementsof(devfiles); i++) if (devfiles[i]) { if (stat(devfiles[i], &st)) devfiles[i] = 0; else { v = since(st.st_atime); if (!ss.users && v < CS_STAT_IGNORE) ss.users++; if (idlecmd <= 0 && v < ss.idle) ss.idle = v; } } } /* * get the hard stuff */ #if NAMELIST if (kf >= 0) { /* * update memfile symbol values */ for (n = 0; n < elementsof(symbols); n++) if (symbols[n].once >= 0) { if (lseek(kf, (long)names[n].n_value, 0) != (long)names[n].n_value) error(ERROR_SYSTEM|3, "%s: %s seek error", memfile, names[n].n_name); if (read(kf, symbols[n].addr, symbols[n].size) != symbols[n].size) error(ERROR_SYSTEM|3, "%s: %s read error", memfile, names[n].n_name); if (symbols[n].once) symbols[n].once = -1; } #ifdef CP_TIME for (i = 0; i < CPUSTATES; i++) cp_time[i] = 0; for (n = 0; n <= maxcpu; n++) if (CPUFOUND(n)) for (i = 0; i < CPUSTATES; i++) cp_time[i] += CP_TIME(n)[i]; #endif ss.load = (avenrun * 100) / FSCALE; } else #endif if (wf >= 0) { if (lseek(wf, 0L, 0)) error(ERROR_SYSTEM|3, "%s: seek error", whofile); read(wf, &who, sizeof(who)); ss.load = who.wd_loadav[0]; boottime = who.wd_boottime; for (i = 0; i < elementsof(cp_time); i++) cp_time[i] = 100; } else if (!(proc = procopen(av[0], av, NiL, NiL, PROC_READ|PROC_UID|PROC_GID))) error(ERROR_SYSTEM|3, "%s: exec error", av[0]); else { /* * defer to process with memfile access */ n = read(proc->rfd, buf, sizeof(buf) - 1); if (procclose(proc) || n <= 0) error(3, "%s: invalid", av[0]); buf[n] = 0; if (!(s = strrchr(buf, ':'))) error(3, "%s: invalid output", av[0]); ss.load = strton(s + 1, NiL, NiL, 100); n = 0; if ((s = strchr(buf, 'u')) && *++s == 'p') { n = strtol(s + 1, &e, 10) * 60 * 60; s = e; while (isspace(*s)) s++; if (*s == 'd') { n *= 24; while (*s && !isdigit(*s)) s++; n += strtol(s, &e, 10) * 60 * 60; s = e; } if (*s == ':') n += strtol(s + 1, NiL, 10) * 60; } boottime = since(n); for (i = 0; i < elementsof(cp_time); i++) cp_time[i] = 0; } /* * finalize the new stat info */ t = 0; for (i = 0; i < elementsof(cp_time); i++) { if ((cp_diff[i] = cp_time[i] - cp_prev[i]) < 0) cp_diff[i] = -cp_diff[i]; t += cp_diff[i]; cp_prev[i] = cp_time[i]; } if (!t) t = 1; ss.pctsys = (cp_diff[CP_SYS] * 100) / t; ss.pctusr = ((cp_diff[CP_USER] + cp_diff[CP_NICE]) * 100) / t; ss.up = since(boottime); update(data, now, (4 * CS_STAT_FREQ + 2 * (CSTOSS(toss, 0) % (CS_STAT_FREQ + 1))) / 5, &ss); } }
int csopen(register Cs_t* state, const char* apath, int op) { register char* path = (char*)apath; register char* b; register char* s; register int n; int fd; char* t; char* u; char* type; char* endtype; char* host; char* endhost; char* serv; char* endserv; char* qual; char* endqual; char* opath; char* user = 0; char* group = 0; char* trust = 0; char* arg = 0; int nfd = -1; int uid = -1; int gid = -1; int sid = -1; int auth = 1; int mode; unsigned long addr; unsigned long port = 0; struct stat st; char buf[PATH_MAX]; char tmp[PATH_MAX]; if (!path) { errno = EFAULT; return -1; } csprotect(&cs); if (op < 0) op = CS_OPEN_TEST; messagef((state->id, NiL, -8, "open(%s,%o) call", path, op)); /* * blast out the parts */ opath = path; if (pathgetlink(path, buf, sizeof(buf)) <= 0) { if (strlen(path) >= sizeof(buf)) return -1; strcpy(buf, path); } else if ((state->flags & CS_ADDR_LOCAL) && (s = strrchr(buf, '/'))) { /* * dynamic ip assignment can change the addr * underfoot in some implementations so we * double check the local ip here */ strcpy(tmp, buf); if (tokscan(tmp, NiL, "/dev/%s/%s/%s", &type, NiL, &serv) == 3) sfsprintf(buf, sizeof(buf), "/dev/%s/%s/%s", type, csntoa(state, 0), serv); } path = buf; pathcanon(path, 0, 0); errno = ENOENT; strcpy(state->path, path); b = path; if ((*b++ != '/') || !(s = strchr(b, '/'))) return -1; *s++ = 0; if (!streq(b, "dev")) return -1; if (b = strchr(s, '/')) *b++ = 0; if (streq(s, "fdp")) { #if !( CS_LIB_SOCKET_UN || CS_LIB_STREAM || CS_LIB_V10 ) if (access(CS_PROC_FD_TST, F_OK)) { errno = ENODEV; messagef((state->id, NiL, -1, "open: %s: %s: not supported", state->path, s)); return -1; } #endif } else if (!streq(s, "tcp") && !streq(s, "udp")) { messagef((state->id, NiL, -1, "open: %s: %s: invalid type", state->path, s)); return -1; } #if !( CS_LIB_SOCKET || CS_LIB_STREAM || CS_LIB_V10 ) else { errno = ENODEV; messagef((state->id, NiL, -1, "open: %s: %s: not supported", state->path, s)); return -1; } #endif type = s; qual = state->qual; if (!b) host = serv = 0; else { host = b; if (!(s = strchr(b, '/'))) serv = 0; else { *s++ = 0; serv = s; /* * grab the next fd to preserve open semantics */ for (n = 0; n < 10; n++) if ((nfd = dup(n)) >= 0) break; /* * get qual, perm and arg */ mode = S_IRWXU|S_IRWXG|S_IRWXO; if (b = strchr(s, '/')) { *b++ = 0; do { if (*b == '#') { arg = b + 1; break; } if (u = strchr(b, '/')) *u++ = 0; if (s = strchr(b, '=')) *s++ = 0; for (n = 0, t = b; *t; n = HASHKEYPART(n, *t++)); switch (n) { case HASHKEY5('g','r','o','u','p'): group = s ? s : ""; break; case HASHKEY5('l','o','c','a','l'): op |= CS_OPEN_LOCAL; break; case HASHKEY3('n','o','w'): op |= CS_OPEN_NOW; break; case HASHKEY5('o','t','h','e','r'): auth = 0; break; case HASHKEY6('r','e','m','o','t','e'): op |= CS_OPEN_REMOTE; break; case HASHKEY5('s','h','a','r','e'): op |= CS_OPEN_SHARE; break; case HASHKEY5('s','l','a','v','e'): op |= CS_OPEN_SLAVE; break; case HASHKEY4('t','e','s','t'): op |= CS_OPEN_TEST; break; case HASHKEY5('t','r','u','s','t'): op |= CS_OPEN_TRUST; trust = s; break; case HASHKEY4('u','s','e','r'): user = s ? s : ""; break; default: qual += sfsprintf(qual, sizeof(state->qual) - (qual - state->qual) - 1, "%s%s", qual == state->qual ? "" : "-", b); if (s) *(s - 1) = '='; break; } } while (b = u); } } } if (*type != 't') auth = 0; strncpy(state->type, type, sizeof(state->type) - 1); qual = (qual == state->qual) ? (char*)0 : state->qual; messagef((state->id, NiL, -8, "open: type=%s host=%s serv=%s qual=%s", type, host, serv, qual)); if (host) { /* * validate host */ if (!(state->addr = addr = csaddr(state, host))) { if (serv && !(op & CS_OPEN_CREATE) && *type == 't' && (port = csport(state, type, serv)) >= CS_PORT_MIN && port <= CS_PORT_MAX) { /* * attempt proxy connection */ if (nfd >= 0) { close(nfd); nfd = -1; } if ((fd = state->proxy.addr ? csbind(state, type, state->proxy.addr, state->proxy.port, 0L) : reopen(state, csvar(state, CS_VAR_PROXY, 0))) >= 0) { state->proxy.addr = state->addr; state->proxy.port = state->port; n = sfsprintf(tmp, sizeof(tmp), "\n%s!%s!%d\n\n%s\n%s\n0\n-1\n-1\n", type, host, port, csname(state, 0), error_info.id ? error_info.id : state->id); if (cswrite(state, fd, tmp, n) == n && (n = csread(state, fd, tmp, sizeof(tmp), CS_LINE)) >= 2) { if (tmp[0] == '0' && tmp[1] == '\n') return fd; if (error_info.trace <= -4 && n > 2) { s = tmp; s[n - 1] = 0; while (*s && *s++ != '\n'); messagef((state->id, NiL, -4, "%s error message `%s'", csvar(state, CS_VAR_PROXY, 0), s)); } } close(fd); } } #ifdef EADDRNOTAVAIL errno = EADDRNOTAVAIL; #else errno = ENOENT; #endif goto bad; } if (op & CS_OPEN_LOCAL) { state->flags |= CS_ADDR_LOCAL; state->flags &= ~CS_ADDR_REMOTE; } if (op & CS_OPEN_NOW) state->flags |= CS_ADDR_NOW; if ((op & (CS_OPEN_AGENT|CS_OPEN_REMOTE)) == CS_OPEN_REMOTE) { state->flags |= CS_ADDR_REMOTE; state->flags &= ~CS_ADDR_LOCAL; } if (op & CS_OPEN_SHARE) state->flags |= CS_ADDR_SHARE; if (op & CS_OPEN_SLAVE) state->flags |= CS_DAEMON_SLAVE; if (op & CS_OPEN_TEST) state->flags |= CS_ADDR_TEST; if (op & CS_OPEN_TRUST) state->flags |= CS_ADDR_TRUST; if ((state->flags & CS_ADDR_REMOTE) && (!serv || !strneq(serv, CS_SVC_INET, sizeof(CS_SVC_INET) - 1) && (strtol(serv, &t, 0), *t))) return agent(state, state->host, state->user, state->path); if (s = user) { n = geteuid(); if (*s) { if ((uid = struid(s)) < 0) { uid = strtol(s, &t, 0); if (*t) { errno = EACCES; goto bad; } } if (n && uid != n) { errno = EACCES; goto bad; } } else uid = n; mode &= ~(S_IRWXG|S_IRWXO); } if (s = group) { n = getegid(); if (*s) { if ((gid = strgid(s)) < 0) { gid = strtol(s, &t, 0); if (*t) { errno = EACCES; goto bad; } } if (geteuid() && gid != n) { gid_t* groups; int g; if ((g = getgroups(0, NiL)) <= 0) g = getconf("NGROUPS_MAX"); if (groups = newof(0, gid_t, g, 0)) { for (n = getgroups(g, groups); n >= 0; n--) if (gid == groups[n]) break; free(groups); } else n = -1; if (n < 0) { errno = EACCES; goto bad; } } } else gid = n; mode &= ~S_IRWXO; } if (s = trust) { if (!*s) sid = geteuid(); else if ((sid = struid(s)) < 0) { sid = strtol(s, &t, 0); if (*t) { errno = EACCES; goto bad; } } } if (state->flags & CS_ADDR_SHARE) host = CS_HOST_SHARE; else { host = state->host; if (!(state->flags & CS_ADDR_LOCAL)) { if (*type == 'f') { errno = ENODEV; goto bad; } if (op & CS_OPEN_CREATE) { errno = EROFS; goto bad; } } if (serv && !qual && *type != 'f' && (port = csport(state, type, serv)) != CS_PORT_INVALID) { if (op & CS_OPEN_CREATE) addr = 0; else if (port == CS_PORT_RESERVED || port == CS_PORT_NORMAL) goto bad; if (nfd >= 0) { close(nfd); nfd = -1; } state->control = 0; if ((fd = csbind(state, type, addr, port, 0L)) >= 0) { if (mode != (S_IRWXU|S_IRWXG|S_IRWXO) && csauth(state, fd, NiL, NiL)) { close(fd); return -1; } return fd; } } } } /* * get the mount dir prefix */ if (opath == (b = path = state->mount)) { #ifdef ELOOP errno = ELOOP; #else errno = EINVAL; #endif goto bad; } if (*type == 'f') { if (host && !(state->flags & CS_ADDR_LOCAL)) { errno = ENODEV; goto bad; } b += sfsprintf(b, sizeof(state->mount) - (b - path), "%s", csvar(state, CS_VAR_LOCAL, 0)); if ((op & CS_OPEN_CREATE) && eaccess(path, X_OK) && (mkdir(path, S_IRWXU|S_IRWXG|S_IRWXO) || chmod(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO))) goto bad; } else { if (op & CS_OPEN_TRUST) { if (!pathaccess(csvar(state, CS_VAR_TRUST, 1), csvar(state, CS_VAR_SHARE, 1), NiL, PATH_EXECUTE, b, sizeof(state->mount) - (b - state->mount))) goto bad; } else if (!pathpath(csvar(state, CS_VAR_SHARE, 0), "", PATH_EXECUTE, b, sizeof(state->mount) - (b - state->mount))) goto bad; b += strlen(b); } /* * add the type */ b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", type); if (!host) { *(state->control = b + 1) = 0; if (nfd >= 0) close(nfd); if ((fd = open(path, O_RDONLY)) < 0) { mkmount(state, S_IRWXU|S_IRWXG|S_IRWXO, -1, -1, NiL, NiL, NiL); fd = open(path, O_RDONLY); } if (fd < 0) messagef((state->id, NiL, -1, "open: %s: %s: open error", state->path, path)); return fd; } endtype = b; /* * add the host */ if (strlen(host) <= CS_MNT_MAX) b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", host); else { s = csntoa(state, addr); if (strlen(s) <= CS_MNT_MAX) b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", s); else { unsigned char* a = (unsigned char*)&addr; b += sfsprintf(b, sizeof(state->mount) - (b - path), "/0x%X.%X.%X.%X", a[0], a[1], a[2], a[3]); } } messagef((state->id, NiL, -8, "%s:%d host=`%s' path=`%s'", __FILE__, __LINE__, host, path)); if (!serv) { *(state->control = b + 1) = 0; if (nfd >= 0) close(nfd); if ((fd = open(path, O_RDONLY)) < 0) messagef((state->id, NiL, -1, "open: %s: %s: open error", state->path, path)); return fd; } endhost = b; /* * add the service */ sfsprintf(b, sizeof(state->mount) - (b - path), "%s/%s/%s/%s%s", CS_SVC_DIR, type, serv, serv, CS_SVC_SUFFIX); if (!pathpath(b, "", PATH_ABSOLUTE|PATH_EXECUTE, tmp, sizeof(tmp)) || stat(tmp, &st)) op |= CS_OPEN_TEST; else { *strrchr(tmp, '/') = 0; if (!(op & CS_OPEN_TRUST)) sid = st.st_uid; if (!st.st_size) op |= CS_OPEN_TEST; } b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", serv); endserv = b; /* * add the qualifier and perm */ if (sid >= 0) b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%d-", sid); else b += sfsprintf(b, sizeof(state->mount) - (b - path), "/-"); if (uid >= 0) b += sfsprintf(b, sizeof(state->mount) - (b - path), "%d-", uid); else if (gid >= 0) b += sfsprintf(b, sizeof(state->mount) - (b - path), "-%d", gid); else b += sfsprintf(b, sizeof(state->mount) - (b - path), "-"); #if limit_qualifier_length endqual = endserv + CS_MNT_MAX + 1; #else endqual = state->mount + sizeof(state->mount) - 1; #endif if (qual) { if (b < endqual) *b++ = '-'; while (b < endqual && *qual) *b++ = *qual++; } if (*type == 't' && !auth) { if (b >= endqual) b--; *b++ = CS_MNT_OTHER; } /* * add in the connect stream control */ *b++ = '/'; *b = CS_MNT_STREAM; strcpy(b + 1, CS_MNT_TAIL); messagef((state->id, NiL, -8, "%s:%d %s", __FILE__, __LINE__, state->mount)); state->control = b; /* * create the mount subdirs if necessary */ if ((op & CS_OPEN_CREATE) && mkmount(state, mode, uid, gid, endserv, endhost, endtype)) goto bad; mode &= S_IRWXU|S_IRWXG|S_IRWXO; if (nfd >= 0) { close(nfd); nfd = -1; } if (op & CS_OPEN_MOUNT) { messagef((state->id, NiL, -1, "open(%s,%o) = %d, mount = %s", state->path, op, state->mount)); return 0; } if (*type == 'f') { /* * {fdp} */ if ((fd = doattach(state, path, op, mode, user, opath, tmp, serv, b)) < 0) return -1; } else { /* * {tcp,udp} */ messagef((state->id, NiL, -8, "%s:%d %s", __FILE__, __LINE__, state->mount)); if ((fd = reopen(state, path)) < 0) { /* * check for old single char cs mount */ *(state->control + 1) = 0; if ((fd = reopen(state, path)) < 0) messagef((state->id, NiL, -1, "open: %s: %s: reopen error", state->path, path)); *(state->control + 1) = CS_MNT_TAIL[0]; } if (op & CS_OPEN_CREATE) { if (fd >= 0) { close(fd); errno = EEXIST; return -1; } if (errno != ENOENT && errno != ENOTDIR) return -1; sigcritical(1); *state->control = CS_MNT_LOCK; if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0)) < 0) { if (stat(path, &st)) { messagef((state->id, NiL, -1, "open: %s: %s: creat error", state->path, path)); goto unblock; } if ((CSTIME() - (unsigned long)st.st_ctime) < 2 * 60) { errno = EEXIST; messagef((state->id, NiL, -1, "open: %s: %s: another server won the race", state->path, path)); goto unblock; } if (remove(path)) { messagef((state->id, NiL, -1, "open: %s: %s: remove error", state->path, path)); goto unblock; } if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0)) < 0) { messagef((state->id, NiL, -1, "open: %s: %s: creat error", state->path, path)); goto unblock; } } close(fd); if (!port && (n = strtol(serv, &t, 0)) && t > serv && !*t) port = n; else if (geteuid()) port = CS_NORMAL; else port = CS_RESERVED; if ((fd = csbind(state, type, 0L, port, 0L)) >= 0) { *state->control = CS_MNT_STREAM; remove(path); if (pathsetlink(cspath(state, fd, 0), path)) { messagef((state->id, NiL, -1, "open: %s: %s: link error", cspath(state, fd, 0), path)); close(fd); fd = -1; } } unblock: *state->control = CS_MNT_LOCK; remove(path); sigcritical(0); *state->control = CS_MNT_STREAM; if (fd < 0) return -1; } else if (fd < 0 && ((op & CS_OPEN_TEST) || initiate(state, user, opath, tmp, serv) || (fd = reopen(state, path)) < 0)) { messagef((state->id, NiL, -1, "open: %s: %s: reopen/initiate error", state->path, path)); return -1; } else if (!(op & CS_OPEN_AGENT)) { *state->control = CS_MNT_AUTH; n = csauth(state, fd, path, arg); *state->control = CS_MNT_STREAM; if (n) { close(fd); messagef((state->id, NiL, -1, "open: %s: %s: authentication error", state->path, path)); return -1; } } } /* * fd is open at this point * make sure its not a bogus mount */ if (mode != (S_IRWXU|S_IRWXG|S_IRWXO)) { *state->control = 0; n = stat(path, &st); *state->control = CS_MNT_STREAM; if (n) { messagef((state->id, NiL, -1, "open: %s: %s: stat error", state->path, path)); close(fd); return -1; } if (uid >= 0 && st.st_uid != uid || gid >= 0 && st.st_gid != gid) { close(fd); errno = EPERM; messagef((state->id, NiL, -1, "open: %s: %s: uid/gid error", state->path, path)); return -1; } } return fd; bad: if (nfd >= 0) close(nfd); return -1; }
int main(int argc, char** argv) { register char* s; register Rule_t* r; register List_t* p; int i; int args; int trace; char* t; char* buf; char* tok; Var_t* v; Stat_t st; Stat_t ds; Sfio_t* tmp; /* * initialize dynamic globals */ version = strdup(fmtident(version)); setlocale(LC_ALL, ""); error_info.id = idname; error_info.version = version; error_info.exit = finish; error_info.auxilliary = intercept; if (pathcheck(PATHCHECK, error_info.id, NiL)) return 1; error(-99, "startup"); settypes("*?[]", C_MATCH); settypes("+-|=", C_OPTVAL); settypes(" \t\n", C_SEP); settype(0, C_SEP); settypes(" \t\v\n:+&=;\"\\", C_TERMINAL); settype(0, C_TERMINAL); settypes("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", C_ID1|C_ID2|C_VARIABLE1|C_VARIABLE2); settypes(".", C_VARIABLE1|C_VARIABLE2); settypes("0123456789", C_ID2|C_VARIABLE2); /* * close garbage fd's -- we'll be tidy from this point on * 3 may be /dev/tty on some systems * 0..9 for user redirection in shell * 10..19 left open by bugs in some shells * error_info.fd interited from parent * any close-on-exec fd's must have been done on our behalf */ i = 3; if (isatty(i)) i++; for (; i < 20; i++) if (i != error_info.fd && !fcntl(i, F_GETFD, 0)) close(i); /* * allocate the very temporary buffer streams */ internal.met = sfstropen(); internal.nam = sfstropen(); internal.tmp = sfstropen(); internal.val = sfstropen(); internal.wrk = sfstropen(); tmp = sfstropen(); sfstrrsrv(tmp, 2 * MAXNAME); /* * initialize the code and hash tables */ initcode(); inithash(); /* * set the default state */ state.alias = 1; state.exec = 1; state.global = 1; state.init = 1; #if DEBUG state.intermediate = 1; #endif state.io[0] = sfstdin; state.io[1] = sfstdout; state.io[2] = sfstderr; state.jobs = 1; state.pid = getpid(); state.readstate = MAXVIEW; state.scan = 1; state.start = CURTIME; state.stateview = -1; state.tabstops = 8; state.targetview = -1; #if BINDINDEX state.view[0].path = makerule("."); #else state.view[0].path = "."; #endif state.view[0].pathlen = 1; state.writeobject = state.writestate = "-"; /* * pwd initialization * * for project management, if . is group|other writeable * then change umask() for similar write protections */ buf = sfstrbase(tmp); internal.pwd = (s = getcwd(buf, MAXNAME)) ? strdup(s) : strdup("."); internal.pwdlen = strlen(internal.pwd); if (stat(".", &st)) error(3, "cannot stat ."); if (S_ISDIR(st.st_mode) && (st.st_mode & (S_IWGRP|S_IWOTH))) umask(umask(0) & ~(st.st_mode & (S_IWGRP|S_IWOTH))); /* * set some variable default values */ hashclear(table.var, HASH_ALLOCATE); setvar(external.make, argv[0], V_import); t = "lib/make"; setvar(external.lib, strdup((s = pathpath(t, argv[0], PATH_EXECUTE, buf, SF_BUFSIZE)) ? s : t), V_import); setvar(external.pwd, internal.pwd, V_import); setvar(external.version, version, V_import); hashset(table.var, HASH_ALLOCATE); /* * read the environment */ readenv(); if (v = getvar(external.nproc)) state.jobs = (int)strtol(v->value, NiL, 0); if ((v = getvar(external.pwd)) && !streq(v->value, internal.pwd)) { if (!stat(v->value, &st) && !stat(internal.pwd, &ds) && st.st_ino == ds.st_ino && st.st_dev == ds.st_dev) { free(internal.pwd); internal.pwd = strdup(v->value); internal.pwdlen = strlen(v->value); } else { v->property &= ~V_import; v->property |= V_free; v->value = strdup(internal.pwd); } } /* * initialize the internal rule pointers */ initrule(); /* * read the static initialization script */ sfputr(tmp, initstatic, -1); parse(NiL, sfstruse(tmp), "initstatic", NiL); /* * check and read the args file */ if (s = colonlist(tmp, external.args, 1, ' ')) { i = fs3d(0); tok = tokopen(s, 1); while (s = tokread(tok)) if (vecargs(vecfile(s), &argc, &argv) >= 0) break; else if (errno != ENOENT) error(1, "cannot read args file %s", s); tokclose(tok); fs3d(i); } state.argf = newof(0, int, argc, 0); /* * set the command line options * read the command line assignments * mark the command line scripts and targets */ state.init = 0; state.readonly = 1; state.argv = argv; state.argc = argc; if ((args = scanargs(state.argc, state.argv, state.argf)) < 0) return 1; state.readonly = 0; state.init = 1; if (state.base) state.readstate = 0; if (state.compileonly) { state.forceread = 1; state.virtualdot = 0; } /* * tone down the bootstrap noise */ if ((trace = error_info.trace) == -1) error_info.trace = 0; /* * check explicit environment overrides */ if (s = colonlist(tmp, external.import, 1, ' ')) { tok = tokopen(s, 1); while (s = tokread(tok)) { if (i = *s == '!') s++; if (v = getvar(s)) { if (i) v->property &= ~V_import; else v->property |= V_readonly; } } tokclose(tok); } /* * set up the traps */ inittrap(); /* * announce the version */ if (error_info.trace < 0) { errno = 0; error(error_info.trace, "%s [%d %s]", version, state.pid, timestr(state.start)); } /* * initialize the views */ state.global = 0; state.user = 1; initview(); /* * check for mam */ if (state.mam.out) { if (!state.mam.statix || *state.mam.label) error_info.write = mamerror; if (state.mam.regress || state.regress) { sfprintf(state.mam.out, "%sinfo mam %s %05d\n", state.mam.label, state.mam.type, state.mam.parent); if (state.mam.regress) sfprintf(state.mam.out, "%sinfo start regression\n", state.mam.label); } else { sfprintf(state.mam.out, "%sinfo mam %s %05d 1994-07-17 %s\n", state.mam.label, state.mam.type, state.mam.parent, version); if (!state.mam.statix || *state.mam.label) { sfprintf(state.mam.out, "%sinfo start %lu\n", state.mam.label, CURTIME); if (!state.mam.root || streq(state.mam.root, internal.pwd)) sfprintf(state.mam.out, "%sinfo pwd %s\n", state.mam.label, internal.pwd); else sfprintf(state.mam.out, "%sinfo pwd %s %s\n", state.mam.label, state.mam.root, mamname(makerule(internal.pwd))); buf = sfstrbase(tmp); if (state.fsview && !mount(NiL, buf, FS3D_GET|FS3D_ALL|FS3D_SIZE(sfstrsize(tmp)), NiL)) sfprintf(state.mam.out, "%sinfo view %s\n", state.mam.label, buf); } } } /* * read the dynamic initialization script */ if ((i = error_info.trace) > -20) error_info.trace = 0; sfputr(tmp, initdynamic, -1); parse(NiL, sfstruse(tmp), "initdynamic", NiL); error_info.trace = i; state.user = 0; state.init = 0; /* * read the explicit makefiles * readfile() handles the base and global rules * * NOTE: internal.tmplist is used to handle the effects of * load() on internal list pointers */ compref(NiL, 0); if (p = internal.makefiles->prereqs) { p = internal.tmplist->prereqs = listcopy(p); for (; p; p = p->next) readfile(p->rule->name, COMP_FILE, NiL); freelist(internal.tmplist->prereqs); internal.tmplist->prereqs = 0; } /* * if no explicit makefiles then try external.{convert,files} */ if (!state.makefile) { int sep; Sfio_t* exp; Sfio_t* imp; exp = 0; imp = sfstropen(); sep = 0; s = 0; if (*(t = getval(external.convert, VAL_PRIMARY))) { sfputr(tmp, t, 0); sfstrrsrv(tmp, MAXNAME); tok = tokopen(sfstrbase(tmp), 0); while (s = tokread(tok)) { if (!exp) exp = sfstropen(); if (t = colonlist(exp, s, 0, ' ')) { t = tokopen(t, 0); while (s = tokread(t)) { if (readfile(s, COMP_INCLUDE|COMP_DONTCARE, NiL)) break; if (sep) sfputc(imp, ','); else sep = 1; sfputr(imp, s, -1); } tokclose(t); if (s) break; } if (!(s = tokread(tok))) break; } tokclose(tok); sfstrseek(tmp, 0, SEEK_SET); } if (!s && (s = colonlist(tmp, external.files, 1, ' '))) { tok = tokopen(s, 1); while (s = tokread(tok)) { if (readfile(s, COMP_INCLUDE|COMP_DONTCARE, NiL)) break; if (sep) sfputc(imp, ','); else sep = 1; sfputr(imp, s, -1); } tokclose(tok); } if (!s) { /* * this readfile() pulls in the default base rules * that might resolve any delayed self-documenting * options in optcheck() */ if (readfile("-", COMP_FILE, NiL)) optcheck(1); if (*(s = sfstruse(imp))) error(state.errorid ? 1 : 3, "a makefile must be specified when %s omitted", s); else error(state.errorid ? 1 : 3, "a makefile must be specified"); } sfstrclose(imp); if (exp) sfstrclose(exp); } /* * validate external command line options */ optcheck(1); /* * check for listing of variable and rule definitions */ if (state.list) { dump(sfstdout, 0); return 0; } /* * check if makefiles to be compiled */ if (state.compile && !state.virtualdot && state.writeobject) { /* * make the compinit trap */ if (r = getrule(external.compinit)) { state.reading = 1; maketop(r, P_dontcare|P_foreground, NiL); state.reading = 0; } if (state.exec && state.objectfile) { message((-2, "compiling makefile input into %s", state.objectfile)); compile(state.objectfile, NiL); } /* * make the compdone trap */ if (r = getrule(external.compdone)) { state.reading = 1; maketop(r, P_dontcare|P_foreground, NiL); state.reading = 0; } } /* * makefile read cleanup */ if (state.compileonly) return 0; compref(NiL, 0); sfstrclose(tmp); state.compile = COMPILED; if (state.believe) { if (!state.maxview) state.believe = 0; else if (state.fsview) error(3, "%s: option currently works in 2d only", optflag(OPT_believe)->name); } /* * read the state file */ readstate(); /* * place the command line targets in internal.args */ if (internal.main->dynamic & D_dynamic) dynamic(internal.main); internal.args->prereqs = p = 0; for (i = args; i < state.argc; i++) if (state.argf[i] & ARG_TARGET) { List_t* q; q = cons(makerule(state.argv[i]), NiL); if (p) p = p->next = q; else internal.args->prereqs = p = q; } /* * the engine bootstrap is complete -- start user activities */ state.user = 1; /* * make the makeinit trap */ if (r = getrule(external.makeinit)) maketop(r, P_dontcare|P_foreground, NiL); /* * read the command line scripts */ for (i = args; i < state.argc; i++) if (state.argf[i] & ARG_SCRIPT) { state.reading = 1; parse(NiL, state.argv[i], "command line script", NiL); state.reading = 0; } /* * freeze the parameter files and candidate state variables */ state.user = 2; candidates(); /* * make the init trap */ if (r = getrule(external.init)) maketop(r, P_dontcare|P_foreground, NiL); /* * internal.args default to internal.main */ if (!internal.args->prereqs && internal.main->prereqs) internal.args->prereqs = listcopy(internal.main->prereqs); /* * turn up the volume again */ if (!error_info.trace) error_info.trace = trace; /* * make the prerequisites of internal.args */ if (internal.args->prereqs) while (internal.args->prereqs) { /* * we explicitly allow internal.args modifications */ r = internal.args->prereqs->rule; internal.making->prereqs = internal.args->prereqs; internal.args->prereqs = internal.args->prereqs->next; internal.making->prereqs->next = 0; maketop(r, 0, NiL); } else if (state.makefile) error(3, "%s: a main target must be specified", state.makefile); /* * finish up */ finish(0); return 0; }
char* pathfind(const char* name, const char* lib, const char* type, char* buf, size_t size) { register Dir_t* dp; register char* s; char tmp[PATH_MAX]; struct stat st; if (((s = strrchr(name, '/')) || (s = (char*)name)) && strchr(s, '.')) type = 0; /* * always check the unadorned path first * this handles . and absolute paths */ if (regular(name, &st)) { strncopy(buf, name, size); return buf; } if (type) { sfsprintf(buf, size, "%s.%s", name, type); if (regular(buf, &st)) return buf; } if (*name == '/') return 0; /* * check the directory of the including file * on the assumption that error_info.file is properly stacked */ if (error_info.file && (s = strrchr(error_info.file, '/'))) { sfsprintf(buf, size, "%-.*s%s", s - error_info.file + 1, error_info.file, name); if (regular(buf, &st)) return buf; if (type) { sfsprintf(buf, size, "%-.*s%s%.s", s - error_info.file + 1, error_info.file, name, type); if (regular(buf, &st)) return buf; } } /* * check the include dir list */ for (dp = state.head; dp; dp = dp->next) { sfsprintf(tmp, sizeof(tmp), "%s/%s", dp->dir, name); if (pathpath(tmp, "", PATH_REGULAR, buf, size)) return buf; if (type) { sfsprintf(tmp, sizeof(tmp), "%s/%s.%s", dp->dir, name, type); if (pathpath(tmp, "", PATH_REGULAR, buf, size)) return buf; } } /* * finally a lib related search on PATH */ if (lib) { if (s = strrchr((char*)lib, ':')) lib = (const char*)s + 1; sfsprintf(tmp, sizeof(tmp), "lib/%s/%s", lib, name); if (pathpath(tmp, "", PATH_REGULAR, buf, size)) return buf; if (type) { sfsprintf(tmp, sizeof(tmp), "lib/%s/%s.%s", lib, name, type); if (pathpath(tmp, "", PATH_REGULAR, buf, size)) return buf; } } return 0; }
char* pathprobe_20100601(const char* lang, const char* tool, const char* aproc, int op, char* path, size_t pathsize, char* attr, size_t attrsize) { 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(proc, NiL, PATH_ABSOLUTE|PATH_REGULAR|PATH_EXECUTE, cmd, sizeof(cmd)))) proc = (char*)aproc; else if (p) { n = strlen(proc); strncopy(proc + n, p, PATH_MAX - n - 1); } } if (!path) { path = buf; pathsize = sizeof(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(lang, tool, proc, key, sizeof(key), attr, attrsize); if (op >= -2) { strncopy(p, key, x - p); if (pathpath(lib, "", PATH_ABSOLUTE, path, pathsize) && !stat(path, &st) && (st.st_mode & S_IWUSR)) return path == buf ? strdup(path) : path; } e = strncopy(p, probe, x - p); if (!pathpath(lib, "", PATH_ABSOLUTE|PATH_EXECUTE, path, pathsize) || 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(dir, ':', "..", exe, path, pathsize); pathcanon(path, pathsize, 0); if (*path == '/' && pathexists(path, PATH_REGULAR|PATH_EXECUTE)) { pathcat(dir, ':', "..", lib, path, pathsize); pathcanon(path, pathsize, 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; }