#include #include #include int Interrupt=0; int Wake=0; pid_t Child=0; int Pipe[2]; sigset_t AllSigs; struct resource { char *name; struct resource *next; } *Resources=0; extern int ConnectToServer(); extern struct resource *NewResource(char *); extern void FreeResources(); void to_parent(char *buf) { int done=0; do { if(write(Pipe[0], buf, 1)==1) { done=1; } else if(errno!=EINTR) { done=1; perror(0); } } while(!done); } void remember(int signo) { to_parent("i"); Interrupt=1; } void wake() { to_parent("i"); Wake=1; } void handle() { FreeResources(); sigset(SIGINT, SIG_DFL); kill(getpid(), SIGINT); } void poll() { while(Wake || Interrupt) { Wake=0; to_parent("p"); if(Interrupt) { /* Wake needed in case SIGINT right here */ Interrupt=0; handle(); } } } ssize_t my_write(int fildes, char *buf, ssize_t nbyte) { poll(); /* What if SIGINT is received right here? */ return write(fildes, buf, nbyte); } void AllocateResource(int server, char *name) { int done=0; do { char buf[1000]; sprintf(buf, "Create %s for me, please\n", name); if(my_write(server, name, strlen(name))==-1) { if(errno!=EINTR) { FreeResources(); sigset(SIGINT, SIG_DFL); poll(); exit(2); } } else { Resources=NewResource(name); done=1; } } while(!done); } void p_poll() { if(Interrupt) { if(Child && Child!=-1) { kill(Child, SIGINT); } else { sigset(SIGINT, SIG_DFL); kill(getpid(), SIGINT); /* unreachable */ } } } void p_handler(int signo) { /* Might get called after fork(), but before setting Child */ switch(signo) { case SIGALRM: if(Child) { /* It's OK if Child is a zombie */ kill(Child, SIGUSR1); } break; default: if(Child && Child!=-1) { kill(Child, signo); } else { Interrupt=1; } break; } } void p_chld_handler() { int result; if(waitpid(-1, &result, WNOHANG)) { if(WIFEXITED(result) || WIFSIGNALED(result)) { Child=0; /* SIGQUIT gets translated into SIGINT, which * is what we want. */ sigset(SIGTSTP, SIG_DFL); sigset(SIGCONT, SIG_DFL); sigprocmask(SIG_UNBLOCK, &AllSigs, 0); if(WIFSIGNALED(result)) { kill(getpid(), WTERMSIG(result)); sigset(SIGINT, SIG_DFL); p_poll(); exit(WTERMSIG(result) | 0x80); } else { sigset(SIGINT, SIG_DFL); p_poll(); exit(WEXITSTATUS(result)); } /* unreachable */ } } } int main() { int server; struct sigaction sa; sigfillset(&AllSigs); if(pipe(Pipe)==-1) { perror(0); exit(2); } sigset(SIGINT, p_handler); sigset(SIGALRM, p_handler); sa.sa_handler=p_chld_handler; sa.sa_mask=AllSigs; sa.sa_flags=0; sigaction(SIGCHLD, &sa, 0); /* block all signals */ if((Child=fork())!=0) { close(Pipe[0]); if(Child==-1) { perror(0); sigset(SIGINT, SIG_DFL); p_poll(); exit(2); } /* If SIGQUIT happens right here, then you'll * get an unwanted second core dump. */ sigset(SIGQUIT, p_handler); sigset(SIGTSTP, p_handler); sigset(SIGCONT, p_handler); /* Sending SIGSTOP to parent doesn't affect the child * until the pipe gets clogged. */ p_poll(); while(1) { char c[1]; ssize_t nbytes; if((nbytes=read(Pipe[1],c,1))==1) { switch(*c) { case 'i': alarm(1); break; case 'p': alarm(0); break; } } else if(nbytes==0 || errno!=EINTR) { if(nbytes!=0) { perror(0); } pause(); } } } else { close(Pipe[1]); sigset(SIGCHLD, SIG_DFL); sigset(SIGALRM, SIG_DFL); sigset(SIGINT, SIG_DFL); p_poll(); sigset(SIGUSR1, wake); /* From original main(): */ sigset(SIGINT, remember); server=ConnectToServer(); AllocateResource(server,"bob"); AllocateResource(server,"mary"); FreeResources(); return(0); } }