1
votes

I am currently building a shell program in c. I am required to use signals to capture the CTRL-C signal and output "Use exit" to ask the user to use my built-in command exit to leave myshell. I have added the signal handler to output "Use exit", but is not able to cause my shell to output [/] > after "Use exit".

Expected outcome:

[assignment2] > ^C
Use exit
[assignment2] > ^C
Use exit
[assignment2] > ^C
Use exit
[assignment2] > exit

However, the output of my program did not have the [assignment2] > output after each ^C Use exit, I would have to press enter on my keyboard for [assignment2] > to show up:

[assignment2] > ^C
Use exit
^C
Use exit
^C
Use exit
[assignment2] > exit

I suspect that after handling the signal, the program is still stuck in the fgets(cmdline, MAXLINE,stdin) so that the program doesn't continue to the beginning of the while loop to print out [assignment2] > as shown below in my code:

My code for myshell and handle signal function:

// Signal handler funciton
void handler(int signal){
   switch(signal) {
      case SIGINT:
        write(STDOUT_FILENO, "\nUse exit\n", 10);
        break;
      case SIGTSTP:
        write(STDOUT_FILENO, "\nUse exit\n", 10);
        break;
      case SIGQUIT:
        write(STDOUT_FILENO, "\nUse exit\n", 10);
        break;
}

int main(){
   // Add signal handler
   if (signal(SIGINT, handler) == SIG_ERR) {
      perror("signal");
    } else if (signal(SIGTSTP, handler) == SIG_ERR){
      perror("signal");
    } else if (signal(SIGQUIT, handler) == SIG_ERR){
      perror("signal");
    }

    // ...... init cmdline and cwd char

    while(1){
       char dir[MAXPATH];
       getcwd(cwd, sizeof(cwd));
       printf("[%s] > ", parsecwd(cwd));

       if (get_cmd_line(cmdline) == -1) // The program is stuck inside this funciton
            continue; /* empty line handling */

        eval(cmdline); /* Evaluate and process the cmd input*/
    }
}


int get_cmd_line(char *cmdline)
{
    int i;
    int n;
    if (!fgets(cmdline, MAXLINE, stdin))  // The program is stuck in this fgets
        return -1;
    // Ignore the newline character
    n = strlen(cmdline);
    cmdline[--n] = '\0';
    i = 0;
    while (i < n && cmdline[i] == ' ') {
        ++i;
    }
    if (i == n) {
        // Empty command
        return -1;
    }
    return 0;
}

I have tried to add a write(STDIN_FILENO, "\n", 1) to signal handler as such to try and pass new line to fgets(cmdline, MAXLINE, stdin) so that it would return a -1 to continue to the start of the while loop:

switch(signal) {
      case SIGINT:
        write(STDOUT_FILENO, "\nUse exit\n", 10);
        write(STDIN_FILENO, "\n", 1);
        break;
      case SIGTSTP:
        write(STDOUT_FILENO, "\nUse exit\n", 10);
        write(STDIN_FILENO, "\n", 1)
        break;
      ....

But this does not solve the problem, it just print out a new line in the terminal.

Any idea how to fix this problem? Thank You.

1

1 Answers

3
votes

The standard library (glibc) implementation of signal uses BSD semantics, which means that system-calls will be "restarted" (instead of failing) when a signal occurs.

This behavior makes e.g. fgets not return when the signal handler has finished.

To control this behavior use the recommended sigaction function instead. More specifically make sure that the SA_RESTART flag is not set:

struct sigaction action = { 0 };  // Initialize all members to zero or null

action.sa_handler = &handler;
sigaction(SIGINT, &action, NULL);

This way, the fgets function will return with a NULL pointer and errno should be equal to EINTR.