[comp.unix.questions] signal problems on BSD

jsv@cci632.UUCP ( co-op) (03/06/90)

I have a program written in C that captures the HUP signal sent when a user
logs out and prints some information to a file.  This program works fine on
System V but on BSD machines, the program doesn't seem to get the signal and
when I log back on, it's still running (the interrupt function on this signal
has an exit() in it) and nothing was written to the file.

I know the program understands the signal, because when I execute:

   % kill -HUP <pid>

the program ends (on both systems) properly.

Any ideas as to why?

Jepher


-------------------
Jeffer Veiss

Rochester Institute of Technology       |     Computer Consoles, Inc.
JSV9504@RITVAX.BITNET                   |     jsv@cci632.uucp
jsv9504@ritcv.uucp                      |

Disclaimer?  yeah, right.

jik@athena.mit.edu (Jonathan I. Kamens) (03/06/90)

  In article <34853@cci632.UUCP>, Jeffer Veis (jsv@cci632.UUCP) asks why
a program waiting for a HUP signal (which it should get when the user
logs out) gets the signal when it is run under System V, but never
receives the signal when it is run under BSD.

  The likely explanation of this is that you are using the C shell (csh)
on the BSD machine, rather than the bourne shell (sh).  While sh
automatically sends a HUP signal to all its children when you log out
(actually, I'm not sure the shell does this actively; it's might be a
side-effect of the way and process groups et al work in BSD), this
doesn't happen in csh.  Therefore, the reason your process is not
getting the signal is because the signal is never sent.

  Here at Project Athena, we have a hack in our /bin/login which makes
it possible to do what you want, although I don't know how universal
this is (it isn't in the vanilla 4.3-tahoe sources, which means it isn't
a standard 4.3bsd thing).  After the child process (i.e. the login
shell) of /bin/login exits, /bin/login does "killpg(child, SIGHUP)",
where "child" is the process group of the child.

  Then, any process that wants to get a HUP signal when it logs out,
after being placed into the background upon start-up, simply does
"setpgrp(0, getpgrp(getppid()))".  In other words, "Make my process
group the same as the process group of my parent."  This overrides the
default csh behavior of creating a new process group for each child
process it runs.

  I don't know if there's any other way (that is more universally
available, and doesn't require modifications to /bin/login) to do what
you want effectively, other than having your process periodically check
the status of the parent process, by doing "kill(getppid(), 0)", and
assuming that the user has logged out when this kill returns an error.

  I hope this is helpful.

Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8495			      Home: 617-782-0710

maart@cs.vu.nl (Maarten Litmaath) (03/07/90)

In article <1990Mar6.070333.29327@athena.mit.edu>,
	jik@athena.mit.edu (Jonathan I. Kamens) writes:
)...  While sh
)automatically sends a HUP signal to all its children when you log out
)(actually, I'm not sure the shell does this actively; it's might be a
)side-effect of the way and process groups et al work in BSD), this

It's always been the kernel.  Quoting from termio(4) on SunOS 4.0.3c:

  Modem Disconnect
     If a modem disconnect is detected, and the  CLOCAL  flag  is
     not set in the c_cflag field, a SIGHUP signal is sent to all
     processes in the distinguished process group associated with
     this  terminal.   Unless  other arrangements have been made,
     this signal terminates the processes.  If SIGHUP is  ignored
     or caught, any subsequent read() returns with an end-of-file
     indication until the terminal  is  closed.   Thus,  programs
     that  read a terminal and test for end-of-file can terminate
     appropriately after a disconnect.   Any  subsequent  write()
     will  return  -1  and set errno to EIO until the terminal is
     closed.

)doesn't happen in csh.  Therefore, the reason your process is not
)getting the signal is because the signal is never sent.

...because csh puts each job into its own process group and a the group of a
background job never equals the tty process group (by definition!).

)  Here at Project Athena, we have a hack in our /bin/login which makes
)it possible to do what you want, although I don't know how universal
)this is (it isn't in the vanilla 4.3-tahoe sources, which means it isn't
)a standard 4.3bsd thing).  After the child process (i.e. the login
)shell) of /bin/login exits, /bin/login does "killpg(child, SIGHUP)",
)where "child" is the process group of the child.

Why don't you let the kernel or init(8) do the killpg()?  Now you have an
extra process hanging around, doing nothing but wait()ing.

)  Then, any process that wants to get a HUP signal when it logs out,
)after being placed into the background upon start-up, simply does
)"setpgrp(0, getpgrp(getppid()))".  In other words, "Make my process
)group the same as the process group of my parent."  This overrides the
)default csh behavior of creating a new process group for each child
)process it runs.  [...]

I assume you wrote a utility `hup' (the opposite of nohup(1)) too:

	a) to do this setpgrp() for you (!)
	b) to catch the keyboard signals (!!)
--
  "Belfast: a sentimental journey to the Dark Ages - Crusades & Witchburning
  - Europe's Lebanon - Book Now!" | maart@cs.vu.nl,  uunet!mcsun!botter!maart

jik@athena.mit.edu (Jonathan I. Kamens) (03/08/90)

In article <5913@star.cs.vu.nl>, maart@cs.vu.nl (Maarten Litmaath) writes:
> It's always been the kernel.  Quoting from termio(4) on SunOS 4.0.3c:

  Well, first of all, it's tty(4) in BSD, which is what I was talking
about.  Just a a small nit :-)

> )doesn't happen in csh.  Therefore, the reason your process is not
> )getting the signal is because the signal is never sent.
> 
> ...because csh puts each job into its own process group and a the group of a
> background job never equals the tty process group (by definition!).

  Yes, I should have let that specifically.

> )  Here at Project Athena, we have a hack in our /bin/login which makes
> )it possible to do what you want, although I don't know how universal
> )this is (it isn't in the vanilla 4.3-tahoe sources, which means it isn't
> )a standard 4.3bsd thing).  After the child process (i.e. the login
> )shell) of /bin/login exits, /bin/login does "killpg(child, SIGHUP)",
> )where "child" is the process group of the child.
> 
> Why don't you let the kernel or init(8) do the killpg()?  Now you have an
> extra process hanging around, doing nothing but wait()ing.

  Because I believe that the kernel SIGHUP functionality only works when
a dialup line or a hard-wired terminal line (i.e. something that init
deals with, I believe) is the login tty in question.  We don't use
hard-wired tty's here at Project Athena, we use pty's almost exclusively
(Remember, the X Window System was INVENTED at Project Athena :-).

> I assume you wrote a utility `hup' (the opposite of nohup(1)) too:

  No, actually, although it's an interesting idea.  I'll have to think
about it some more :-)

> 	a) to do this setpgrp() for you (!)
> 	b) to catch the keyboard signals (!!)

  What do you mean by "keyboard signals"??

Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8495			      Home: 617-782-0710

maart@cs.vu.nl (Maarten Litmaath) (03/09/90)

In article <1990Mar8.084830.9252@athena.mit.edu>,
	jik@athena.mit.edu (Jonathan I. Kamens) writes:
)...
)> )  Here at Project Athena, we have a hack in our /bin/login which makes
)> )it possible to do what you want, although I don't know how universal
)> )this is (it isn't in the vanilla 4.3-tahoe sources, which means it isn't
)> )a standard 4.3bsd thing).  After the child process (i.e. the login
)> )shell) of /bin/login exits, /bin/login does "killpg(child, SIGHUP)",
)> )where "child" is the process group of the child.
)> 
)> Why don't you let the kernel or init(8) do the killpg()?  Now you have an
)> extra process hanging around, doing nothing but wait()ing.
)
)  Because I believe that the kernel SIGHUP functionality only works when
)a dialup line or a hard-wired terminal line (i.e. something that init
)deals with, I believe) is the login tty in question.

Not necessarily.  Here's a little excerpt from the SunOS pty `deallocate'
routine:

        if (tp->t_state & TS_ISOPEN)
                gsignal(tp->t_pgrp, SIGHUP);
        tp->t_state &= ~TS_CARR_ON;     /* virtual carrier gone */

You're right insofar that init(8) can't offer the SIGHUP service for ptys.

)...
)> I assume you wrote a utility `hup' (the opposite of nohup(1)) too:
)...
)> 	a) to do this setpgrp() for you (!)
)> 	b) to catch the keyboard signals (!!)
)
)  What do you mean by "keyboard signals"??

If you setpgrp() to your parent's group, and this parent happens to be in
the foreground, *you* will receive keyboard signals too.

	% cat c.c
	main()
	{
		if (setpgrp(0, getpgrp(getppid())) < 0)
			perror("setpgrp");
		pause();
	}
	% cc c.c
	% a.out &
	[1] 6338
	% ^?
	[1]    a.out
	% kill -0 6338
	6338: No such process
	% a.out &
	[1] 6349
	% ^\
	[1]    Quit                 a.out (core dumped)
	% 

Remarks:
-	`^?' denotes `DEL' (interrupt);
-	csh seems to get confused about process 6338.
--
 1) Will 4.5BSD have wait5()?         |Maarten Litmaath @ VU Amsterdam:
 2) Sleep(3) should be sleep(2) again.|maart@cs.vu.nl, uunet!mcsun!botter!maart

guy@auspex.auspex.com (Guy Harris) (03/10/90)

>  Because I believe that the kernel SIGHUP functionality only works when
>a dialup line or a hard-wired terminal line (i.e. something that init
>deals with, I believe) is the login tty in question.

Well, it's a bit more complicated.  Your original article said:

> While sh automatically sends a HUP signal to all its children when you
> log out (actually, I'm not sure the shell does this actively; it's
> might be a side-effect of the way and process groups et al work in
> BSD) ...

and Maarten replied:

> It's always been the kernel.  Quoting from termio(4) on SunOS 4.0.3c:
> 
>   Modem Disconnect
>      If a modem disconnect is detected, and the  CLOCAL  flag  is
>      not set in the c_cflag field, a SIGHUP signal is sent to all
>      processes in the distinguished process group associated with
>      this  terminal.  ...

The latter has been true since Time Immemorial; however, it only
delivers the SIGHUP if you actually get a "modem disconnect".  Note,
though, that both the 4.3BSD and SunOS 4.x pseudo-tty drivers (and, I
suspect, the S5R4 pseudo-tty mechanism as well) treat the final "close"
of the controller side of a pseudo-tty as being the moral equivalent of
a "modem disconnect", and send a SIGHUP exactly as it's done for Carrier
Detect dropping on a "real" tty line.  So your belief that the kernel
SIGHUP functionality only works on a dialup or hardwired terminal line
is incorrect; it works on pseudo-ttys as well.

However, there is *another* source of SIGHUP.  In BSD, "getty",
"rlogind", and "telnetd" issue a "vhangup" call on the tty they're
using, in order to blow any old processes off that line.  One
consequence of "vhangup" is that, if the tty is still open, a SIGHUP
gets sent to its process group.  In System V, when a "process group
leader" (a login shell, for example) exits, and:

	1) it has a controlling tty

and

	2) that tty's process group is the process group of which that
	   process is a leader,

a SIGHUP is given to that process group.

Of course, if you have a job control shell, neither of the SIGHUPs
mentioned above will reach background processes, as stated by Maarten.

jik@athena.mit.edu (Jonathan I. Kamens) (03/12/90)

  Well, I've looked in my kernel code, and I do, indeed, see code that
*should be* sending a HUP signal to the process group of the tty,
although I may certainly be reading something wrong, given that I have
limited experience with kernel hacking.

  I then compiled a new /bin/login for testing -- the new /bin/login
doesn't do anything with killpg(), because I'm trying to figure out if
the kernel does it automatically.

  I then compiled the following program, telnet'd into my workstation
(thus, I assume logging in on a pty), ran it in the background, and logged out:

    #include <syslog.h>
    #include <signal.h>

    report_it()
    {
         syslog(LOG_DEBUG, "Got a SIGHUP!");
         exit(0);
    }

	  
    main()
    {
         signal(SIGHUP, report_it);
         setpgrp(0, getpgrp(getppid()));
     
         for (;;)
              sigpause(0);
    }

Now, *if* the kernel sends a HUP when a pty is closed, then the program
should have received a HUP and exited when I logged out, right?  Well,
it didn't.  How come?

  When I put back our standard /bin/login with the killpg() statement in
it and do the same thing, the program does get a HUP when I log out.  So
what am I missing?

  One more thing -- I think that someone else asked earlier what the
point is of having /bin/login wait around while the user is logged in --
he said that if we take out the killpg() in /bin/login, we can just have
it exec() the login shell rather than waiting for the shell to finish.

  Well, unfortunately, we can't do that, because our /bin/login does
various other clean-up tasks after the user logs out (e.g. removing the
user's entry from the /etc/passwd file if he was added to the
/etc/passwd file when he logged in, and automatically destroying the
user's kerberos tickets).  Therefore, exec()'ing the login shell isn't
an option.

Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8495			      Home: 617-782-0710

rr@csuna.cs.uh.edu (Ravindran Ramachandran) (03/14/90)

In article <1990Mar8.084830.9252@athena.mit.edu> jik@athena.mit.edu (Jonathan I. Kamens) writes:
>In article <5913@star.cs.vu.nl>, maart@cs.vu.nl (Maarten Litmaath) writes:
>> )doesn't happen in csh.  Therefore, the reason your process is not
>> )getting the signal is because the signal is never sent.
>> 
>> ...because csh puts each job into its own process group and a the group of a
>> background job never equals the tty process group (by definition!).
>

I had a similar kind of a problem on a Pyramid running ATT Sys 5, having
csh as the base shell. I have a Bourne shell script that is started up
from my .login, which runs a clock at the top corner of my tty. When I
log out, however, the process remains active even though there is no
actual device to output to. I tried in the script to 'trap' the signals,
but they are not received on logout.

The suggestion to:
a) set setpgrp() is slightly a pain in a shell script.

b) modify /bin/login (?) is impossible for me.


Right now, in my .logout I do a ps, and use awk to get the pid so that
I can explicitly kill the process. However, I wish the script would 
directly get the signal when I logout and expire!

For your eyes only (please destroy after reading),
  --Ravi-