How to Handle a Ctrl-Break Signal in a Command Line Interface

How to properly handle SIGBREAK when closing console?

I believe the timeout is fixed and there is no way to modify it, see API reference:

The system also displays the dialog box if the process does not respond within a certain time-out period (5 seconds for CTRL_CLOSE_EVENT, and 20 seconds for CTRL_LOGOFF_EVENT and CTRL_SHUTDOWN_EVENT).

A process can use the SetProcessShutdownParameters function to prevent the CTRL_LOGOFF_EVENT and CTRL_SHUTDOWN_EVENT dialog box from being displayed. In this case, the system just terminates the process when a HandlerRoutine returns TRUE or when the time-out period elapses.

How about an alternative approach, just disable the close button of your console window:

GUITHREADINFO info = {0};
info.cbSize=sizeof(info);
GetGUIThreadInfo(NULL, &info);
HMENU hSysMenu = GetSystemMenu(info.hwndActive, FALSE);
EnableMenuItem(hSysMenu, SC_CLOSE, MF_GRAYED);

Now you can safely handling closing through Ctrl-C or other keyboard input instead.

how to prevent Ctrl+Break/Ctrl+C from closing both programs if one was launched from another by system() or CreateProcess()?

Add this::

#include <signal.h>

...

signal (SIGINT, SIG_IGN);

After the signal() call, the program ignores Ctrl-Break. On Linux, ignoring signals propagates to child processes through fork()/exec().

I would expect Windows to reset the default signal handling on exec() due to the way the O/S + runtime library work. So if you want the child to ignore Break, add the code above to it too.

break from infinite loop from console

You could use signals:

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

static void on_close(int signal)
{
puts("Cleaning up...");
exit(EXIT_SUCCESS);
}

int main(void)
{
signal(SIGINT, on_close);

while (1)
{
}

return EXIT_SUCCESS;
}

Is it possible to capture a Ctrl+C signal (SIGINT) and run a cleanup function, in a defer fashion?

You can use the os/signal package to handle incoming signals. Ctrl+C is SIGINT, so you can use this to trap os.Interrupt.

c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func(){
for sig := range c {
// sig is a ^C, handle it
}
}()

The manner in which you cause your program to terminate and print information is entirely up to you.

Can i read the exit value from the console application?

This work for me on Windows try it

#include <csignal>
#include <iostream>
#include <ostream>
#include <string>
using namespace std;

namespace
{
volatile sig_atomic_t exit;

void signal_handler(int sig)
{
signal(sig, signal_handler);
exit= 1;
}
}

int main()
{
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
#ifdef SIGBREAK
signal(SIGBREAK, signal_handler);
#endif

while (!exit)
{
/* do something */
}
// Catch signal here
}

CLI in C++: cin and Ctrl+C

Edit :

Your problem is that in C++, when the getline function is interrupted, you have to manually clear the error state before next call. It is enough in your loop to write :

while (1) {

getline(cin, _input);
if (cin.fail() || cin.eof()) {
cin.clear(); // reset cin state
}
_parse_cmd(_input);

}

But beware: as you also filter Ctrl-Break, you loop could be hard to stop ...

TL/DR : Below are my first steps to this simple solution first in C idiom, next in C++ one, that only filter Ctrl-C and are killed on Ctrl-Break.

You can obtain easily the Ctrl-C interception with the signal function.

Here is an example of usage :

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

void ctrl_c(int sig) {
fprintf(stderr, "Ctrl-C caught\n");
signal(sig, ctrl_c); /* re-installs handler */
}

int main() {
char buf[256];
void (*old)(int);

old = signal(SIGINT, ctrl_c); /* installs handler */

for (;;) {
if (fgets(buf, sizeof(buf), stdin) != NULL) {
printf("Got : %s", buf);
}
}
signal(SIGINT, old); /* restore initial handler */
return 0;
}

Ctrl-C is intercepted, Ctrl-Break kills the program.

Edit :

Old C version was straightforward. In C++ you have to clear flags in cin if getline was interrupted :

#include <iostream>
#include <string>
#include <csignal>

using namespace std;

void ctrl_c(int sig) {
cerr << "Ctrl-C caught" << endl;
signal(sig, ctrl_c); // re-installs handler
}

int main() {
string buf;
void (*old)(int);

old = signal(SIGINT, ctrl_c); // installs handler

for (;;) {
getline(cin, buf);
if (cin.fail() || cin.eof()) {
cin.clear(); // reset cin state
}
else {
cout << "Got" << buf << endl;
}
}
signal(SIGINT, old); // restore initial handler
return 0;
}

It's now correct C++ even if I do not use the Microsoft specific SetConsoleCtrlHandler. IMHO signal usage if more simple if only Ctrl-C has to be caught.

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

Catching terminal terminations/exits with Symfony Console (CTRL+C)

When you do CTRL+C it is actually SIGINT that is being sent, not SIGTERM. The best way I can think of is to register handler with http://php.net/manual/en/function.pcntl-signal.php and dispatch the signal with pcntl_signal_dispatch, example:

pcntl_signal(SIGINT,'sigIntHandler');

function sigIntHandler() {
// Do some stuff
}

Of course you need to adjust it to your needs. Note that you can also defer to class methods inside your commands, so you could for example create an AbstractCommand with generic sigIntHandler() and register it in the constructor:

pcntl_signal(SIGINT, [$this, 'sigIntHandler']);

Then use pcntl_signal_dispatch() for example in the loop of your command (it needs to be called in each iteration).



Related Topics



Leave a reply



Submit