0

I want to write some code that takes input from stdin, and prints the input on the next line, until a signal is sent, the signal being SIGINT in this case. So far I have this code which just prints a message when SIGINT is sent:

#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <stdbool.h>
#include <time.h>

bool signalSent = false;    

void flag(int signal) { 
    signalSent = true;
}

int main(int argc, char** argv) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = flag;   
    sa.sa_flags = SA_RESTART;
    sigaction(SIGINT, &sa, 0);
    while (true) {
        while (!signalSent) {
            usleep(500000);
        }
        printf("signal sent\n");
    signalSent = false;
    }
    return 0;
}

I tried using fgets() to get the input from stdin and print to stdout, but when I enter ^C (SIGINT) I have to press enter, but I want it to send the message ("signal sent\n"); as soon as it is pressed like the code I currently have. Code using fgets():

#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <stdbool.h>
#include <time.h>

bool signalSent = false;    

void flag(int signal) { 
    signalSent = true;
}

int main(int argc, char** argv) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = flag;   
    sa.sa_flags = SA_RESTART;
    sigaction(SIGINT, &sa, 0);
    while (true) {
        while (!signalSent) {
            char buffer[80];
            char* t = fgets(buffer, sizeof(buffer), stdin);
            printf("%s", t);
            fflush(stdout);

        }
        printf("signal sent\n");
    signalSent = false;
    }
    return 0;
}

Output from code:

enter image description here

For reference I want my output to look like the picture below. Im very new to using signals in C as well.

enter image description here

Mgert33
  • 83
  • 10

1 Answers1

0

As detailed in the manual pages for sigaction(2) and signal(7), the SA_RESTART flag causes certain system calls, including read(2), to automatically restart after the signal handler has ended.

It seems that you do not want this behaviour.

fgets is implemented by way of read, and can fail for the same reasons it can. Without SA_RESTART, while waiting on input from a "slow" device (this includes terminals), these functions will set errno to EINTR if interrupted by a signal.

Note that what can and cannot be done in a signal handler is complicated.

Consider using sig_atomic_t objects, declared as volatile, for these types of flags.

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

static volatile sig_atomic_t signal_sent = 0;

void flag(int signal) {
    (void) signal;
    signal_sent = 1;
}

int main(void) {
    struct sigaction sa = { .sa_handler = flag };

    sigaction(SIGINT, &sa, 0);

    while (!signal_sent) {
        char buffer[80];

        if (fgets(buffer, sizeof buffer, stdin))
            printf("%s", buffer);
        else if (errno == EINTR && signal_sent) {
            puts("signal sent");
            signal_sent = 0;
        } else
            return 1;
    }
}

Example of running this program (^D to terminate):

 $ ./a.out
foo
foo
^Csignal sent
^Csignal sent
bar
bar
hello wor^Csignal sent
Oka
  • 23,367
  • 6
  • 42
  • 53