www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to wait for a shell process to finish on ctrl+c before exiting?

reply aliak <something something.com> writes:
I'm writing some command line tooling stuff, and one of the 
command spins up a docker compose file (which in short, spins up 
some services and aggregates the output of each service to 
stdout).

When a user presses ctrl+c, i would like to pass on the ctrl+c to 
the spawned process and wait till it handles ctrl+c and then let 
go of the current process.

So far I have this:

int spawnedPid;

extern(C) int kill(int pid, int sig) nothrow  nogc  system;

extern(C) void interruptHandler(int sig) nothrow  nogc  system {
     kill(spawnedPid, sig);
}

int spawnProcessAndWait(string[] cmd) {
     auto pid = spawnProcess(cmd, stdin, stdout, stderr);
     spawnedPid = pid.processID;
     signal(SIGINT, &interruptHandler);
     int result = wait(pid);
     return wait(pid);
}

It doesn't work. I think the call to kill doesn't wait? Is there 
a way to make it wait?

I can't call kill(Pid) or wait(Pid) inside the interrupt handler 
because those are not  nogc [0].

[0]: 
https://forum.dlang.org/thread/mtikzznfaahiltguvybw forum.dlang.org
Nov 24 2019
next sibling parent reply mipri <mipri minimaltype.com> writes:
On Sunday, 24 November 2019 at 15:44:00 UTC, aliak wrote:
 I'm writing some command line tooling stuff, and one of the 
 command spins up a docker compose file (which in short, spins 
 up some services and aggregates the output of each service to 
 stdout).

 When a user presses ctrl+c, i would like to pass on the ctrl+c 
 to the spawned process and wait till it handles ctrl+c and then 
 let go of the current process.
This might be useful: --- import std; import core.stdc.signal; int spawnedPid; extern(C) int kill(int pid, int sig) nothrow nogc system; extern (C) int waitpid(int pid, int* status, int options) nothrow nogc system; extern(C) void interruptHandler(int sig) nothrow nogc system { kill(spawnedPid, SIGTERM); waitpid(spawnedPid, null, 0); } int spawnProcessAndWait(string[] cmd) { auto pid = spawnProcess(cmd, stdin, stdout, stderr); spawnedPid = pid.processID; signal(SIGINT, &interruptHandler); int result = wait(pid); return result; } void main() { spawnProcessAndWait(["perl", "-le", "$SIG{INT} = sub { print 'Ignoring interrupt' }; $SIG{TERM} = sub { print 'Got'; sleep 2; print 'Term' }; print 'Go'; sleep 10"]); } --- I don't just using this code, but running this and seeing how it behaves. For example, you might be surprised by the output if you hit control-C as soon as you see the "Go": --- Go ^CIgnoring interrupt Got Term --- with a 2s delay after 'Got'. Or this output if you remove the Perl $SIG{INT} assignment: --- Go ^C --- with you dropped right back to the shell. It's also possible to cause this code to exit with an exception from the wait(), because there's no process for it wait for.
Nov 24 2019
parent aliak <something something.com> writes:
On Sunday, 24 November 2019 at 16:05:14 UTC, mipri wrote:
 On Sunday, 24 November 2019 at 15:44:00 UTC, aliak wrote:
 [...]
This might be useful: --- import std; import core.stdc.signal; [...]
waitpid, of course! Thanks agin :)
Nov 25 2019
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 11/24/19 10:44 AM, aliak wrote:
 I'm writing some command line tooling stuff, and one of the command 
 spins up a docker compose file (which in short, spins up some services 
 and aggregates the output of each service to stdout).
 
 When a user presses ctrl+c, i would like to pass on the ctrl+c to the 
 spawned process and wait till it handles ctrl+c and then let go of the 
 current process.
 
 So far I have this:
 
 int spawnedPid;
 
 extern(C) int kill(int pid, int sig) nothrow  nogc  system;
 
 extern(C) void interruptHandler(int sig) nothrow  nogc  system {
      kill(spawnedPid, sig);
 }
 
 int spawnProcessAndWait(string[] cmd) {
      auto pid = spawnProcess(cmd, stdin, stdout, stderr);
      spawnedPid = pid.processID;
      signal(SIGINT, &interruptHandler);
      int result = wait(pid);
      return wait(pid);
 }
 
 It doesn't work. I think the call to kill doesn't wait? Is there a way 
 to make it wait?
Hm.. are you sure that ctrl-c isn't also sending the signal to your child process? I thought it did. -Steve
Nov 24 2019
parent reply aliak <something something.com> writes:
On Sunday, 24 November 2019 at 17:04:49 UTC, Steven Schveighoffer 
wrote:
 On 11/24/19 10:44 AM, aliak wrote:
 [...]
Hm.. are you sure that ctrl-c isn't also sending the signal to your child process? I thought it did. -Steve
Yesh, you're right. That extra kill is unnecessary and was actually causing problems. So thanks for that!
Nov 25 2019
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 11/25/19 3:55 AM, aliak wrote:
 On Sunday, 24 November 2019 at 17:04:49 UTC, Steven Schveighoffer wrote:
 On 11/24/19 10:44 AM, aliak wrote:
 [...]
Hm.. are you sure that ctrl-c isn't also sending the signal to your child process? I thought it did.
Yesh, you're right. That extra kill is unnecessary and was actually causing problems. So thanks for that!
As a general rule though, I would say you should do as LITTLE as possible inside your signal handler. Being inside a signal handler is unlike any other function, because you can be called from anywhere. Locks can be held that are not normally, etc. Most of the time, I just set flags in signal handlers and handle them asynchronously outside in my main loop. -Steve
Nov 25 2019