简体   繁体   中英

configuring handler for SIGTSTP signal to display a message but not terminate the current process

I have this very basic shell program that just keeps prompting for an input until user presses control+c.

I am trying to configure the SIGTSTP signal's handler function to display a message and not terminate the program. (SIGTSTP is triggered by control+z and by default terminates the program and all child processes.)

The problem: As soon as I press control+z, which triggers the SIGTSTP signal, the program crashes.

Below is my code which includes my basic shell program and my attempt to define the custom handler function for SIGTSTP.

Thank you for any help and suggestions!

#define _GNU_SOURCE
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdbool.h>
#include <sys/types.h>
#include <signal.h>

void handle_SIGTSTP(int signo){
   char* message = "\ncontrol + z pressed\n";
   write(STDOUT_FILENO, message, 21);
   fflush(stdout);
};

int main() {
   struct sigaction SIGTSTP_custom = {0};
   SIGTSTP_custom.sa_handler = handle_SIGTSTP;
   sigaction(SIGTSTP, &SIGTSTP_custom, NULL);
   while(true) {
      char *user_input = malloc(200);
      memset(user_input, '\0', 200);
      size_t max_input_size = 199;
      printf("enter input: ");
      fflush(stdout);
      getline(&user_input, &max_input_size, stdin);
   };
   return 0;
};

You need to pass the SA_RESTART flag to the sigaction structure:

...
   struct sigaction SIGTSTP_custom = {0};
   SIGTSTP_custom.sa_flags |= SA_RESTART; // add this
   SIGTSTP_custom.sa_handler = handle_SIGTSTP;
   sigaction(SIGTSTP, &SIGTSTP_custom, NULL);
...

The behavior of the signal handler will depend on those flags, information on all flags is available on the man page (man sigaction).

Pablo found your main bug: you need to set the SA_RESTART flag to have interrupted system calls restarted.

When SIGTSTP arrives, your program is presumably waiting for I/O in getline , with a system call such as read(2) . Without SA_RESTART , this system call fails when the signal arrives, and sets errno to EINTR . The failure of read() causes getline to set the error flag on stdin , which makes all successive getline calls fail immediately, and puts you in an infinite loop of printf . If you had checked the return value from getline() and done proper error reporting, it would have helped you find this.

The infinite loop by itself wouldn't make your program crash, but you have another bug: you allocate memory on every iteration of the loop, and then leak it. You really want to declare and initialize user_input and max_input_size outside your loop. You also don't need to allocate memory yourself; you can let getline() do it the first time by initializing to NULL and 0. The usual idiom for getline would look like:

char *user_input = NULL;
size_t max_input_size = 0;
while (getline(&user_input, &max_input_size, stdin) != -1) {
    // do stuff with user_input
}
// either error or EOF, handle appropriately
free(user_input);

And one more bug: it's not safe to call fflush or other stdio functions in a signal handler. It's also not necessary because the write() doesn't go through the stdio buffers anyway.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM