-- David Fox http://found.cs.nyu.edu/fox xoF divaD NYU Media Research Lab baL hcraeseR aideM UYN----------------- Cut Here --------------------- // procbug.c - demonstrate a Linux kernel bug? // David Fox, NYU Media Research Lab, 9 Mar 1996. // // Run the following program with arguments like // // % ./procbug "/bin/sh" "-c" "yes | cat -n | head -2000" // // About half the time I get only 1820 lines of output. How about // you?
#include <unistd.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <minmax.h> #include <sys/time.h> #include <sys/resource.h> #include <sys/wait.h>
// These macros define which end of the pipes we read from and which // we write to.
#define READEND 0 #define WRITEEND 1
class ChildProcess { public: ChildProcess(const char** argv, int infd=-1, int outfd=-1, int errfd=-1); ~ChildProcess();
// Input(), output(), and error() return the file descriptors through // which you can communicate with the process. Getpid() returns the // child's process id.
int input() {return infd_;} int output() {return outfd_;} int error() {return errfd_;} int getpid() {return pid_;} private: void Init(const char* path, const char** argv, int infd, int outfd, int errfd);
int infd_; int outfd_; int errfd_; int pid_; };
inline void doclose(int fd) { if (close(fd) == -1) { perror("close"); exit(1); } }
inline void dodup(int fd1, int fd2) { if (fd1 != fd2) {if (dup2(fd1, fd2) == -1) {perror("dup"); exit(1);}} }
ChildProcess::ChildProcess(const char** argv, int infd, int outfd, int errfd) { infd_ = infd; outfd_ = outfd; errfd_ = errfd;
int pfdin[2]; int pfdout[2]; int pfderr[2];
// Thank God I never have to write this again!
if (infd_ == -1) {if (pipe(pfdin) == -1) {perror("pipe"); exit(1);}} if (outfd_ == -1) {if (pipe(pfdout) == -1) {perror("pipe"); exit(1);}} if (errfd_ == -1) {if (pipe(pfderr) == -1) {perror("pipe"); exit(1);}}
switch (pid_ = fork()) { case -1: perror("fork"); exit(1); case 0:
// This section is executed by the child process. We close the // three standard file descriptors and dup the child end of the // three pipes so the child process standard descriptors and the // pipes are the same file.
dodup(infd_ == -1 ? pfdin[READEND] : infd_, STDIN_FILENO); dodup(outfd_ == -1 ? pfdout[WRITEEND] : outfd_, STDOUT_FILENO); dodup(errfd_ == -1 ? pfderr[WRITEEND] : errfd_, STDERR_FILENO);
// Here we close all the pipe descriptors, either because they // are the parent end of the pipe or because we have duped them // above as file descriptors 0, 1, and 2.
if (infd_ == -1) {doclose(pfdin[WRITEEND]); doclose(pfdin[READEND]);} else if (infd_ != 0) {doclose(infd_);}
if (outfd_ == -1) {doclose(pfdout[READEND]); doclose(pfdout[WRITEEND]);} else if (outfd_ != 1) {doclose(outfd_);}
if (errfd_ == -1){doclose(pfderr[READEND]); doclose(pfderr[WRITEEND]);} else if (errfd_ != 2) {doclose(errfd_);}
// Launch the child program.
execvp(argv[0], (char**)argv); perror("execvp"); // Not reached. exit(1); }
// This section is executed by the parent process.
// Close the child end of each pipe. If we opened a pipe for this // fd then assign the parent's fd. If an fd was supplied no pipe // was opened so no fd is available for the parent, set it to -1.
if (infd_ == -1) {doclose(pfdin[READEND]); infd_ = pfdin[WRITEEND];} else infd_ = -1; if (outfd_ == -1) {doclose(pfdout[WRITEEND]); outfd_ = pfdout[READEND];} else outfd_ = -1; if (errfd_ == -1) {doclose(pfderr[WRITEEND]); errfd_ = pfderr[READEND];} else errfd_ = -1; }
ChildProcess::~ChildProcess() { close(infd_); close(outfd_); close(errfd_); ::wait(0); }
void sigchld_handler(int signo) { //printf("sigchld_handler\n"); int status; int pid = wait(&status); if (WIFEXITED(status)) exit(0); }
main(int ac, char *av[]) { char buf[512];
if (av[ac] != 0) { fprintf(stderr, "Sorry, av[%d] is not NULL, I thought it would be.\n", ac); exit(1); }
ChildProcess p((const char **)&av[1]);
signal(SIGCHLD, sigchld_handler);
fd_set readfds; FD_ZERO(&readfds); FD_SET(p.output(), &readfds); FD_SET(p.error(), &readfds); while (1) { int count = 0; select(getdtablesize(), &readfds, 0, 0, 0); if (FD_ISSET(p.output(), &readfds)) { count = read(p.output(), buf, 512); if (count) write(STDOUT_FILENO, buf, count); else exit(0); } if (FD_ISSET(p.error(), &readfds)) { count = read(p.error(), buf, 512); if (count) write(STDERR_FILENO, buf, count); else exit(0); } } }