Reputation: 21
I'm having some issues handling the "CTRL-C" signal, what I need is for handle_SIGINT to only output once, and then for it to return to the read in the setup function, but I don't know how to go about this. I was told to add a loop around the length=read call since read is a blocking call. I'm just confused on how to handle these signals. Anyways my current output is:
COMMAND->test
COMMAND->test2
COMMAND->test3
COMMAND->test4
COMMAND->^CFound your CTRL-C
Found your CTRL-C
COMMAND->Found your CTRL-C
COMMAND->Found your CTRL-C
COMMAND->error reading the command: Interrupted system call
^Z
Suspended
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#define MAX_LINE 80 /* 80 chars per line, per command, should be enough. */
void handle_SIGINT()
{
printf("Found your CTRL-C\n");
}
void setup(char inputBuffer[], char *args[],int *background)
{
int length, /* # of characters in the command line */
i, /* loop index for accessing inputBuffer array */
start, /* index where beginning of next command parameter is */
ct; /* index of where to place the next parameter into args[] */
ct = 0;
/* read what the user enters on the command line */
length = read(STDIN_FILENO, inputBuffer, MAX_LINE);
start = -1;
if (length == 0)
exit(0); /* ^d was entered, end of user command stream */
if (length < 0){
perror("error reading the command");
exit(-1); /* terminate with error code of -1 */
}
/* examine every character in the inputBuffer */
for (i = 0; i < length; i++) {
switch (inputBuffer[i]){
case ' ':
case '\t' : /* argument separators */
if(start != -1){
args[ct] = &inputBuffer[start]; /* set up pointer */
ct++;
}
inputBuffer[i] = '\0'; /* add a null char; make a C string */
start = -1;
break;
case '\n': /* should be the final char examined */
if (start != -1){
args[ct] = &inputBuffer[start];
ct++;
}
inputBuffer[i] = '\0';
args[ct] = NULL; /* no more arguments to this command */
break;
case '&':
*background = 1;
inputBuffer[i] = '\0';
break;
default : /* some other character */
if (start == -1)
start = i;
}
}
args[ct] = NULL; /* just in case the input line was > 80 */
}
int main(void)
{
char inputBuffer[MAX_LINE]; /* buffer to hold the command entered */
int background; /* equals 1 if a command is followed by '&' */
char *args[MAX_LINE/2+1];/* command line (of 80) has max of 40 arguments */
struct sigaction handler;
handler.sa_handler = handle_SIGINT;
sigaction(SIGINT, &handler, NULL);
while (1){ /* Program terminates normally inside setup */
background = 0;
printf("COMMAND->");
fflush(0);
setup(inputBuffer, args, &background); /* get next command */
int pid;
pid = fork(); /* for a child process */
if (pid < 0) { /* check if error occurred with child process */
fprintf(stderr, "Fork Failed");
return 1;
} else if (pid == 0) { /* child process */
execvp(args[0], args);
} else {
if (background == 0){ /* check if the parent should wait */
//wait(args);
waitpid(pid, NULL, 0);
}
}
/* the steps are:
(1) fork a child process using fork()
(2) the child process will invoke execvp()
(3) if background == 0, the parent will wait,
otherwise returns to the setup() function. */
}
}
Upvotes: 1
Views: 6139
Reputation: 281
As other guys said above, I just add a while-loop:
while((length = read(STDIN_FILENO, inputBuffer, MAX_LINE) == -1);
Upvotes: 0
Reputation: 3009
Just as a recommendation, try making your programs as portable as possible, I mean, it seems to me that you can write a big deal of your program with ANSI C.
consider using fgets() instead of read() and don't never rely on behaviors, even if that behavior is well documented as bimda point it out.
void setup(char inputBuffer[], char *args[],int *background)
{
char *ret;
int length; /* # of characters in the command line */
int i; /* loop index for accessing inputBuffer array */
int start=-1; /* index where beginning of next command parameter is */
int ct=0; /* index of where to place the next parameter into args[] */
/* read what the user enters on the command line */
ret = fgets(inputBuffer, MAX_LINE, stdin);
if(!ret)
exit(0); /* ^d was entered, end of user command stream */
/* examine every character in the inputBuffer */
for (i = 0; inputBuffer[i]; i++) {
...
}
...
}
and the C-c part as patater said:
void handle_SIGINT()
{
printf("Found your CTRL-C\n");
signal(SIGINT, SIG_IGN);
}
~$ man signal
maybe it's not what you need, but that was just my humble opinion.
Upvotes: 0
Reputation: 6674
This is how you should've written using sigaction()
:
struct sigaction new_handler,old_handler;
new_handler.sa_handler=handle_SIGINT;
sigemptyset(&new_handler.sa_mask);
new_handler.sa_flags=0;
sigaction(SIGINT,NULL, &old_handler);
if(old_handler.sa_handler != SIG_IGN)
sigaction(SIGINT, &new_handler,NULL);
Upvotes: 0
Reputation: 127
Tell your program to ignore the SIGINT signal in handle_SIGINT after printing your message.
void handle_SIGINT()
{
printf("Found your CTRL-C\n");
signal(SIGINT, SIG_IGN);
}
Upvotes: 1
Reputation: 27581
When read() return -1, check errno
- If it is EINTR
than return to perform read once more.
EINTR
indicates blocking system call (in our case read()) is interrupted by signal, so you can safely return to perform read one more
EINTR The call was interrupted by a signal before any data was read; see signal(7).
From other side, in many cases pressing CTRL+C indicated user wants to terminate program, so quitting the program is expected behavior.
Upvotes: 3