#include <signal.h>
#include <errno.h>
#include <wait.h>
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);
	}
}

