IC221: Systems Programming (SP14)


Home Policy Calendar Syllabus Resources Piazza

Lab 07: Solutions

Table of Contents

TASK 3: myps

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>

char * clean_comm(char * comm){
  char *p;
  for(p=comm+1;*p;p++){
    if ( *p == ')' ){
      *p = '\0';
      break;
    }
  }
  return comm+1;
}

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

  pid_t pid, ppid;
  char comm[1024];
  char state;

  int i;
  FILE * stat_f;
  char proc_path[1024];

  if (argc < 2){
    fprintf(stderr, "ERROR: Require a pid to print the satus of\n");
    exit(1);
  }


  printf("PID\tCOMM\tSTATE\tPPID\n");

  for(i = 1 ; i < argc; i ++){


    //TODO: Read from /proc/[pid]/stat and print PID,COMM,STATE,PPID information
    //      see man 5 proc for scanf() formats to use
    //      use clean_comm() to remove '(' and ')' from the command name

    snprintf(proc_path, 1024, "/proc/%s/stat", argv[i]);

    if ( (stat_f = fopen(proc_path, "r")) == NULL){
      fprintf(stderr, "ERROR: Invalid pid %s\n", argv[i]);
      continue;
    }

    //get pid
    fscanf(stat_f, "%d %s %c %d", &pid, comm, &state, &ppid);


    printf("%d\t%s\t%c\t%d\n", pid, clean_comm(comm), state, ppid);
    fclose(stdin);
  }

}

TASK 4: mypstree

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <stdio_ext.h>


char * clean_comm(char * comm){
  char *p;
  for(p=comm+1;*p;p++){
    if ( *p == ')' ){
      *p = '\0';
      break;
    }
  }
  return comm+1;
}


int getparent(pid_t parent, int depth){
  pid_t pid, ppid;
  char comm[1024];
  char state[2];
  FILE * stat_f;
  char proc_path[1024];
  int max_depth,i;

  //reached the kernel, return how far we've come
  if(parent == 0){
    return depth;
  }

  //TODO: READ from stat recursively child-to-parent-...-to-init
  //      return the max depth

  snprintf(proc_path, 1024, "/proc/%d/stat", parent);

  if ( (stat_f = fopen(proc_path, "r")) == NULL){
    fprintf(stderr, "ERROR: Invalid pid %d\n", parent);
    return 0;
  }

  //get info
  fscanf(stat_f, "%d %s %c %d", &pid, comm, state, &ppid);
  state[1] = '\0';
  fclose(stat_f);

  //get info on parent
  max_depth = getparent(ppid, depth+1); //recursive call


  //print the command
  printf("%s\n", clean_comm(comm));

  //space in based on the current depth and max_depth
  for(i=depth;i<max_depth;i++){
    printf("  "); //print a space for each depth
  }
  if(depth > 0){
    printf("└─"); //nice symbold
  }
  //return the max_depth
  return max_depth;
}


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

  pid_t pid;

  int i;

  if (argc < 2){
    fprintf(stderr, "ERROR: Require a pid to print the satus of\n");
    exit(1);
  }

  for(i = 1 ; i < argc; i++){
    if(sscanf(argv[i],"%d",&pid) == 0){
      fprintf(stderr, "ERROR: Invalid pid %s\n", argv[i]);
      continue;
    }
    getparent(pid,0);

    printf("\n");
  }



}

TASK 5: fg-shell

#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

//global last_pid: -1 no background job, > 0 pid of background job
pid_t last_pid = -1;

////////////////////////////////////////////////////
//*DO* EDIT BELOW

void my_wait(){
  pid_t pid;
  int status;

  //wait for child to change state 
  while(1){

    if ( (pid = waitpid(-1, &status, WUNTRACED)) < 0){
      perror("wait failed");
      _exit(2);
    }

    //TODO: Check if process changed state due to being stopped
    //      if so, and no background process, save pid and break loop
    //      if background process already, continue that process, and continue waiting
    //      if process changed state for another reason, termination, break loop


    //Did wait return because the child stopped?
    if(WIFSTOPPED(status)){ 
      printf("\n");
      //save the stopped child to bring to foreground later
      // if no current backgournd
      if( last_pid > 0){
        //already have a stopped job, don't allow and call wait again
        kill(pid, SIGCONT);
        printf("\n"); //for spacing prettyness

        continue; //continue to wait
      }else{
        last_pid = pid; 
        break; //break loop
      }
    }else{
      break; //break loop, child stoped for other reason
    }
  }


  //reclaim terminal before returning to the shell!
  if(tcsetpgrp(0,getpid()) < 0){
    perror("tcsetpgrp");
  }

  return;
}

////////////////////////////////////////////////////
//DO NOT EDIT BELOW

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


  char * line; 
  pid_t c_pid;
  int i;
  char * cmd_argv[MAX_ARGS]; //NOTE: use this store your argv array, 
                             //      don't go beyond MAX_ARGS
  char prompt[1024];
  char * tok;   //NOTE: This is useful for tokenizatoin

  //Need to ignore these signals for terminal control to work
  signal(SIGTTIN, SIG_IGN);
  signal(SIGTTOU, SIG_IGN);


  //for readline setup, don't edit!
  rl_bind_key('\t', rl_abort);



  while(1){ //fork/exec/wait loop

    //setup the prompt
    snprintf(prompt, 1024, "fg-shell (%d) #> ",last_pid);
    line = readline(prompt);   //readline allocates a new line every time, need to free

    //read EOF, break
    if (line == NULL){
      free(line);
      printf("\n");
      break;
    }

    //read empty line, continue
    if (strcmp(line,"")==0){
      free(line);
      continue;
    }


    //parse the line using strtok
    tok = strtok(line, " ");
    cmd_argv[0] = tok;
    i = 1;
    while( (tok = strtok(NULL, " ")) != NULL){
      cmd_argv[i] = tok;

      if ( i+1 > MAX_ARGS ){ //don't go beyond MAX_ARGS
        break;
      }
      i++;
    }
    cmd_argv[i] = NULL;  //NULL Final Space in argv array

    ////////////////////////////////////////////////////////////////
    //*DO* EDIT BELOW
    //

    //TODO: See if user entered "fg" command 
    //      If so, try and start the background process and call my_wait()
    //      If no last background process, report error to STDERR
    //      Don't forget to give the foreground process control of the terminal

    //perform a foreground
    if ( strcmp(cmd_argv[0], "fg") == 0){
      if (last_pid > 0){

        tcsetpgrp(0,last_pid); //give child terminal control

        kill(last_pid, SIGCONT); //tell child to continue

        last_pid = -1; //set last_pid to no background

        my_wait(); //wait for the next state change
      }else{
        printf("ERROR: No last background process\n");
      }

      free(line); //free line
      continue; //continue the shell after waiting
    }


    //////////////////////////////////////////////////////////////
    //DO NOT EDIT BELOW
    //

    if ( (c_pid = fork()) == 0 ){
      /* CHILD */

      //set as it's own process group
      setpgrp();

      execvp(cmd_argv[0],cmd_argv);
      perror("fg-shell"); //error
      _exit(2); //hard exit, don't want a fork bomb!

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

      //give child its own process group
      if(setpgid(c_pid, c_pid) < 0){
        perror("setpgrp");
      }

      //give child the terminal control
      if( tcsetpgrp(0, c_pid) < 0){
        perror("tcsetpgrp");
      }

      //wait for a state change
      my_wait();

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

    }

    free(line); //free the current line
  } 
  return 0;
}