Search code examples
linuxgcccompiler-errorsposixc11

Compiler error using WCONTINUED option for waitpid()


I am working on an exercise to understand how signals work on a POSIX OS, but I can't figure out how to compile the example. Here is the code that I'm trying to compile:

/* file sig_ex3.c: This is a more complex example of signals
   Author: Ramesh Yerraballi
   Attempt to mimic:
     prompt>> top | grep firefox 
   The parent creates  a pipe and two child processes with the
   write end of the pipe serving as the stdout for top and
   the read end serving as the stdin for grep.
   The first child that exec's top creates a new session with itself a member leader
   of the process group in it. The process group's id is same as the child's pid (pid_ch1).
   The second child that exec's grep joins the process group that the first child created
   Now when a Ctr-c is pressed the parent relays a SIGINT to both children using 
      kill(-pid_ch1,SIGINT); alternative you could call killpg(pid_ch1,SIGINT); 
   The two child processes receive the SIGINT and their default behavior is to terminate.
   Once they do that the parent reaps their exit status (using wait), prints and exits.
   When a  Ctrl-z is pressed the the parent relays a SIGTSTP to both children using 
      kill(-pid_ch1,SIGTSTP);
   The parent's waitpid() call unblocks when the child receives the STOP signal. The parent
   waits for 4 secs and resumes the the child that STOPped. This happens once for each of 
   the two children.
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>

int pipefd[2];
int status, pid_ch1, pid_ch2, pid;

static void sig_int(int signo) {
  printf("Sending signals to group:%d\n",pid_ch1); // group id is pid of first in pipeline
  kill(-pid_ch1,SIGINT);
}
static void sig_tstp(int signo) {
  printf("Sending SIGTSTP to group:%d\n",pid_ch1); // group id is pid of first in pipeline
  kill(-pid_ch1,SIGTSTP);
}

int main(void) {

  char ch[1]={0};

  if (pipe(pipefd) == -1) {
    perror("pipe");
    exit(-1);
  }

  pid_ch1 = fork();
  if (pid_ch1 > 0){
    printf("Child1 pid = %d\n",pid_ch1);
    // Parent
    pid_ch2 = fork();
    if (pid_ch2 > 0){
      printf("Child2 pid = %d\n",pid_ch2);
      if (signal(SIGINT, sig_int) == SIG_ERR)
        printf("signal(SIGINT) error");
      if (signal(SIGTSTP, sig_tstp) == SIG_ERR)
        printf("signal(SIGTSTP) error");
      close(pipefd[0]); //close the pipe in the parent
      close(pipefd[1]);
      int count = 0;
      while (count < 2) {
        // Parent's wait processing is based on the sig_ex4.c
        pid = waitpid(-1, &status, WUNTRACED | WCONTINUED);
        // wait does not take options:
        //    waitpid(-1,&status,0) is same as wait(&status)
        // with no options waitpid wait only for terminated child processes
        // with options we can specify what other changes in the child's status
        // we can respond to. Here we are saying we want to also know if the child
        // has been stopped (WUNTRACED) or continued (WCONTINUED)
        if (pid == -1) {
          perror("waitpid");
          exit(EXIT_FAILURE);
        }

        if (WIFEXITED(status)) {
          printf("child %d exited, status=%d\n", pid, WEXITSTATUS(status));count++;
        } else if (WIFSIGNALED(status)) {
          printf("child %d killed by signal %d\n", pid, WTERMSIG(status));count++;
        } else if (WIFSTOPPED(status)) {
          printf("%d stopped by signal %d\n", pid,WSTOPSIG(status));
          printf("Sending CONT to %d\n", pid);
          sleep(4); //sleep for 4 seconds before sending CONT
          kill(pid,SIGCONT);
        } else if (WIFCONTINUED(status)) {
          printf("Continuing %d\n",pid);
        }
      }
      exit(1);
    } else {
      //Child 2
      sleep(1);
      setpgid(0,pid_ch1); //child2 joins the group whose group id is same as child1's pid
      close(pipefd[1]); // close the write end
      dup2(pipefd[0],STDIN_FILENO);
      char *myargs[3];
      myargs[0] = strdup("grep");   // program: "grep" (word count)
      myargs[1] = strdup("firefox");   // argument: "firefox"
      myargs[2] = NULL;           // marks end of array
      execvp(myargs[0], myargs);  // runs word count
    }
  } else {
    // Child 1
    setsid(); // child 1 creates a new session and a new group and becomes leader -
    // group id is same as his pid: pid_ch1
    close(pipefd[0]); // close the read end
    dup2(pipefd[1],STDOUT_FILENO);
    char *myargs[2];
    myargs[0] = strdup("top");   // program: "top" (writes to stdout which is now pipe)
    myargs[1] = NULL;
    execvp(myargs[0], myargs);  // runs top
  }
}

This is the command that I'm using to compile it, and the output that I get:

$ gcc -D_POSIX_C_SOURCE -std=c11 -Wall sig_ex3.c -o sig_ex3
sig_ex3.c: In function ‘main’:
sig_ex3.c:66:48: error: ‘WCONTINUED’ undeclared (first use in this function); did you mean ‘WUNTRACED’?
         pid = waitpid(-1, &status, WUNTRACED | WCONTINUED);
                                                ^~~~~~~~~~
                                                WUNTRACED
sig_ex3.c:66:48: note: each undeclared identifier is reported only once for each function it appears in
sig_ex3.c:87:20: warning: implicit declaration of function ‘WIFCONTINUED’; did you mean ‘WIFEXITED’? [-Wimplicit-function-declaration]
         } else if (WIFCONTINUED(status)) {
                    ^~~~~~~~~~~~
                    WIFEXITED
sig_ex3.c:99:19: warning: implicit declaration of function ‘strdup’; did you mean ‘strcmp’? [-Wimplicit-function-declaration]
       myargs[0] = strdup("grep");   // program: "grep" (word count)
                   ^~~~~~
                   strcmp
sig_ex3.c:99:17: warning: assignment to ‘char *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
       myargs[0] = strdup("grep");   // program: "grep" (word count)
                 ^
sig_ex3.c:100:17: warning: assignment to ‘char *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
       myargs[1] = strdup("firefox");   // argument: "firefox"
                 ^
sig_ex3.c:111:15: warning: assignment to ‘char *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
     myargs[0] = strdup("top");   // program: "top" (writes to stdout which is now pipe)
               ^
sig_ex3.c:43:8: warning: unused variable ‘ch’ [-Wunused-variable]
   char ch[1]={0};
        ^~

Here is more info about my system:

$ uname -a
Linux hostname 5.0.0-38-generic #41-Ubuntu SMP Tue Dec 3 00:27:35 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu 8.3.0-6ubuntu1) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ ldd --version
ldd (Ubuntu GLIBC 2.29-0ubuntu2) 2.29
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.

I included all the necessary header files and definitions as stated in the waitpid() man page. However, I still get the error error: ‘WCONTINUED’ undeclared (first use in this function); did you mean ‘WUNTRACED’?. What am I missing?

Thanks for the help in advanced.


Solution

  • I figured out the culprit. I was using the -D_POSIX_C_SOURCE flag when I was using -std=c11. However, I switched to -std=gnu11 (as suggested in the comments to resolve some of the warnings - thanks @Shawn), and removing the -D_POSIX_C_SOURCE flag when using -std=gnu11 solves the error.