How to "Intercept" Ctrl+C in a Cli Application

How can I intercept Ctrl+C in a CLI application?

Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() { /*
my shutdown code here
*/ }
});

This should be able to intercept the signal, but only as an intermediate step before the JVM completely shutdowns itself, so it may not be what you are looking after.

You need to use a SignalHandler (sun.misc.SignalHandler) to intercept the SIGINT signal triggered by a Ctrl+C (on Unix as well as on Windows).

See this article (pdf, page 8 and 9).

How do I trap Ctrl+C (SIGINT) in a C# console app?

See MSDN:

Console.CancelKeyPress Event

Article with code samples:

Ctrl-C and the .NET console application

Can I send a ctrl-C (SIGINT) to an application on Windows?

The closest that I've come to a solution is the SendSignal 3rd party app. The author lists source code and an executable. I've verified that it works under 64-bit windows (running as a 32-bit program, killing another 32-bit program), but I've not figured out how to embed the code into a windows program (either 32-bit or 64-bit).

How it works:

After much digging around in the debugger I discovered that the entry point that actually does the behavior associated with a signal like ctrl-break is kernel32!CtrlRoutine. The function had the same prototype as ThreadProc, so it can be used with CreateRemoteThread directly, without having to inject code. However, that's not an exported symbol! It's at different addresses (and even has different names) on different versions of Windows. What to do?

Here is the solution I finally came up with. I install a console ctrl handler for my app, then generate a ctrl-break signal for my app. When my handler gets called, I look back at the top of the stack to find out the parameters passed to kernel32!BaseThreadStart. I grab the first param, which is the desired start address of the thread, which is the address of kernel32!CtrlRoutine. Then I return from my handler, indicating that I have handled the signal and my app should not be terminated. Back in the main thread, I wait until the address of kernel32!CtrlRoutine has been retrieved. Once I've got it, I create a remote thread in the target process with the discovered start address. This causes the ctrl handlers in the target process to be evaluated as if ctrl-break had been pressed!

The nice thing is that only the target process is affected, and any process (even a windowed process) can be targeted. One downside is that my little app can't be used in a batch file, since it will kill it when it sends the ctrl-break event in order to discover the address of kernel32!CtrlRoutine.

(Precede it with start if running it in a batch file.)

Catching Ctrl+C in Java

You can attach a shutdown hook to the VM which gets run whenever the VM shuts down:

The Java virtual machine shuts down in response to two kinds of events:

  • The program exits normally, when the last non-daemon thread exits or when the exit (equivalently, System.exit) method is invoked, or

  • The virtual machine is terminated in response to a user interrupt, such as typing Ctrl+C, or a system-wide event, such as user logoff or system shutdown.

The thread you pass as shutdown hook has to follow several rules, though, so read the linked documentation carefully to avoid any problems. This includes ensuring thread-safety, quick termination of the thread, etc.

Also, as commenter Jesper points out, shutdown hooks are guaranteed to run on normal shutdown of the VM but if the VM process is terminated forcibly they don't. This can happen if native code screws up or if you forcibly kill the process (kill -9, taskkill /f).

But in those scenarios all bets are off anyway, so I wouldn't waste too much thought on it.

How can I catch a ctrl-c event?

signal isn't the most reliable way as it differs in implementations. I would recommend using sigaction. Tom's code would now look like this :

#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

void my_handler(int s){
printf("Caught signal %d\n",s);
exit(1);

}

int main(int argc,char** argv)
{

struct sigaction sigIntHandler;

sigIntHandler.sa_handler = my_handler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;

sigaction(SIGINT, &sigIntHandler, NULL);

pause();

return 0;
}

How to catch Ctrl-C signal inside an Objective-C function?

Technically inside your Objective-C method you can't catch a SIGINT signal (generated by user upon pressing CTRL-C) - your program gets interrupted.
The control of flow at this point is handled by kernel. The state of cpu registers valid during execution of your objective-c run method are inevitably lost. Hence you can't continue executing your run method anymore.

What you can do is register a custom SIGINT handler using C API.

#import <signal.h>
void sigHandler(int sig) {
//as noted by @bbum list of functions with guaranteed behavior in sighandler is very limited
//obj-c runtime is definitely NOT on the list
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
[process1 stop];
[process2 stop];
});
//give extra time for the async part to run
usleep(50);
}

int main(int argc, const char * argv[]) {
signal(SIGINT, sigHandler);
@autoreleasepool {
//your normal program launch goes here
}
return 0;
}

If you have a console app alternatively you could consider route here
so basically disable CTRL-C firing SIGINT and asynchronously handle the keyboard reading for CTRL-C yourself. The main drawback is if your program would hit an endless loop it cannot be interrupted with CTRL-C. So to minimise this effect you could set this special terminal mode at the beginning of your run method and restore it once you're done.



Related Topics



Leave a reply



Submit