CSC 357 Lecture Notes Week 8
Introduction to Signals and Pipes
   1  #include <stdio.h>
   2  #include <stdlib.h>
   3  #include <unistd.h>
   4  #include <termios.h>
   5  #include <signal.h>
   6  #include <sys/time.h>
   7
   8
   9  /****
  10   *
  11   * This program is a simple example of using setitimer and sigaction to
  12   * generate and handle SIGALRM signals.  The program sets up an interval timer
  13   * that generates a SIGALRM once a second.  The SIGALRM handler simply
  14   * increments a signal counter and prints out its value.
  15   *
  16   * Before starting the timer, the program puts the terminal in "raw mode".
  17   * This means that it does not echo input characters, wait for a newline to
  18   * complete an input, or respond to interrupt characters.
  19   *
  20   * After starting the timer and setting up the handler, the main function waits
  21   * for the user to type the character 'q' to quit.  Since the terminal is in
  22   * raw mode, typing 'q' is the only way to stop, since all other printable
  23   * characters are ignored, and terminal interrupt characters are disabled.
  24   *
  25   */
  26
  27
  28  /**
  29   * The ticks variable counts the number of one-second ticks since the program
  30   * started.
  31   */
  32  static int ticks;
  33
  34  /**
  35   * The tick function is the handler for SIGALRM.
  36   */
  37  void tick(int sig) {
  38      printf("%d\r", ++ticks);
  39  }
  40
  41  /**
  42   * Set the terminal to "raw" input mode.  This is defined by setting terminal
  43   * control flags to noncanonical mode, turning off character echoing, and
  44   * ignoring signaling characters.  Before setting to raw mode, save the
  45   * current mode so it can be restored later.  After setting, return the
  46   * saved mode.
  47   *
  48   * For explanatory details, see Sections 18.10, 18.11 of Stevens, the
  49   * termio(7I) man page, and the tcsetattr(3C) man page.  (To see a particular
  50   * man page section, use the "-s" argument, e.g., "man -s 7I termio" on
  51   * falcon/hornet.)
  52   */
  53  struct termios set_raw_term_mode() {
  54      struct termios cur_term_mode, raw_term_mode;
  55
  56      tcgetattr(STDIN_FILENO, &cur_term_mode);
  57      raw_term_mode = cur_term_mode;
  58      raw_term_mode.c_lflag &= ~(ICANON | ECHO | ISIG) ;
  59      raw_term_mode.c_cc[VMIN] = 1 ;
  60      raw_term_mode.c_cc[VTIME] = 0;
  61      tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_term_mode);
  62
  63      return cur_term_mode;
  64  }
  65
  66  /**
  67   * Restore the terminal mode to that saved by set_raw_term_mode.
  68   */
  69  void restore_term_mode(struct termios saved_term_mode) {
  70      tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_term_mode);
  71  }
  72
  73  /**
  74   * Set the terminal to raw mode, set up a one-second timer, set up the SIGARLM
  75   * handler, and then wait for the user to type 'q'.
  76   *
  77   * For details of timer setup, see Stevens Section 6.1 and the man pages for
  78   * setitimer(2) and gettimeofday(3C).
  79   *
  80   * For details of signal setup, see Stevens Section 10.14, and the man page for
  81   * sigaction.
  82   */
  83  int main(int argc, char** argv) {
  84
  85      struct termios saved_term_mode;      /* saved entering terminal mode */
  86      struct itimerval tbuf;               /* interval timer structure */
  87      struct sigaction action;             /* signal action structure */
  88
  89      /*
  90       * Explain to the user how to quit.
  91       */
  92      printf("Type 'q' to quit.\n\n");
  93
  94      /*
  95       * Initialize ticks to 0.
  96       */
  97      ticks = 0;
  98
  99      /*
 100       * Set up the SIGALRM handler.
 101       */
 102      action.sa_handler = tick;     /* set tick to be the handler function */
 103      sigemptyset(&action.sa_mask); /* clear out masked functions */
 104      action.sa_flags   = 0;        /* no special handling */
 105
 106      /*
 107       * Use the sigaction function to associate the signal action with SIGALRM.
 108       */
 109      if (sigaction(SIGALRM, &action, NULL) < 0 ) {
 110          perror("SIGALRM");
 111          exit(-1);
 112      }
 113
 114      /*
 115       * Define a one-second timer.
 116       */
 117      tbuf.it_interval.tv_sec  = 1;
 118      tbuf.it_interval.tv_usec = 0;
 119      tbuf.it_value.tv_sec  = 1;
 120      tbuf.it_value.tv_usec = 0;
 121
 122      /*
 123       * Use the setitimer function to start the timer.
 124       */
 125      if ( setitimer(ITIMER_REAL, &tbuf, NULL) == -1 ) {
 126          perror("setitimer");
 127          exit(-1);                   /* should only fail for serious reasons */
 128      }
 129
 130      /*
 131       * Set the terminal to raw mode.
 132       */
 133      saved_term_mode = set_raw_term_mode();
 134
 135      /*
 136       * Busy wait until the user types 'q'.
 137       */
 138      while (getchar() != 'q') {}
 139
 140      /*
 141       * Restore the terminal to the mode it was in at program entry.
 142       */
 143      restore_term_mode(saved_term_mode);
 144
 145  }
int sigaction(int sig, const struct sigaction *restrict act,
              struct sigaction *restrict oact);
struct sigaction {
    union {                              /* handling function */
        void (*sa_handler)(int);         /*
        void (*sa_sigaction)(
              int, siginfo_t*, void*);
    } funcptr;
    int sa_flags;                        /* options flags */
    sigset_t sa_mask;                    /* signals to block */
}
typedef void Sigfunc(int); Sigfunc* signal(int, Sigfunc*);
We'll return to some additional signal topics from chapter 10 in a later lecture; in the meantime, we'll introduce the important concept of pipes, which you will use in Lab 7, and the upcoming programming assignment.
which pipes the output of "ps" through grep to find all processes owned by "gfisher".ps -fe | grep "gfisher"
int pipe(int filedes[2]);
   1  #include <unistd.h>
   2  #include <stdlib.h>
   3  #include <stdio.h>
   4  #include <string.h>
   5
   6  #define MAXLINE 100
   7  #define err_sys(msg) perror(msg); exit(-1)
   8
   9  int main() {
  10      int n;              /* number of bytes read from the pipe */
  11      int fd[2];          /* the pipe read/write file descriptors */
  12      pid_t pid;          /* pid of the child process */
  13      char line[MAXLINE]; /* line of input read from the pipe */
  14
  15      /*
  16       * Create a pipe.
  17       */
  18      if (pipe(fd) < 0) {
  19          err_sys("pipe");
  20      }
  21
  22      /*
  23       * Fork a child process.
  24       */
  25      if ((pid = fork()) < 0) {
  26          err_sys("fork");
  27      }
  28
  29      /*
  30       * Parent writes down the pipe, closing the unneeded read end of the pipe.
  31       */
  32      else if (pid > 0) {
  33          close(fd[0]);
  34          write(fd[1], "Hello world0, strlen("Hello world0));
  35      }
  36
  37      /*
  38       * Child reads the pipe, closing the unneded write end.  It then writes the
  39       * read data to stdout, just for confirmation.
  40       */
  41      else {
  42          close(fd[1]);
  43          n = read(fd[0], line, MAXLINE);
  44          write(STDOUT_FILENO, line, n);
  45      }
  46      exit(0);
  47  }
    
   1  /**
   2   * Exec a program and read the first 80 chars of its stdout.
   3   */
   4
   5  #include <stdio.h>
   6  #include <unistd.h>
   7  #include <stdlib.h>
   8  #include <sys/wait.h>
   9
  10  #define READ_SIZE 80
  11
  12  int main(int argc, char** argv) {
  13
  14      int fd[2];
  15      int pid;
  16      char read_buf[READ_SIZE + 1];
  17      int chars_read;
  18
  19      /*
  20       * Check for adequate args.
  21       */
  22      if (argc < 1) {
  23          fprintf(stderr, "usage: grab-stdout prog arg ...");
  24          exit(-1);
  25      }
  26
  27      /*
  28       * Create a pipe that will have its output written to by the executed
  29       * program.
  30       */
  31      pipe(fd);
  32
  33      /*
  34       * Fork off a process to exec the program.
  35       */
  36      if ((pid = fork()) < 0) {
  37          perror("fork");
  38    exit(-1);
  39      }
  40
  41      /*
  42       * Forked child has its stdout be the write end of the pipe.
  43       */
  44      else if (pid == 0) {
  45
  46          /*
  47           * Don't need read end of pipe in child.
  48           */
  49          close(fd[0]);
  50
  51          /*
  52           * This use of dup2 makes the output end of the pipe be stdout.
  53           */
  54          dup2(fd[1], STDOUT_FILENO);
  55
  56          /*
  57           * Don't need fd[1] after the dup2.
  58           */
  59          close(fd[1]);
  60
  61          /*
  62           * Exec the program given in the command line, including any args.
  63           */
  64          execvp(argv[1], &(argv[1]));
  65          perror("exec");
  66          exit(-1);
  67      }
  68
  69      /*
  70       * Parent takes its input from the read end of the pipe.
  71       */
  72      else {
  73          /*
  74           * Don't need write end of pipe in parent.
  75           */
  76          close(fd[1]);
  77
  78          /*
  79           * Read the data coming in on the read end of the pipe.
  80           */
  81          chars_read = read(fd[0], read_buf, READ_SIZE);
  82
  83          /*
  84           * Print data, if any read, otherwise print "no output" message.
  85           */
  86          if (chars_read > 0) {
  87
  88              /*
  89               * Null terminate the read string.
  90               */
  91              read_buf[chars_read] = '\0';
  92
  93              /*
  94               * Print the string out, for confirmation.
  95               */
  96              printf("read_buf:\n%s\n", read_buf);
  97          }
  98
  99          else {
 100              fprintf(stderr, "Program produced no output.\n");
 101              exit(-1);
 102          }
 103      }
 104
 105      exit(0);
 106  }