user3717434
user3717434

Reputation: 225

signal handler to terminate the main program and all forks when arrives a double SIGINT

I really didn't get how signal handlers work especially with forks. So i need to do this exercise but i couldn't get it work properly.

My main program makes 5 forks, each fork prints simply 10 messages with its pid. So the purpose of the program, when i send a SIGINT signal via keyboard(Ctrl-c) it should print, "a single SIGINT arrived", if two SIGINT arrives between one second, it should print "double SIGINT arrived" and should terminate the whole program. So when i launch my program, it handles first two SIGINT(that i send the second more than 1 second after the first one) but then it doesn't handle single SIGINT and neither double SIGINT.

So i'm very confused about signals. Forks continue to stamp messages. I load same handler both to main and to forks but what should i do to terminate all forks when arrives double SIGINT? Should i call killl or some other function in handler to terminate them?

the main function

 /* libraries... */

 volatile sig_atomic_t double_sigint = 0;
 int64_t time_diff = 0;

 int main()
 {
  int i;
  int pid;

  sigset_t set;
  struct sigaction sa;


         /* mask all signals */ 
 /*H*/   if(sigfillset(&set) == -1 )
 /*A*/     {perror("sigfillset"); exit(errno);} 
 /*N*/  
 /*D*/   if(sigprocmask(SIG_SETMASK,&set,NULL) == -1)
 /*L*/     {perror("sigfillset"); exit(errno);} 
 /*E*/       
 /*R*/   memset(&sa,0,sizeof(sa));
 /*B*/   
 /*L*/   sa.sa_handler = handler;
 /*O*/   
 /*C*/   if(sigaction(SIGINT, &sa, NULL) == -1)
 /*K*/         {perror("sigaction"); exit(errno);}
 /**/   
 /**/    /* unmask all signals */
 /**/    if( sigemptyset(&set) == -1 )
 /**/      {perror("sigepmtyset"); exit(errno);}
 /**/   
 /**/    if(sigprocmask(SIG_SETMASK,&set,NULL) == -1 )
 /**/        {perror("sigprocmask"); exit(errno);}



  for(i=0;i<5;++i)
  {
    if((pid = fork()) == -1)
      { perror("rec:fork"); exit(errno); }

    if(pid == 0)/* figlio */
    {

      /* SAME HANDLER BLOCK IS HERE */        

      foo(i);

      return;

    }
    sleep(1);
  }

  return 0;
 }

foo function

 void foo(int i)
{
  int k;

  for(k=0; k<10; ++k)
  {
    printf("%d. fork %d. print\n", i, k);
    sleep(1);
  }
}

signal handler

  void handler (int signum) {

  struct timespec sig1;
  struct timespec sig2;

  if(double_sigint == 0)
  {
    if(clock_gettime(CLOCK_REALTIME, &sig1))
      { perror("failed to get sig1 time"); exit(errno); }

    write(1,"Received single SIGINT\n",18);

    double_sigint = 1;

  }
  else if(double_sigint == 1)
  {
     if(clock_gettime(CLOCK_REALTIME, &sig2))
       { perror("failed to get sig2 time"); exit(errno); }

     time_diff = (sig2.tv_sec - sig1.tv_sec) + (sig2.tv_nsec - sig1.tv_nsec)/1000000000;

     if(time_diff < 1)
     {
       double_sigint = 2;  
       write(1,"Received double SIGINT\n",18);
       _exit(EXIT_FAILURE);

     }
     else
     {
      sig1.tv_sec = sig2.tv_sec;
      sig1.tv_nsec = sig2.tv_nsec;

      write(1,"Received single SIGINT\n",18);

     }

  }

}

Upvotes: 0

Views: 1539

Answers (2)

William Pursell
William Pursell

Reputation: 212248

In the handler, in the else clause (double_sigint == 1) you are comparing sig2 and sig1, but sig1 is uninitialized. The value that you gave it the first time the handler was called went away when that handler returned. You could simply give those variables file scope.

By using the uninitialized value of the local variable, you are getting undefined behavior. If the signal handler is called and the signal handling stack happens to be in the same state it was on the previous call, then things may work fine. This can happen if you send the signal twice with no intervening signals, for example. Since sleep is likely implemented with a signal, it is quite likely that the stack has been modified since the previous call and sig1 is not what you expect. However, speculation about undefined behavior is somewhat pointless.

Upvotes: 0

BlackDwarf
BlackDwarf

Reputation: 2070

When you receive a double-SIGINT, you only kill the parent process, with the line _exit(EXIT_FAILURE);. The forks you have created before are not killed and keep running, their parent now being the init process.

If you want all the children to terminate, you have to kill them manually. Maybe this post would be helpful : How to make child process die after parent exits

Edit: That was not the problem since Ctrl+C sends a SIGINT to all the children (see comments). What worked for me was :

  • As said in William Pursell's comment, make sig1 and sig2 global variables.
  • Make the parent process always run (just added a while (1); before the return statement), because some signals were not taken into account once the parent process was terminated.

Upvotes: 0

Related Questions