Aaron Li
Aaron Li

Reputation: 89

C: writing a bash shell, how to mimic bash shell behavior for SIGINT

When receiving a SIGINT signal, the bash shell quits the line the user is currently writing, and prints out a new prompt, but it doens't exit. How can I mimic that behavior? I'm writing my own shell and I want the shell to continue running on SIGINT. I'm not sure how to properly terminate the previous fget from reading from stdin and then print out a new prompt:

my code:

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

char *get_args()
{
    char *args = calloc(sizeof(char), 1024);
    fgets(args, 1023, stdin);
    char *pter = strrchr(args, '\n');
    if (pter)
        *pter = 0;
    return args;
}

void sigintHandler(int sig_num)
{
    // Reset handler to catch SIGINT next time.
    signal(SIGINT, sigintHandler);
    printf("Received ctrl c\n");
}

int main(int argc, char *argv[])
{
    char host_name[64]; // hostname
    gethostname(host_name, 64);
    signal(SIGINT, sigintHandler);
    while (1)
    {
        if (isatty(STDIN_FILENO))
        {
            printf("[%s]$ ", host_name);
            char *line = get_args();
            printf("\nline: %s\n", line);
        }
    }
    return 0;
};

How can I terminate fgets from running after SIGINT so that the prompt (hostname) can be printed again?

Upvotes: 2

Views: 213

Answers (1)

Aaron Li
Aaron Li

Reputation: 89

Answer with setlongjmp and sigsetjmp: After some research, it seemed the easiest way (least lines of code) to cancel an existing fgets, scanf, etc call that's waiting to read from stdin after a SIGINT interrupt is to use a siglongjmp statement.

Using sigsetjmp, you can declare a jump location (in my case it is in main, so that my infinite while loop can print the prompt again. Then, inside the SIGINT handler, you can call siglongjmp.

Relevant code:

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

// initialize marker for jump
sigjmp_buf mark;

// handler for SIGINT creates another SIGINT handler
// for the next SIGINT interrupt, and jumps back
// to infinite lopp
void sigintHandler(int sig_num)
{
    // Reset handler to catch SIGINT next time.
    signal(SIGINT, sigintHandler);
    // jump back to main to abort current stdin from fgets
    siglongjmp(mark, 1);
}

int main(int argc, char *argv[])
{
    // signal handler for SIGINT
    signal(SIGINT, sigintHandler);
    // set the marker to this point in main
    sigsetjmp(mark, 1);

    while (1)
    {
        if (isatty(STDIN_FILENO))
        {
            // some code that prints the prompt in the form
            // of username@hostname:cwd$ 
        }
    }
    return 0;
};

Upvotes: 1

Related Questions