#pragma implementation #include "program.h" #include #include #include #include #include #include #include Program *Program::first; Program::Program() { pid = -1; suid = getuid(); // default to running programs as real user sgid = getgid(); // default to running programs as real group setfd(2,dup(2)); // by default, map stderr only } Program::~Program() { resetfd(); remove(pid); } Program &Program::resetfd() { // close file descriptors for (int i = 0; i < fd.size(); ++i) close(fd[i]); fd.clear(); return *this; } Program &Program::setfd(int f,int dupf) { if (f < 0) close(dupf); else { while (fd.size() <= f) fd.add(-1); close(fd[f]); fd[f] = dupf; } return *this; } int Program::pipein(int f) { int p[2]; if (pipe(p)) return -1; setfd(f,p[0]); return p[1]; } int Program::pipeout(int f) { int p[2]; if (pipe(p)) return -1; setfd(f,p[1]); return p[0]; } Program &Program::input(int f,const char *name) { return setfd(f,open(name,O_RDONLY)); } Program &Program::output(int f,const char *name) { return setfd(f,open(name,O_WRONLY + O_TRUNC + O_CREAT,0666)); } Program &Program::append(int f,const char *name) { return setfd(f,open(name,O_WRONLY + O_APPEND + O_CREAT,0666)); } int Program::run(const char *s) { const char *sh[4]; sh[0] = getenv("SHELL"); if (!sh[0]) sh[0] = "/bin/sh"; sh[1] = "-c"; sh[2] = s; sh[3] = 0; return run(sh[0],sh); } void Program::beforeExec() { // set up environment int i; for (i = 0; i < env.size(); ++i) { char *s = new char[env[i].length() + 1]; strcpy(s,env[i].data()); putenv(s); } // set user and group for child setuid(suid); setgid(sgid); // redirect the file descriptors for the child for (i = 0; i < fd.size(); ++i) { // move any one pointing to us int j; for (j = i + 1; j < fd.size(); ++j) if (fd[j] == i) fd[j] = fcntl(i,F_DUPFD,j); int f = fd[i]; if (f == i) continue; // already there close(i); if (f >= 0) { fcntl(f,F_DUPFD,i); if (f > i) { close(f); for (j = i + 1; j < fd.size(); ++j) if (fd[j] == f) fd[j] = i; } } } // close remaining files // it would be nice to know how may files a process has open . . . // for now, we stop after many in a row were already closed for (int lim = 0; lim < 30; ++lim) if (close(i++) == 0) lim = 0; } int Program::run(const char *prog,const char * const *argv) { wait(); pid = fork(); status = -1; switch (pid) { case -1: // fork failed return -1; case 0: // we are the child process beforeExec(); execvp(prog,(char **)argv); perror("exec"); _exit(1); } next = first; first = this; return pid; } int Program::wait() { while (pid > 0) { int s; int p = ::wait(&s); if (p > 0) post(p,s); else if (errno != EINTR) return -1; } return status; } void Program::childExit(int s) { status = s; } void Program::childStop(int s) { status = s; } Program *Program::remove(int p) { if (p <= 0) return 0; Program *g; for (Program **f = &first; g = *f; f = &g->next) { if (g->pid == p) { *f = g->next; // remove from chain g->pid = -1; return g; } } return 0; } Program *Program::find(int p) { for (Program *g = first; g; g = g->next) if (g->pid == p) return g; return 0; } Program *Program::post(int p,int s) { bool stopped = WIFSTOPPED(s); Program *g = stopped ? find(p) : remove(p); if (g) { if (stopped) { int stopsig = WSTOPSIG(s); g->childStop(stopsig); } else g->childExit(s); } return g; } Program &Program::setenv(const char *s) { const char *p = strchr(s,'='); int len = p ? p - s : strlen(s); int i; for (i = 0; i < env.size(); ++i) { if (strncmp(env[i].data(),s,len) == 0 && env[i][len] == '=') { env[i] = s; break; } } if (i >= env.size()) env.add(s); if (s[len] != '=') env[i] += '='; return *this; }