IC221: Systems Programming (SP14)


Home Policy Calendar Syllabus Resources Piazza

Lab 8: Solution

Table of Contents

1 Task 1 sleep-unroll

#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <readline/readline.h>
#include <readline/history.h>

#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>

#define MAX_ARGS 128

/************************************************************
 * sleep-unroll.c
 *
 * Unroll a pipeline of sleep calls, placing all children in their own
 * process group. 
 *
 * Sample Usage:
 *  $ ./sleep_unroll "sleep 1"   
 *  $ ./sleep_unroll "sleep 1 | sleep 2"
 *  $ ./sleep_unroll "sleep 2 | sleep 3 | sleep 1"
 *
 ************************************************************/


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

  pid_t cpid;
  pid_t cpgid=0; 

  int i, status;
  char * cmd_argv[MAX_ARGS];
  char * line, * tok;

  //split the command based on | into lines
  line = strtok(argv[1], "|");
  do{


    //TODO: Complete the unrolling
    //
    //   General outline:
    //    (fork)
    //      + Child: setpgid of itself
    //      + Child: parse command and exec
    //      + Parent: setpgid of child
    //
    //   Notes:
    //     - All children should be in the same process group
    //     - Use the pid of the *first* child as the pgid
    //     - Take advantage of setpgid(cpid,0) and setpgid(0,0)
    //       properties to assist logic
    //     - You can create orphaned process groups, be sure to kill
    //     - those with killall


    cpid = fork();
    if(cpid == 0){
      //first time, cpgid = 0, so same as setpgrp
      setpgid(cpid, cpgid);

      //parse command fill argv
      tok = strtok(line, " ");
      i = 0;
      do{
        cmd_argv[i] = tok;
        i++;
      }while( (tok = strtok(NULL, " ")) != NULL);
      cmd_argv[i] = NULL;

      //execute
      execvp(cmd_argv[0], cmd_argv);
      perror("exec");
      _exit(1);

    }else if (cpid > 0){
      //fist time, cpgid = 0
      setpgid(cpid,cpgid);
      cpgid = getpgid(cpid); //for next unroll

    }else{
      perror("fork");
      _exit(1);
    }

  }while( (line = strtok(NULL, "|")) != NULL);

  //wait for all children in a process group
  while(waitpid(-cpgid, &status, 0) > 0);

  //SUCCESS!
  return 0;
}

2 PART 2: babypipe

3 Task 2 babypipe

#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <readline/readline.h>
#include <readline/history.h>

#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>

#define MAX_ARGS 128

/***************************************************************************************
 * babypipe.c
 *
 * Will unroll and execute the pipeline with pipe() and dup()
 *
 * Three pipeline options to compile, using make
 *  - make 0  : cat /etc/passwd | cut -d : -f 7 | sort | uniq 
 *  - make 1  : cat sample-db.csv | cut -d , -f 8 | sort | uniq | wc -l
 *  - make 2  : cat sample-db.csv | cut -d , -f 10 | cut -d - -f 1 | sort | uniq |wc -l
 *  - make 3  : cat | cut -d , -f 10 | cut -d - -f 1 | sort | uniq |wc -l 
 ****************************************************************************************/
int main(int argc, char * argv[]){


#ifndef PIPE
  // make
  // make 0
  // cat /etc/passwd | cut -d : -f 7 | sort | uniq ;
  char * pipeline[5][6] = {
    { "cat", "/etc/passwd", NULL},    
    { "cut", "-d", ":", "-f" , "7", NULL},
    { "sort", NULL},
    { "uniq", NULL},
    {NULL},
  };
#elif PIPE == 1
  // make 1
  // cat sample-db.csv | cut -d , -f 8 | sort | uniq | wc -l;
  char * pipeline[6][6] = {
    { "cat", "sample-db.csv", NULL},    
    { "cut", "-d", ",", "-f" , "8", NULL},
    { "sort", NULL},
    { "uniq", NULL},
    { "wc", "-l", NULL},
    {NULL},
  };
#elif PIPE == 2
  // make 2
  // cat sample-db.csv | cut -d , -f 10 | cut -d - -f 1 | sort | uniq |wc -l;
  char * pipeline[7][6] = {
    { "cat", "sample-db.csv", NULL},    
    { "cut", "-d", ",", "-f" , "10", NULL},
    { "cut", "-d", "-", "-f" , "1", NULL},
    { "sort", NULL},
    { "uniq", NULL},
    { "wc", "-l", NULL},
    {NULL},
  };
#elif PIPE == 3
  // make 2
  // cat | cut -d , -f 10 | cut -d - -f 1 | sort | uniq |wc -l;
  char * pipeline[7][6] = {
    { "cat", NULL},    
    { "cut", "-d", ",", "-f" , "10", NULL},
    { "cut", "-d", "-", "-f" , "1", NULL},
    { "sort", NULL},
    { "uniq", NULL},
    { "wc", "-l", NULL},
    {NULL},
  };

#else
  char * pipeline[1][1] = {{NULL}};
#endif


  pid_t cpid;
  int i, status;

  int cur_pipe[2];
  int last_pipe[2];


  //iterate through pipeline
  for(i=0; pipeline[i][0] != NULL; i++){

    //TODO: Setup the pipeline:
    //
    //   General outline:
    //     - create cur_pipe in parent
    //     - fork
    //       + child: duplicate read from last_pipe
    //       + child: duplicate write to cur_pipe
    //       + parent: shift cur_pipe to last pipe
    //       + parent: widow pipe by closing last_pipe[1]
    //    - Parent: wait for all children to close
    //
    //  Notes: 
    //   - Will hange if forget to widow pipe
    //   - Check for ends of pipeline. The front of the pipeline, doe
    //     not duplicate stdin The end of pipeline, do not duplicate stdout


    //check for tail of pipeline, otherwise don't create pipe
    if(pipeline[i+1][0] != NULL){
      pipe(cur_pipe);
    }

    //fork
    cpid = fork();
    if( cpid == 0){
      /* CHILD */

      //not end of pipe, duplicate stdout
      if( pipeline[i+1][0] != NULL){
        close(cur_pipe[0]); //close read end of pipe
        close(1); //close stdout
        dup2(cur_pipe[1],1); //duplicate pipe to stdout
      }

      //not start of pipe, duplicate stdin
      if( i>0){
        close(last_pipe[1]); //close write end of pipe
        close(0); //close stdin
        dup2(last_pipe[0],0); //cuplicate pipe to stdin
      }

      //execute this stage in piepline
      execvp(pipeline[i][0], pipeline[i]);
      perror("exec");
      _exit(1);

    }else if( cpid > 0){
      /*PARENT*/

      //shift pipe
      last_pipe[0] = cur_pipe[0];
      last_pipe[1] = cur_pipe[1];

      //close write end of last_pipe,
      //or *widow* the pipe 
      close(last_pipe[1]);

    }else{
      /*ERROR*/
      perror("fork");
      _exit(1);
    }
  }  


  //wait for all children;
  while( wait(&status) > 0) ;

  return 0;
}