Ctrl-C eaten by getchar() - c

I've been searching for a solution to my problem for a long time now that's why i'm turning to you:
Consider this piece of code:
static char done = 0;
static void sigHandler(void)
{
done = 1;
}
int user_input()
{
return (getchar() == 'q') ? 0 : 1;
}
int main(void)
{
signal(SIGTERM, sigHandler);
signal(SIGINT, sigHandler);
while (user_input() != 0 && !done)
usleep(1000);
printf("exiting\n");
return 0;
}
Expected behavior:
The program exits when user inputs q then enter. If CTRL+C is pressed, it is caught by the sigHandler function which sets the flag 'done' to 1 and exits the program.
Observed behavior:
The CTRL+C character is eaten by the getchar() call, and the sigHandler function is never executed. When CTRL+C and then enter is pressed, the sigHandler function is called and the program exits.
Could someone with more experience and knowledge help me on that one?
Thanks for your input :)

There IS a way to abort the call without resorting to ugly hacks (contrarily to what Paul R said). You should use sigaction() with sa_flags set to 0 instead of signal().
Besides, the signal(2) manual says:
Avoid its use: use sigaction(2) instead.
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
static char done = 0;
static void sigHandler(int signum)
{
done = 1;
}
int user_input()
{
return (getchar() == 'q') ? 0 : 1;
}
int main(void)
{
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = sigHandler;
sa.sa_flags = 0;// not SA_RESTART!;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
while (user_input() != 0 && !done)
usleep(1000);
printf("exiting\n");
return 0;
}
Normally, after catching and handling a signal, most (I'm not sure if not all) syscalls will be restarted. This way, after handling the sigint signal, your getchar function will continue as if nothing happened.
You can change this behavior by calling sigaction with sa_flags=0.
This way, after handling SIGINT, getchar will return -1 and errno will be set to "Interrupted system call" (I don't remember the constant name right now).
You would also have to rewrite your user_input() function to handle the case when returning -1.

The code is actually working as expected - you are not testing the done flag until after you return from user_input(), which is why you need to enter an additional character after the control-C.
If you want to abort the call to getchar when you get a control-C then you'll probably have to do something ugly, e.g. use setjmp/longjmp.

The Ctrl-C character is eaten by the getchar() call, and the sigHandler function is never executed.
Ctrl-C is not eaten by getchar; it results in a signal being delivered and sigHandler being run. This sets done and returns. Only then is getchar called, which eats the newline and after that, done is checked so the program exits.
Btw., a signal handler takes an int argument, not void.

Related

Reading everything currently entered in stdin

I want to read everything that is on stdin after 10 seconds and then break. The code I've been able to write so far is:
#include <stdio.h>
#include <stdlib.h>
int main() {
sleep(10);
char c;
while (1) { // My goal is to modify this while statement to break after it has read everything.
c = getchar();
putchar(c);
}
printf("Everything has been read from stdin");
}
So when the letter "c" is entered before the 10 seconds have elapsed, it should print "c" (after sleep is done) and then "Everything has been read from stdin".
So far I have tried:
Checking if c is EOF -> getchar and similar functions never return EOF for stdin
Using a stat-type function on stdin -> stat-ing stdin always returns 0 for size (st_size).
Here's an offering that meets my interpretation of your requirements:
The program reads whatever data is typed (or otherwise entered) on standard input in a period of 10 seconds (stopping if you manage to enter 2047 characters — which would probably mean that the input is coming from a file or a pipe).
After 10 seconds, it prints whatever it has collected.
The alarm() call sets an alarm for an integral number of seconds hence, and the system generates a SIGALRM signal when the time is up. The alarm signal interrupts the read() system call, even if no data has been read.
The program stops without printing on receiving signals.
If the signal is one of SIGINT, SIGQUIT, SIGHUP, SIGPIPE, or SIGTERM, it stops without printing anything.
It fiddles with the terminal settings so that the input is unbuffered. This avoids it hanging around. It also ensures that system calls do not restart after a signal is received. That may not matter on Linux; using signal() on macOS Big Sur 11.7.1, the input continued after the alarm signal, which was not helpful — using sigaction() gives you better control.
It does its best to ensure that the terminal mode is restored on exit, but if you send an inappropriate signal (not one of those in the list above, or SIGALRM), you will have a terminal in non-canonical (raw) mode. That leads to confusion, in general.
It is easy to modify the program so that:
input is not echoed by the terminal driver;
characters are echoed by the program as they arrive (but beware of editing characters);
signals are not generated by the keyboard;
so it doesn't futz with standard input terminal attributes if it is not a terminal.
Code
/* SO 7450-7966 */
#include <ctype.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#undef sigemptyset /* MacOS has a stupid macro that triggers -Wunused-value */
static struct termios sane;
static void stty_sane(void)
{
tcsetattr(STDIN_FILENO, TCSANOW, &sane);
}
static void stty_raw(void)
{
tcgetattr(STDIN_FILENO, &sane);
struct termios copy = sane;
copy.c_lflag &= ~ICANON;
tcsetattr(STDIN_FILENO, TCSANOW, &copy);
}
static volatile sig_atomic_t alarm_recvd = 0;
static void alarm_handler(int signum)
{
signal(signum, SIG_IGN);
alarm_recvd = 1;
}
static void other_handler(int signum)
{
signal(signum, SIG_IGN);
stty_sane();
exit(128 + signum);
}
static int getch(void)
{
char c;
if (read(STDIN_FILENO, &c, 1) == 1)
return (unsigned char)c;
return EOF;
}
static void set_handler(int signum, void (*handler)(int signum))
{
struct sigaction sa = { 0 };
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0; /* No SA_RESTART! */
if (sigaction(signum, &sa, NULL) != 0)
{
perror("sigaction");
exit(EXIT_FAILURE);
}
}
static void dump_string(const char *tag, const char *buffer)
{
printf("\n%s [", tag);
int c;
while ((c = (unsigned char)*buffer++) != '\0')
{
if (isprint(c) || isspace(c))
putchar(c);
else
printf("\\x%.2X", c);
}
printf("]\n");
}
int main(void)
{
char buffer[2048];
stty_raw();
atexit(stty_sane);
set_handler(SIGALRM, alarm_handler);
set_handler(SIGHUP, other_handler);
set_handler(SIGINT, other_handler);
set_handler(SIGQUIT, other_handler);
set_handler(SIGPIPE, other_handler);
set_handler(SIGTERM, other_handler);
alarm(10);
size_t i = 0;
int c;
while (i < sizeof(buffer) - 1 && !alarm_recvd && (c = getch()) != EOF)
{
if (c == sane.c_cc[VEOF])
break;
if (c == sane.c_cc[VERASE])
{
if (i > 0)
i--;
}
else
buffer[i++] = c;
}
buffer[i] = '\0';
dump_string("Data", buffer);
return 0;
}
Compilation:
gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -fno-common tensec53.c -o tensec53
No errors (or warnings, but warnings are converted to errors).
Analysis
The #undef line removes any macro definition of sigemptyset() leaving the compiler calling an actual function. The C standard requires this to work (§7.1.4 ¶1). On macOS, the macro is #define sigemptyset(set) (*(set) = 0, 0) and GCC complains, not unreasonably, about the "right-hand operand of comma expression has no effect". The alternative way of fixing that warning is to test the return value from sigemptyset(), but that's arguably sillier than the macro. (Yes, I'm disgruntled about this!)
The sane variable records the value of the terminal attributes when the program starts — it is set by calling tcgetattr() in stty_raw(). The code ensures that sane is set before activating any code that will call sttr_sane().
The stty_sane() function resets the terminal attributes to the sane state that was in effect when the program started. It is used by atexit() and also by the signal handlers.
The stty_raw() function gets the original terminal attributes, makes a copy of them, modifies the copy to turn off canonical processing (see Canonical vs non-canonical terminal input for more details), and sets the revised terminal attributes.
Standard C says you can't do much in a signal handler function than set a volatile sig_atomic_t variable, call signal() with the signal number, or call one of the exit functions. POSIX is a lot more gracious — see How to avoid using printf() in a signal handler? for more details.
There are two signal handlers, one for SIGALRM and one for the other signals that are trapped.
The alarm_handler() ignores further alarm signals and records that it was invoked.
The other_handler() ignores further signals of the same type, resets the terminal attributes to the sane state, and exits with a status used to report that a program was terminated by a signal (see POSIX shell Exit status for commands).
The getch() function reads a single character from standard input, mapping failures to EOF. The cast ensures that the return value is positive like getchar() does.
The set_handler() function uses sigaction() to set the signal handling. Using signal() in the signal handlers is a little lazy, but adequate. It ensures that the SA_RESTART bit is not set, so that when a signal interrupts a system call, it returns with an error rather than continuing.
The dump_string() function writes out a string with any non-printable characters other than space characters reported as a hex escape.
The main() function sets up the terminal, ensures that the terminal state is reset on exit (atexit() and the calls to set_handler() with the other_handler argument), and sets an alarm for 10 seconds hence.
The reading loop avoids buffer overflows and stops when the alarm is received or EOF (error) is detected.
Because canonical processing is turned off, there is no line editing. The body of the loop provides primitive line editing — it recognizes the erase (usually backspace '\b', sometimes delete '\177') character and the EOF character and handles them appropriately, otherwise adding the input to the buffer.
When the loop exits, usually because the alarm went off, it null terminates the string and then calls dump_string() to print what was entered.
If you wanted sub-second intervals, you would need to use the POSIX timer_create(), timer_delete(), timer_settime() (and maybe timer_gettime() and timer_getoverrun()) functions, which take struct timespec values for the time values. If they're not available, you might use the obsolescent setitimer() and getitimer() functions instead. The timer_create() step allows you to specify which signal will be sent when the timer expires — unlike alarm() and setitimer() which both send pre-determined signals.
POSIX functions and headers:
Functions
Functions
Headers
alarm()
sigaction()
<ctype.h>
atexit()
sigemptyset()
<signal.h>
exit()
signal()
<stdio.h>
getitimer()
tcgetattr()
<stdlib.h>
isprint()
tcsetattr()
<sys/time.h>
isspace()
timer_create()
<termios.h>
perror()
timer_delete()
<time.h>
printf()
timer_getoverrun()
<unistd.h>
putchar()
timer_gettime()
read()
timer_settime()
setitimer()
You can use the select function to wait to see if there is something to read on stdin with a timeout that starts at 10 seconds. When it detects something, you read a character and check for errors or EOF. If all is good, then you call select again, reducing the timeout by the elapsed time so far.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <time.h>
struct timeval tdiff(struct timeval t2, struct timeval t1)
{
struct timeval result;
result.tv_sec = t2.tv_sec - t1.tv_sec;
result.tv_usec = t2.tv_usec - t1.tv_usec;
while (result.tv_usec < 0) {
result.tv_usec += 1000000;
result.tv_sec--;
}
return result;
}
int cmptimestamp(struct timeval t1, struct timeval t2)
{
if (t1.tv_sec > t2.tv_sec) {
return 1;
} else if (t1.tv_sec < t2.tv_sec) {
return -1;
} else if (t1.tv_usec > t2.tv_usec) {
return 1;
} else if (t1.tv_usec < t2.tv_usec) {
return -1;
} else {
return 0;
}
}
int main()
{
struct timeval cur, end, delay;
int rval, len = 0;
fd_set fds;
gettimeofday(&cur, NULL);
end = cur;
end.tv_sec += 10;
FD_ZERO(&fds);
FD_SET(0, &fds);
if (fcntl(0, F_SETFL, O_NONBLOCK) == -1) {
perror("fcntl failed");
exit(1);
}
do {
delay = tdiff(end, cur);
rval = select(1, &fds, NULL, NULL, &delay);
if (rval == -1) {
perror("select failed");
} else if (rval) {
char c;
len = read(0, &c, 1);
if (len == -1) {
perror("read failed");
} else if (len > 0) {
printf("c=%c (%d)\n", c, c);
} else {
printf("EOF\n");
}
} else {
printf("timeout\n");
}
gettimeofday(&cur, NULL);
} while (rval > 0 && len > 0 && cmptimestamp(end,cur) > 0);
return 0;
}
Note that this doesn't detect the keys as you press them, only after you've either pressed RETURN or stdin is closed.

getline(3) keeps failing on loop after receiving signal SIGINT (Ctrl+C)

When getline() is waiting for user input, upon receiving the signal SIGINT it returns an error. However, in the next calls to getline(), it keeps returning the error, even though no other signal has been sent.
Here is a minimalized example of the problem:
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
static void sig_handler(int signo) {
fprintf(stderr, "SIGINT DETECTED\n");
}
int main(void) {
struct sigaction sa;
sigset_t smask;
sigemptyset(&smask);
sa.sa_handler = sig_handler;
sa.sa_mask = smask;
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
while (1) {
printf("Write something.\n");
char *line = NULL;
size_t buflen = 0;
int res = getline(&line, &buflen, stdin);
if (res == -1) {
perror("getline()");
free(line);
continue;
}
printf("%s", line);
free(line);
break;
}
return 0;
}
I would expect that getline() would fail after receiving the signal (the program would print the error) but when the loop restarts it would ask again "Write something." and wait for the user input.
However, after a single SIGINT signal, the getline() will always fail (it doesn't wait for user input) and the program will be stuck on an infinite loop:
Write something.
getline(): Interrupted system call
Write something.
getline(): Interrupted system call
Write something.
getline(): Interrupted system call
Write something.
getline(): Interrupted system call
...
How can I "clear" the error caused by SIGINT in getline() so that the program doesn't get stuck on an infinite loop?
You practically answered your own question with the phrase "clear the error": you need to call clearerr(stdin) before retrying the getline.
(Unrelated issue: it is generally not safe to call library functions like fprintf inside a signal handler, especially when in this case the handler may run while inside another library function. Standard library functions are not guaranteed to be reentrant unless specifically stated.)
Since you want to resume the read system call, you need to set SA_RESTART flag.
You're currently clearing it:
sa.sa_flags = 0;
Instead, do:
sa.sa_flags = SA_RESTART;

Robust graceful shutdown of an application

To ensure that all destructors are properly called if the program is terminated from keyboard (Ctrl+C), the approach with signals are used:
a handler, which sets an exit flag, is set for SIGINT
if a blocking call (accept(), read(), connect(), etc) is waiting for completion, it returns -1 and errno is set to EINTR
The problem is that SIGINT can arrive between check for exit flag (while (!finish)) and calling read(). In this case, read() will be blocked until the signal is sent once again.
This is a minimal working example:
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
enum { STDIN, STDOUT, STDERR };
static unsigned char finish=0;
static void handleSignal(int signal) {
finish=1;
}
int main(int argc, char ** e) {
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_handler=handleSignal;
action.sa_flags=0;
sigaction(SIGINT, &action, NULL);
char buffer[256];
puts("<<");
while (!finish) {
sleep(2);
ssize_t n=read(STDIN, buffer, sizeof(buffer));
if (n==0) {
// End of stream
finish=1;
}
else if (n<0) {
// Error or interrupt
if (errno!=EINTR)
perror("read");
}
else {
// Convert data to hexadecimal format
for (size_t i=0; i<n; i++)
printf("%02x", buffer[i]);
}
}
puts(">>\n");
return 0;
}
sleep(2) is added for visibility (a real program may perform some preparational work before reading from file descritor).
If there any way of reliable handling of signals without using non-crossplatform things like signalfd()?
The pselect(2) system call was invented to solve this exact problem. It's POSIX, so hopefully cross-platform enough for you.
The purpose of pselect is to atomically unblock some signals, wait for I/O as select() does, and reblock them. So your loop can look something like the following pseudocode:
sigprocmask(SIG_BLOCK, {SIGINT});
while (1) {
if (finish)
graceful_exit();
int ret = pselect(1, {STDIN}, ..., { /* empty signal set */});
if (ret > 0) {
read(STDIN, buf, size); // will not block
// process data
// If you like you can do
sigprocmask(SIG_UNBLOCK, {SIGINT});
// work work work
if (finish)
graceful_exit();
// work work work
sigprocmask(SIG_BLOCK, {SIGINT});
} else {
// handle timeout or other errors
}
}
There is no race here because SIGINT is blocked for the time in between checking the finish flag and the call to pselect, so it cannot be delivered during that window. But the signal is unblocked while pselect is waiting, so if it arrives during that time (or already arrived while it was blocked), pselect will return without further delay. We only call read when pselect has told us it was ready for reading, so it cannot block.
If your program is multithreaded, use pthread_sigmask instead of sigprocmask.
As was noted in comments, you have to make your finish flag volatile, and for best compatibility it should be of type sig_atomic_t.
There is more discussion and another example in the select_tut(2) man page.

How to restore original signal handling properties in C

Tried my best to figure this out on my own, but I really do not want to continue tampering with things that I do not fully understand. So for a programming assignment I have to do in C, I need to terminate a program upon the user entering CTRL+D key stroke via a terminal. I tried to isolate that functionality in a smaller test function, but now my CTRL+D behaves as my CTRL+C and CTRL+C does not have any effect, even outside of the program when it finishes executing. This is the program that caused this change:
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <signal.h>
#include <stdlib.h>
void ctrlD(int sig){
printf("\n");
signal(SIGINT, SIG_DFL);
exit(0);
}
int main(){
signal(SIGINT, ctrlD);
while(1) {
printf("Hello\n");
sleep(5);
}
}
The line signal(SIGINT, SIG_DFL); was added afterward upon realizing my CTRL+C no longer worked. I thought it would return the keystrokes to their original functionalities, but to no avail. What do I do to get back the original functionalities while also making this program work with CTRL+D?
***EDIT: This question seems to have gone off the rails a bit. I get now that Ctrl+D is not a signal. Nonetheless, I no longer have the functionality of Ctrl+C anymore when attempting to use it in my MAC OS terminal, and instead Ctrl+D seems to have that exact functionality. HOW exactly can I return each to have the functionality that they had before I went on this haphazard journey?
If your intention is to restore signal's default behavior after executing handler then, pass SA_RESETHAND flag to sa_flags while registering signal action. For example.
struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
act.sa_flags = SA_RESETHAND;
act.sa_handler = some_handler;
sigaction(SIGINT, &act, NULL);
From sigaction() man
SA_RESETHAND
Restore the signal action to the default upon entry to the signal handler. This flag is meaningful only when
establishing a signal handler.
If you write a program to explore signals, it is much better to write it carefully, using proper POSIX interfaces (sigaction() instead of signal()), and avoiding undefined behaviour (using non-async-signal safe functions in a signal handler).
Consider, for example, the following program:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
static volatile sig_atomic_t sigint_count = 0;
static void catch_sigint(int signum)
{
if (signum == SIGINT)
sigint_count++;
}
static int install_sigint(void)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = catch_sigint;
act.sa_flags = 0;
if (sigaction(SIGINT, &act, NULL) == -1)
return errno;
return 0;
}
static int install_default(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
int main(void)
{
struct timespec duration;
int result;
if (install_sigint()) {
fprintf(stderr, "Cannot install SIGINT handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
duration.tv_sec = 5;
duration.tv_nsec = 0; /* 1/1000000000ths of a second. Nine zeroes. */
printf("Sleeping for %d seconds.\n", (int)duration.tv_sec);
fflush(stdout);
while (1) {
result = nanosleep(&duration, &duration);
if (!result)
break;
if (errno != EINTR) {
fprintf(stderr, "nanosleep() failed: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* nanosleep was interrupted by a delivery of a signal. */
if (sigint_count >= 3) {
/* Ctrl+C pressed three or more times. */
if (install_default(SIGINT) == -1) {
fprintf(stderr, "Cannot revert SIGINT to the default handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
printf("SIGINT has been reverted to the default handler.\n");
fflush(stderr);
}
}
if (sigint_count > 0)
printf("You pressed Ctrl+C %d time%s.\n", (int)sigint_count, (sigint_count > 1) ? "s" : "");
else
printf("You did not press Ctrl+C at all.\n");
return EXIT_SUCCESS;
}
The #define tells your C library (glibc in particular) that you want POSIX.1-2008 (and later) features from it.
The INT signal handler only increments a volatile sig_atomic_t counter. Note that this type may have a very small range it can represent; 0 to 127, inclusive, should be safe.
The main program waits using the POSIX nanosleep() function. On some systems, sleep() may be implemented via the SIGALRM function, so it is better avoided when using signals otherwise; nanosleep() does not interfere with signals like that at all. Plus, nanosleep() can return the amount of time remaining, if it is interrupted by a signal delivery.
In the main loop, nanosleep() will return 0, if it has slept the entire interval (but note that it may not update the remaining time to 0 in this case). If it is interrupted by the delivery of a signal, it will return -1 with errno == EINTR, and the remaining time updated. (The first pointer is to the duration of the sleep, and the second is to where the remaining time should be stored. You can use the same structure for both.)
Normally, the main loop does only one iteration. It can do more than one iteration, if it is interrupted by the delivery of a signal.
When the main loop detects that sigint_count is at least three, i.e. it has received at least three INT signals, it resets the signal handler back to default.
(Note that both the memset() and the sigemptyset() are important when clearing the struct sigaction structure. The memset() ensures that future code is backwards compatible with older code, by ensuring even padding fields are cleared. And sigemptyset() is the safe way to clear the signal mask (set of signals blocked while the handler runs).)
(In theory, memset() is not async-signal-safe, while both sigemptyset() and sigaction() are. This is why I reset the signal handler in the main program, and not in the signal handler.)
If you want to print from a signal handler, you need to use low-level I/O, because <stdio.h> functions are not async-signal safe. For example, you can use the following function to print strings to standard output:
static int wrerr(const char *p)
{
const int saved_errno = errno;
int retval = 0;
if (p) {
const char *q = p;
ssize_t n;
while (*q)
q++;
while (p < q) {
n = write(STDERR_FILENO, p, (size_t)(q - p));
if (n > 0)
p += n;
else
if (n != -1) {
retval = EIO;
break;
} else
if (errno != EINTR) {
retval = errno;
break;
}
}
}
errno = saved_errno;
return retval;
}
The above wrerr() function is async-signal safe (because it only uses async-signal safe functions itself), and it even keeps errno unchanged. (Many guides forget to mention that it is quite important for a signal handler to keep errno unchanged. Otherwise, when a function is interrupted by a signal handler, and that signal handler modifies errno, the original function will return -1 to indicate an error, but then errno is no longer EINTR!)
You can just use wrerr("INT signal!\n") if you want. The return value from wrerr() is zero if the write was successful, and an errno error code otherwise. It ignores interrupts itself.
Do note that you should not mix stderr output via fprintf() or other <stdio.h> functions with the above (except perhaps for printing error messages when the program aborts). Mixing them is not undefined behaviour, it just may yield surprising results, like wrerr() output appearing in the midst of a fprintf(stderr,...) output.
Its because of exit(0) statement in the handler, when SIGINT is raised, handler strlD gets called and you might thinking why signal(SIGINT,SIG_DFL) didn't work ? Actually it works. But your main process a.out get terminated successfully there itself by calling exit(0). remove exit(0) if you want to restore the behavior of SIGINT.
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <signal.h>
#include <stdlib.h>
void ctrlD(int sig){
//printf("CTRL+C pressed\n");/* just to observe I added one printf
statement, Ideally there shouldn't be any printf here */
signal(SIGINT, SIG_DFL);/*restoring back to original action */
}
int main(){
signal(SIGINT, ctrlD);/*1st time when CTRL+C pressed, handler ctrlD gets called */
while(1) {
printf("Hello\n");
sleep(5);
}
return 0;
}
Also its advisable to use sigaction() instead of signal() as told here What is the difference between sigaction and signal? . Read man 2 sigaction and man 2 exit to check what exit(0) means.
Also this How to avoid using printf in a signal handler?
Edit :
void ctrlD(int sig){
/* printf("CTRL+C pressed \n"); */
signal(SIGINT, SIG_DFL); /* only one time CTRL+C works
after that SIG_DFL will terminate whole process */
}
int main(){
signal(SIGINT, ctrlD); /* if you press CTRL+C then it will go to handler
and terminate */
int ch;
while( ((ch = getchar())!=EOF) ) { /* wait or read char until CTrl+D is not pressed */
printf("Hello : %d \n",ch);/* ASCII equivalent of char */
}
return 0;
}
Thank you everyone who contributed to this question. The resources provided/linked were tremendously helpful in learning more about signals (and that EOF isn't a signal), among the other wealth of information provided.
After some more research, I found out that somehow, either through some accidental bash command gone awry, or perhaps the program posted in my original question itself, I had altered the key mappings for my terminal's stty settings. If anyone finds themselves in this oddly specific situation in the future, I hope this can be of help, as it is what fixed my problem:
Enter the command $ stty -a to see all of your terminals settings, specifically the "cchars" section.
I then saw the reversal, and fixed it like so:
$ stty intr ^C
$ stty eof ^D
Then you can run $ stty -a once again to see that the changes have properly taken effect. Once again, thanks everyone.

Catching SIGINT signal to terminate a custom shell

Hope you can help me to resolve this problem.
For school I have to transform Ctrl+C to a command which doesn't shut down the shell, but he reminds through printf() that I must type exit to close the shell. I don't even know where to start.
Thank a lot.
Here's a trivial implementation of handling SIGINT using sigaction which will work on posix systems. Left out error checking for brevity. The linked manual should explain about sigaction.
Basically the program loops through an infinite loop and break if user types exit. Using write as you can't use printf in signal handler. See signal manual for a list of functions that can be used safely in a signal handler.
#include<stdio.h>
#include<signal.h>
#include<string.h>
#include<stdlib.h>
char s[]="Type 'exit' to terminate\n";
void int_handler (int signum)
{
write(fileno(stdin), s, sizeof s - 1);
}
int main (void)
{
char str[256];
struct sigaction sh;
sh.sa_handler = int_handler;
sigemptyset (&sh.sa_mask);
sh.sa_flags = 0;
sigaction (SIGINT, &sh, NULL);
printf("%s", s);
while(1) {
fgets(str, sizeof str, stdin);
char *p = strchr(str, '\n');
if(p) *p = 0;
if(!strcmp(str, "exit")) {
printf("Exiting on request...");
break;
}
}
return 0;
}
As you're talking about doing it from the shell, you probably want:
$ trap "echo Please type \'exit\' to close the shell." SIGINT
<Ctrl-C>
Please type 'exit' to close the shell.
$
This specifies a command to execute when the listed signal is trapped (the trap command can also trap other signals; SIGINT is the one generated by Ctrl-C). The \' protects the quote from being interpreted by the shell.
Ctrl+C sends an interrupt signal (SIGINT) to the running process.You can use signal() to catch SIGINT like this:
#include<stdio.h>
#include<signal.h>
void sigint_handler(int sig)
{
printf("Type exit to close the shell!\n");
}
int main()
{
signal(SIGINT, sigint_handler);
/*Your code should replace the while loop.*/
while(1)
{
printf("Running!\n");
getchar();
}
return 0 ;
}

Resources