Write my own shell

 

Project 1 : sshell report

Key points:

  • Parents and child process
  • Parsing the input
  • Build in functions(cd, pwd)
  • Redirection & pipe
  • Backgound Proccessing

Start

To implement the shell, firstly, we built a prototype sshell which keep running in a while loop printing sshell$ and asking for user input till exit has been entered.

sshell$
sshell$ exit
Bye...

parsing and fork()

Based on the prototype we implement the basic system call which using fork() to duplicate the current status and run the cmd inside the child proccess, the parents will catch the status of child. At this step, we wrote the parsing function, which split the cmd according to the “ “ and “\n”, and store them in an string(char arry) arry for execvp. The arrays are dynamic allocated. After this, we add more features based on this prototype.

  1. for this part we used execvp() because for execve() the envirmental var is alway 0, so execvp() might be better.
  2. we thought using a data structure to hold the parameter for execvp which is clearer, but using array is much simpler.

how to use execvp

Build in function

We faced some problem when dealing with builtin func. Initally we used the child proccess to run cd, however, it didn’t work out. Then we found out that the build in functions are functions inside parent proccess. After taking close look at GNU lib, we found it pretty straight forward by using:

chdir(path)// used for cd to change dir
getcwd()// used to get current working dir
exit() // for exit

Ps. according to the return value of chdir(), print out the error message.

Redirection & pipe

1. redirection

We add an different parsing function for this. After get the input, we search for any redirection sign(<,>), and treat them differently. we created a data structure to store the input, output and cmd differently, also we have direction to indicate the sign.

typedef struct myData { // data to store redirection
    char inputFile[1000]; // content for input
    char outputFile[1000]; // content for output
    char command[1000];
    char direction[1]; // < or >
}Data;

for redirection we used dup2(). Depending on value of data.direction, if we have < then int fd_out = open(data.outputFile, O_RDWR), close STDIN and redirect stdin with input by using dup2(); such as dup2(fd_out, 0); then the rest is same as above, just execvp() the cmd with the inputfile. The output redirection is pretty similar with redirin() in my code.

few things to note:
  1. for > redirection, we initiall usingint fd_out = open(data.outputFile, O_RDWR); which cannot write to nonexisting file later we change it to int fd_out = open(data.outputFile, O_CREAT|O_RDWR, 0666);, it pass the tester.
  2. one thing might need to work on is we cannot deal with the input, output redirection happens at the same time because the our parsing function didn’t support that.
2. pipe

pipe is the hardest part of this assignment for us. We finished part 6 within 2 day after the assignment prompt was published, but spent few days on pipe, I found out its extremly hard to visulized and imagine multi-pipe. it is easy to deal with two pipe A | B.

Here is the way we deal with pipe finally:

  1. new function parsing_pipe for any give argument which contains pipe will provide us pipe number and each argument return -1 when there errors in pipe cmd.
  2. based on previous build, add one more flag to indicate there is a pipe in input and handling the error pipe.
  3. When there is a correct pipe command, then we go over a for loop with the num of pipe times. We used pipe to connect the stdout of the last cmd with the stdin of the next cmd till the end of for loop.
  4. by doing this we basically connect the input and output throughout the pipe. therefor the multi-pipe was implemented.
few notes for pipe:
  1. few ideas from how pipe works
  2. teach u about shell
  3. Book–Unix OS Design and Practics (Chapter 7,8)
  4. < will only appear at the first pipe; and > will only at end of the pipe, if not will cause ambiguity
  5. pipe is kind of similar to redirection part, they both using dup to redirect the input and output, but this part use pipe as a channel to communicate between each other.
  6. the assignment didn’t ask us to do about « and », which might be similar as < and >

Last bg_proccessing

For bg proccess, which noted as & at the end of cmd. To implement this, when get bg_proccess input, the parent didn’t wait for the child to finish directly asking for next operation. We have a new data structure bg_table to hold bg_cmd.

typedef struct bgproccess { // bg processTable
    pid_t pid;
    char cmd[3000];
    int status;
    int completed;
    int printsig;
}bg_proccess;

add two functions:

function 1 : int check_background(char *cmd[])

  • return -1 when & mislocated.
  • return 1 correct bg and remove &.
  • return 0 when given cmd has no bg.

function 2 : void background_processing(bg_proccess **processTable, int *cannot_exit) .

  1. check processtable catch zombie proccess with waitpid((*processTable)[iter].pid, &((*processTable)[iter].status), WNOHANG);.
  2. if proccess completed then check if it is printed.
  3. if there still bg running set cannot_exit for later exit() use.

at the very beginning of the while loop call background_processing see if there any finished bg and set the cannot_exit value. When get an input, callcheck_background see if is a bg proccess. if it is a bg, then fork() save the info into the table. child proccess keep running while the parentscontinue;. If the input is exit then check for the value of cannot_exit, if is 1then exit(0); else print out Error, still bg running.

notes for bg:
  1. if child is exits before parents exits, and if there no specific waitpid() to collect them, it will become zombie proccess.
  2. if parents exits before child exits, the child will become the child of init proccess.

Overview:

  • The parsing part is complex but require less skill comparing to other part. It is very important, though.
  • didn’t using gdb, but using print to find out errors(write function log() to short code the fprintf())
  • Error message is useful when checking for bugs.
  • pointer requires lot of work and easy cause seg fault.
  • using the free() and memset to clean the content of the container is very important which will avoid overlapping cmd.
  • git is useful to keep tracking what we are doing.
  • challenging but interesting project.

Design Choice

We wrote the sshell step by step and build each phase on each other. We tried to separating the code at each phase, so that if one case crashes has no effect on overall sshell. Also it make it easier to debug since it is separate out. Though it will make the code much longer, and a lot of code is kind of repeatitive.

Testting

We run the testing under bash, redirect the stdout and stderr in to a file and using diff to check the results against the sshell_ref. When we found an error, we step printed out the var and sign to find out the problem. Since our program is separate for each phase, it is easy to locate the problem. While we debuging the sshell, free the pointer and memset the content of char arry is very important to get rid of info overlapping, which causes a lot of err.

some link we used:

http://www.cnblogs.com/wuyuegb2312/p/3369082.html

http://stackoverflow.com/questions/14301407/how-does-execvp-run-a-command

http://stackoverflow.com/questions/19099663/how-to-correctly-use-fork-exec-wait