views:

178

answers:

4

In a UNIX-y way, I'm trying to start a process, background it, and tie the lifetime of that process to my shell.

What I'm talking about isn't simply backgrounding the process, I want the process to be sent SIGTERM, or for it to have an open file descriptor that is closed, or something when the shell exits, so that the user of the shell doesn't have to explicitly kill the process or get a "you have running jobs" warning.

Ultimately I want a program that can run, uniquely, for each shell and carry state along with that shell, and close when the shell closes.

IBM's DB2 console commands work this way. When you connect to the database, it spawns a "db2bp" process, that carries the database state and connection and ties it to your shell. You can connect in multiple different terminals or ssh connections, each with its own db2bp process, and when those are closed the appropriate db2bp process dies and that connection is closed.

DB2 queries are then started with the db2 command, which simply hands it off to the appropriate db2bp process. I don't know how it communicates with the correct db2bp process, but maybe it uses the tty device connected to stdin as a unique key? I guess I need to figure that out too.

I've never written anything that does tty manipulation, so I have no clue where to even start. I think I can figure the rest out if I can just spawn a process that is automatically killed on shell exit. Anyone know how DB2 does it?

A: 

Your shell should be sending a SIGHUP signal to any running child processes when it shuts down. Have you tried adding a SIGHUP handler to your application to shut it down cleanly when the shell exits?

Jim Lewis
I just tried that, but I don't think it works. If you background this python process: import os, sys, time, signal def handler(signum, frame): print 'Exiting after caught with signal', signum sys.exit(0) signal.signal(signal.SIGHUP, handler) while True: time.sleep(1) Then the shell still complains about running jobs on exit (or, if configured to, just leaves the process running forever). The process doesn't seem to be sent SIGHUP until I kill -SIGHUP it.
Kyren
Wow, I really fail at Stack Overflow formatting.
Kyren
A: 

Is it possible that your real problem here is the shell and not your process. My understanding agrees with Jim Lewis' that when the shell dies its children should get SIGHUP. But what you're complaining about is the shell (or perhaps the terminal) trying to prevent you from accidentally killing a running shell with active children.

Consider reading the manual for the shell or the terminal to see if this behavior is configurable.

From the bash manual on my MacBook:

The shell exits by default upon receipt of a SIGHUP. Before exiting, an interactive shell resends the SIGHUP to all jobs, running or stopped. Stopped jobs are sent SIGCONT to ensure that they receive the SIGHUP. To prevent the shell from sending the signal to a particular job, it should be removed from the jobs table with the disown builtin (see SHELL BUILTIN COMMANDS below) or marked to not receive SIGHUP using disown -h.

If the huponexit shell option has been set with shopt, bash sends a SIGHUP to all jobs when an interactive login shell exits.

which might point you in the right direction.

dmckee
It is, but I shouldn't have to do that. I hadn't looked before, but I did find that option in bash and zsh manpages. I shouldn't *have* to do that, though. Yeah, I could configure bash or zsh to *kill* all background processes once they exit, even though the manpages suggest not to, but DB2 doesn't seem to need that, and it works right now. How is DB2 doing it?
Kyren
'fraid I can't help much then. I agree that this is a desirable feature, just don't know how to do it.
dmckee
Well, to correct myself, they're not being killed -- like you said, they're being SIGHUP-ed :) Also, it's only the zsh manpage that suggests always using NO_CHECK_JOBS and NO_HUP together. But still, I shouldn't have to reconfigure my shell
Kyren
+1  A: 

Okay, I think I figured it out. I was making it too complicated :)

I think all db2 is daemon-izing db2bp, then db2bp is calling waitpid on the parent PID (the shell's PID) and exiting after waitpid returns.

The communication between the db2 command and db2bp seems to be done via fifo with a filename based on the parent shell PID.

Waaaay simpler than I was thinking :)

For anyone who is curious, this whole endeavor was to be able to tie a python or groovy interactive session to a shell, so I could test code while easily jumping in and out of a session that would retain database connections and temporary classes / variables.

Thank you all for your help!

Kyren
A: 

If your shell isn't a subshell, you can do the following; Put the following into a script called "ttywatch":

#!/usr/bin/perl
my $p=open(PI, "-|") || exec @ARGV; sleep 5 while(-t); kill 15,$p;

Then run your program as:

$ ttywatch commandline... & disown

Disowning the process will prevent the shell from complaining that there are running processes, and when the terminal closes, it will cause SIGTERM (15) to be delivered to the subprocess (your app) within 5 seconds.

If the shell isn't a subshell, you can use a program like ttywrap to at least give it its own tty, and then the above trick will work.

geocar
Yep, that works! I figured out how db2 does it, which is not exactly the same, but this is so short and simple. My perl is not all that great though... what's while(-t) testing?
Kyren
Ah, found it. -t defaults to stdin, and tests to see if the file is opened to a tty... pretty slick.
Kyren