1

I am attempting to write a program that will set an alarm for 10 seconds, block the SIGINT after that 10 seconds, set another alarm for 10 seconds, unblock and ignore SIGINT after that, set another alarm for 10 seconds then terminate. I was hoping I could just do all of the operations within an sa_handler for SIGALRM but it isn't working as I had hoped. Is it at all possible to do this? Edit: when in the sa-handler of SIGALRM i am unable to set the SIGINT to blocked.

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

int ctrlct = 0;
int alrmct = 0;

void catchctrlc(int signo)
{
  printf("Caught CTRL-C! <%d>\n", ++ctrlct);
}

void catchalrm(int signo)
{
  sigset_t alrmset;
  switch(alrmct)
  {
    case 0:
      printf("Alarm 1\n");
      if( (sigemptyset(&alrmset) == -1) || (sigaddset(&alrmset, SIGINT) == -1) )
        printf("Failed to add SIGINT to the alrmset");
      else if( sigprocmask(SIG_BLOCK, &alrmset, NULL) == -1 )
        printf("Failed to block SIGINT");
      break;
    case 1:
      printf("Alarm 2\n");
      break;
    case 2:
      printf("Alarm 3\n");
      exit(EXIT_SUCCESS);
      break;
    default:
      break;
  }
  alrmct++;
  alarm(10);
}

int main(void)
{
  struct sigaction ctrlact, alrmact;
  ctrlact.sa_handler = catchctrlc;
  ctrlact.sa_flags = 0;
  alrmact.sa_handler = catchalrm;
  alrmact.sa_flags = 0;

  /* Install signal handler for CTRL-C */
  if((sigemptyset(&ctrlact.sa_mask) == -1) || (sigaction(SIGINT, &ctrlact, NULL) == -1))
    perror("Failed to set sa_handler for SIGINT");
  /* Install signal handler for SIGALRM */
  if((sigemptyset(&alrmact.sa_mask) == -1) || (sigaction(SIGALRM, &alrmact, NULL)== -1))
    perror("Failed to set sa_handler for SIGALRM");

  alarm(10);
  while(1){}

  return 0;
}
Cheeki
  • 75
  • 4
  • Don't use a busy loop (`while (1) {}`) use `pause()`. It will return with `errno` set to `EINTR` when it is interrupted by a signal. Maybe put a loop around `pause()`. – rveerd Mar 09 '21 at 20:03
  • 2
    There is only a limited set of functions that you can safely call from a signal handler. `printf()` definitely **is not** one of those functions. See [signal-safety(7)](https://man7.org/linux/man-pages/man7/signal-safety.7.html). – rveerd Mar 09 '21 at 20:04
  • 1
    `it isn't working as I had hoped` So how is it working? Is something wrong with the code you posted? What is it? – KamilCuk Mar 09 '21 at 20:14
  • See [How to avoid using `printf()` in a signal handler?](https://stackoverflow.com/q/16891019/16158) – Jonathan Leffler Mar 09 '21 at 20:32
  • You should ensure that the `sa_mask` fields of the two `struct sigaction` variables are properly set. – Jonathan Leffler Mar 09 '21 at 20:33

1 Answers1

1

When a signal is delivered, the kernel saves the ->blocked signals into the signal frame, adds the signal delivered to the ->blocked set (unless SA_NODEFER is specified), and upon returning from handling the signal, it restores the ->blocked set to the one from the signal frame, thus overriding any changes done.

If such behavior is required, we can overcome it by editing the saved signal on the signal frame (which is on the user thread's stack). For that we need to specify the SA_SIGINFO flag for the alarm handler, and then:

void catchalrm(int signo, siginfo_t * info, ucontext_t * context)
{

    sigaddset(&context->uc_sigmask, SIGINT);
    ...
}

such than upon returning from the signal, the ->blocking set restored will contain SIGINT.

Please note that this behavior can be linux and architecture specific.

Semion
  • 126
  • 4