[comp.unix.wizards] How to send child signal when parent exits?

david@wiley.UUCP (David Hull) (02/22/90)

I've written a replacement for biff/comsat that works even when the
mail spool directory is mounted over NFS and the mail is being delivered
on another machine.  It wakes up every minute and stats the user's mail
file to decide whether he has received any new mail.

The program should automatically die when the user logs out.  I'd like
it to receive a SIGHUP when its parent, the login shell, exits, but
haven't been able to make it work.  Aren't all the processes in the
same process group as the login supposed to receive a SIGHUP when it
exits?

The program works now by calling getppid right before it stats the file.
If the parent has exited, init has inherited the program and the ppid
is 1.  It works but seems inelegant.  Does anybody have any help?

-David
					---------------------------------------
					David Hull  TRW Inc.  Redondo Beach, CA
					     david%wiley.uucp@csvax.caltech.edu
						...!{uunet,cit-vax}!wiley!david

bgg@yarra.oz.au (Benjamin G. Golding) (02/23/90)

In article <8948@wiley.UUCP> david@wiley.UUCP (David Hull) writes:
> I've written a replacement for biff/comsat that works even when the
> mail spool directory is mounted over NFS and the mail is being delivered
> on another machine.  It wakes up every minute and stats the user's mail
> file to decide whether he has received any new mail.
>
> The program should automatically die when the user logs out.

Why not reorganise the order of the programs?  If you make your comsat
replacement the login shell and have it fork and wait for an
interactive shell to exit before your program exits.  A timer could
interrupt the wait() to tell it when to check for new mail.  The wait()
needs to be in a while loop so that it will resume if it was
interrupted (on unixes without restartable system calls).

It sounds simpler anyway.

	Ben.

tony@oha.UUCP (Tony Olekshy) (02/25/90)

In message <1416@yarra.oz.au>, bgg@yarra.oz.au (Benjamin G. Golding) writes:
>
> In article <8948@wiley.UUCP> david@wiley.UUCP (David Hull) writes:
> > I've written a replacement for biff/comsat that works even when the
> > mail spool directory is mounted over NFS and the mail is being delivered
> > on another machine.  It wakes up every minute and stats the user's mail
> > file to decide whether he has received any new mail.
> >
> > The program should automatically die when the user logs out.
> 
> Why not reorganise the order of the programs?

Well, that's one way, or you could have the parent spawn both the shell
and mail watcher and kill the living when either dies, or fiddle something
with SIGHUP (I think), or have the child try to kill(parent_id, 0) once
around each loop.

However, the following will also work as long as the parent doesn't close
the writing end of the pipe until it is exiting.  You should clean it up
to check all return codes, of course.  Interested readers are referred to
the pipe, read, and exit man pages:

From pipe:

     Description
         fildes[0] is opened for reading and fildes[1] is opened for writing
	 and the O_NDELAY flag is clear.  The descriptors remain open across
	 fork system calls...

From read:

    When attempting to read from an empty pipe:

        If O_NDELAY is clear, the read will block until data is written to
	the file or the file is no longer open for writing.
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    read will fail if one or more of the following are true:

        A signal was caught during the read system call.  [EINTR]

From exit:

    All of the file descriptors open in the calling process are closed.

----------------------------------------------------------------------------

#include <stdio.h>
#include <signal.h>
#include <errno.h>

#define CHILD_CYCLE 2	/* Delay between child ops. */

childWork()
{
    alarm(0);
    /* Put your mail watching code here. */
    fprintf(stderr, "Child working!\n");
    }

main()
{
    int		 pfd[2];
    int		 pid;
    char	 byte;

    if (pipe(pfd) == -1) perror("pipe");

    switch (pid = fork()) {

	case -1:
	    perror("fork");
	    exit (1);

	case 0:			/* Child */
	    close(pfd[1]);
	    signal(SIGINT, SIG_IGN);
	    fprintf(stderr, "Child %d running...\n", getpid());

	    while (1) {
		alarm(CHILD_CYCLE);
		signal(SIGALRM, childWork);
		errno = 0;
		read(pfd[0], &byte, 1);	/* Blocks till write or sig. */
		switch (errno) {
		    case 0:		/* Parent exit closed pipe. */
			fprintf(stderr, "Child %d done.\n", getpid());
			exit (0);
		    case EINTR:		/* Alarm rang through childWork. */
			break;
		    default:		/* Hmmm? */
			fprintf(stderr, "Child read errno %d.\n", errno);
			exit (1);
		    }
		}
	    /* NOTREACHED */

	default:		/* Parent */
	    break;
	}

    close(pfd[0]);
    fprintf(stderr, "Parent %d execing shell, ^D to stop...\n", getpid());
    execl("/bin/sh", "sh", NULL);
    perror("exec");
    exit (1);
    }

--
Yours, etc., Tony Olekshy (...!alberta!oha!tony or tony@oha.UUCP).