Reputation: 89
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
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