João Valente
João Valente

Reputation: 195

Error using sigaction multiple times

I'm trying to call a function everytime my child process dies. This is done by catching the SIGCHLD sent by execve(). My problem here is that my handler function is not always called a second time. Sometimes it is though so I'm a bit confused.

My code is this one:

  #include <stdio.h>
  #include <stdlib.h>
  #include <signal.h> // sigaction()
  #include <time.h>
  #include <errno.h>
  #include <unistd.h>
  #define MAX_LENGTH 1000
  #define BILLION 1E9

  struct timespec start,stop;
  float totalTime = 0;

  void runCommand(int signo)
  {
    printf("Run command called\n");

    float processTime = 0;

    //Get the time when the process ended
    if(clock_gettime(CLOCK_MONOTONIC,&stop) == -1){
      perror(NULL);
    }

    processTime = stop.tv_sec - start.tv_sec + ( stop.tv_nsec - start.tv_nsec ) / BILLION;
    totalTime += processTime;
  }


  int main(int argc, char const *argv[]) {

    int counter = 0;
    int pid = 0;
    int status;
    char *args[4];
    char line[MAX_LENGTH];

    //Setup behaviour for SIGCHLD
    struct sigaction psa;
    psa.sa_handler = runCommand;
    sigaction(SIGCHLD, &psa, NULL);

    FILE *fp = fopen("script_file.txt","r");
    if(!fp)
    {
      return 0;
    }

    //Setup argsgcc
    args[0] = "/bin/sh";
    args[1] = "-c";
    args[3] = NULL;

    while(fgets(line, MAX_LENGTH, fp))
    {
      printf("The command number %d is %s \n", counter, line);

      //Get the time when the process started
      if(clock_gettime(CLOCK_MONOTONIC,&start) == -1){
        perror(NULL);
      }

      //Create a new process
      pid = fork();

      //Child process
      if(pid == 0)
      {
        args[2] = line;
        execve("/bin/sh", args, NULL);
      }
      //Parent process
      else
      {
        wait(&status);
      }

      counter++;
    }

    printf("The overall time was %f seconds.\n", totalTime);

    fclose(fp);


    return 0;
  }

When I try to run it sometimes my result is:

The command number 0 is mkdir test/

Run command called
The command number 1 is sleep 0.5

Run command called
The command number 2 is sleep 1

Run command called
The command number 3 is rmdir test/

Run command called
The overall time was 1.544909 seconds.

Which seems correct. But in other instances my result is:

The command number 0 is mkdir test/

Run command called
The command number 1 is sleep 0.5

The command number 2 is sleep 1

The command number 3 is rmdir test/

The overall time was 0.009810 seconds.

This makes me believe that sigaction is not always receiving my SIGCHLD signal. When I run valgrind I get the following error:

    ==5407== All heap blocks were freed -- no leaks are possible
    ==5407== 
    ==5407== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
    ==5407== 
    ==5407== 1 errors in context 1 of 2:
    ==5407== Syscall param rt_sigaction(act->sa_flags) points to uninitialised byte(s)
    ==5407==    at 0x4E6F5AE: __libc_sigaction (sigaction.c:62)
    ==5407==    by 0x40098B: main (iv.c:40)
    ==5407==  Address 0xffefff498 is on thread 1's stack
    ==5407==  Uninitialised value was created by a stack allocation
    ==5407==    at 0x400931: main (iv.c:29)
    ==5407== 
    ==5407== 
    ==5407== 1 errors in context 2 of 2:
    ==5407== Syscall param rt_sigaction(act->sa_mask) points to uninitialised byte(s)
    ==5407==    at 0x4E6F5AE: __libc_sigaction (sigaction.c:62)
    ==5407==    by 0x40098B: main (iv.c:40)
    ==5407==  Address 0xffefff4a8 is on thread 1's stack
    ==5407==  Uninitialised value was created by a stack allocation
    ==5407==    at 0x400931: main (iv.c:29)
    ==5407== 
    ==5407== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

But I'm not sure what this means.

Upvotes: 0

Views: 1394

Answers (1)

P.P
P.P

Reputation: 121347

The simplest way is to initialise psa is to zero initialise it:

struct sigaction psa = {0};

However, there are subtleties. You can't call async-signal-unsafe functions, such as printf, from a signal handler. See signal safty for the list of allowed functions. You could replace the printf with a write(2) call to avoid this:

write(STDOUT_FILENO, "Run command called\n", sizeof "Run command called\n" - 1);

Also you're performing wait on each of the child process before creating another. Thus there's always only one child running at any time. If you modify your program to allow multiple child processes to run simultaneously and the child processes die around the same, then not all SIGCHLD signals can be caught. For example, if a SIGCHLD arrives while the signal handler is processing is previous SIGCHLD, then it'll be lost. Because pending signals will be discarded unless they're different signals, except for real-time signals. For example, if SIGUSR1 arrives while SIGCHLD is being processed (by signal handler), it'll be blocked; but another SIGCHLD will be ignored.

Upvotes: 3

Related Questions