RE: Process library and signals

My apologies if I misinterpreted your comments. There appear to be some use cases and conventions here that I'm not altogether familiar with. So basically you're saying that if runProcess is to be used in a system()-like way, that is the parent is going to wait synchronously for the child, then the parent should be ignoring SIGQUIT/SIGINT. On the other hand, if runProcess is going to be used in a popen()-like way, then the parent should not be ignoring SIGQUIT/SIGINT. The current interface doesn't allow for controlling the behaviour in this way. So the current signal handling in runProcess is wrong, and should probably be removed. What should we have instead? We could implement the system()-like signal handling for System.Cmd.system only, perhaps. Cheers, Simon On 26 October 2004 23:38, Glynn Clements wrote:
Having looked at the latest version of the Process library, it appears that my earlier comments about signal handling may have been misinterpreted.
First, my comments regarding the handling of SIGINT/SIGQUIT were specific to system(). The C system() function ignores these signals in the parent while the child is executing. However, this doesn't necessarily apply to other functions; e.g. popen() doesn't ignore these signals, and runProcess probably shouldn't either.
With system(), the parent blocks until the child is finished, so if the user presses Ctrl-C to kill the "currently executing" process, they probably want to kill the child. If the parent wants to die on Ctrl-C, it can use WIFSIGNALED/WTERMSIG to determine that the child was killed and terminate itself.
OTOH, with popen(), the parent continues to run alongside the child, with the child behaving as a "slave", so the parent will normally want to control the signal handling.
Ideally, system() equivalents (e.g. system, rawSystem) would ignore the signals in the parent, popen() equivalents (e.g. runInteractiveProcess) wouldn't, and lower-level functions (e.g. runProcess) would give you a choice.
Unfortunately, there is an inherent conflict between portability and generality, as the Unix and Windows interfaces are substantially different. Unix has separate fork/exec primitives, with the option to execute arbitrary code between the two, whilst Windows has a single primitive with a fixed set of options.
Essentially, I'm not sure that a Windows-compatible runProcess would be sufficiently general to accurately implement both system() and popen() equivalents on Unix. Either system/rawSystem should be implemented using lower-level functions (i.e. not runProcess) or runProcess needs an additional option to control the handling of signals in the child.
Also, my comment regarding the signals being "reset" in the child was inaccurate. system() doesn't reset them in the sense of SIG_DFL. It sets them to SIG_IGN before the fork(), recording their previous handlers. After the fork, it resets them in the child to the values they had upon entry to the system() function (i.e. to the values they had before they were ignored). The effect is as if they had been set to SIG_IGN in the parent after the fork(), but without the potential race condition.
Thus, if they were originally ignored in the parent before system() was entered, they will be ignored in the child. If they were at their defaults (SIG_DFL) before system() was entered, they will be so in the child. If they had been set to specific handlers, system() will restore those handlers in the child, but then execve() will reset them to SIG_DFL, as the handler functions won't exist after the execve().

Simon Marlow wrote:
So basically you're saying that if runProcess is to be used in a system()-like way, that is the parent is going to wait synchronously for the child, then the parent should be ignoring SIGQUIT/SIGINT. On the other hand, if runProcess is going to be used in a popen()-like way, then the parent should not be ignoring SIGQUIT/SIGINT.
Exactly.
The current interface doesn't allow for controlling the behaviour in this way.
Yep.
So the current signal handling in runProcess is wrong, and should probably be removed. What should we have instead? We could implement the system()-like signal handling for System.Cmd.system only, perhaps.
Well, probably for system and rawSystem.
The problem, as I see it, is that the Process library is meant to be
both flexible and portable. If you don't need the portability, you
already have the primitives in System.Posix, and separate fork/exec
will inevitably provide more flexibility than an all-in one version.
If you provide system/rawSystem and runInteractive{Command,Process},
that's covered the most common cases (i.e. system() and popen()). So
what is runProcess for? If it doesn't do the signal handling, it's
only really suitable for popen-style usage.
Which is unfortunate; I can imagine a use for an intermediate
"semi-raw" system, which supports e.g. file redirection or even
command pipelines, but without using the shell (i.e. accepts the
argv[] individually). In particular, using the shell is risky if you
want to use untrusted data in the argument list (e.g. CGI programs).
If runProcess doesn't do the signal handling between the fork and the
exec, you can't change the child's signal handling after the exec. You
could change the signal handling of the parent (i.e. the current
process) before calling runProcess, let the child inherit it, then
change it back again after runProcess returns, but that gives rise to
a potential race condition.
One possibility would be to allow an extra argument of type IO () (or
Maybe (IO ()), where Nothing is shorthand for Just $ return ()) which
would be executed between the fork and the exec on Unix and ignored on
Windows. AFAICT, that would expose the full functionality available on
Unix without interfering with Windows usage or adding complexity.
--
Glynn Clements
participants (2)
-
Glynn Clements
-
Simon Marlow