ZODIACK
ZODIACK

Reputation: 23

SIGINT signal will stop client application during scanf/fgets

I have a Client-Server application written in C language. This is not a professional poject, so everything I do is from command line (ubuntu terminal). Whenever the client code requires an input from the user, I use an fgets to take said input. The problem is that if the user wants to stop the input process and exit the server by sending a CTRL+C signal (SIGINT), this won't work.

Specifically, what happens is this:

  1. Client code asks for input and uses fgets to get one;
  2. User presses CTRL+C while the client code is waiting for input;
  3. Client code won't stop (if it stops, then the client code will send a specific kill signal to the server and shut the connection down);
  4. Client will print on screen ^C;
  5. If you try to press anything else, client code will just display it on the screen while you do the input, but if you press ENTER you won't send anything to the Server.

Therefore, the connection is stuck and the client is forever stopped in this input operation.

I don't know how to fix this. What i want is my client code to ask for input, if the user presses CTRL+C then the client code MUST stop and send such kill signal to the server. How can I do that?

I also tried with scanf like this:

   while ((scanf("%s", buff)) == -1) EINTR_ABORT("Errore di scanf");
     getchar();

(I know I should never use scanf, but for some reason my professor is fine with it -and I know is kinda sad but whatever-)

This is my fgets:

if(fgets(buff_ID, sizeof(buff_ID), stdin)== NULL)
   EINTR_ABORT("Errore di fgets"); 

Where this is EINTR_ABORT:

#define ISTANT_ABORT(warning) ({ \
    fprintf(stderr, "%s: %s.\n", warning, strerror(errno)); \
    exit(EXIT_FAILURE);\
})

#define EINTR_ABORT(warning) ({ \
    if(errno != EINTR) \
        ISTANT_ABORT(warning); \
})

My professor never pointed this issue out, but he (obviously, because why not after all) expects us to solve it on our own in this project. I tried my best and never worked out anything that would actually fix the issue.

Upvotes: 0

Views: 514

Answers (1)

dimich
dimich

Reputation: 1843

You can handle INT signal and set a flag signalling that user interrupted input.

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

static sig_atomic_t interrupted = 0;
static void sigint_handler(int)
{
    interrupted = 1;
}

int main(void)
{
    struct sigaction act;

    act.sa_handler = sigint_handler; // Set new SIGINT handler
    sigemptyset(&act.sa_mask);       // No need to mask signals in handler
    act.sa_flags = 0;                // Don't set SA_RESTART flag

    if (sigaction(SIGINT, &act, NULL) != 0)
        perror("sigaction");

    char str[256];
    char *ret = fgets(str, sizeof(str), stdin);

    if (interrupted)
    {
        // Handle interrupt here
        printf("Interrupted!\n");

    } else if (ret)
    {
        // Handle fgets result here
    }

    return 0;
}

Note: don't use signal() function for setting up signal handler because on Linux it applies SA_RESTART flag.

Upvotes: 1

Related Questions