Sei sulla pagina 1di 9

#include <string.

h>
#include <stdio.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include<fcntl.h>
#include<signal.h>
#include<malloc.h>
#include<stdlib.h>
struct job{
struct job* next;
int jobno; /* our own job control no..starts from 1
*/
pid_t processgroup; /* process id of the main process made into group id
*/
int bg; /* set 1 true if process is bg
*/
int status; /*running=1,stopped=2,needsterminal=3,finished=4,terminate
d=5*/
int filein; /* if stdin and stdout are redirected .
*/
int fileout;
char infilename[20];
char outfilename[20];
char jobname[20];
pid_t pid;
struct termios tmodes;
};
int gjobno;
pid_t shellPid;
pid_t shellPgid;
int volatile run;
struct job *joblist;
char* argv[20];
int argc;
char* charBuffer;
/*takes commands from stdin and parses into an array and keeps the count in argc
*/
struct termios shell_tmodes;
int shell_terminal;
int shell_is_interactive;
void processInput();
struct job * find_job (pid_t pid)
{
struct job *j;
for (j = joblist; j; j = j->next)
if (j->pid == pid)
return j;
return NULL;
}
/*creats a job in response to parameters passed from processInput(),
* specifically initialise the FILEIN/OUT Bg,jobno,jobstatus jobname
* (Note: next,processgoup,tmodes has to be set by after forking)*/
struct job* creatjob(char * pjobname, int ptmpin,int ptmpout,int pbg,char* pinfi
lename,char* poutfilename){
struct job* tmp = (struct job*)malloc(sizeof(struct job));
tmp->next=NULL;
tmp->jobno=gjobno;
tmp->bg=pbg;
tmp->filein=ptmpin;
tmp->fileout=ptmpout;
tmp->status=1;
strcpy(tmp->infilename,pinfilename);
strcpy(tmp->outfilename,poutfilename);
tmp->processgroup=0;
tmp->pid=0;
tcgetattr(shell_terminal, &tmp->tmodes);
strcpy(tmp->jobname,pjobname);
if(joblist!=NULL){tmp->next=joblist;joblist =tmp;}
else joblist =tmp;
gjobno++;
return tmp;
}
/* Make sure the shell is running interactively as the foreground job
before proceeding. */
void init_shell() {
run = 1;
gjobno = 1;
shellPid = getpid();
joblist = NULL;
/* See if we are running interactively. */
shell_terminal = STDIN_FILENO;
shell_is_interactive = isatty(shell_terminal);
if (shell_is_interactive) {
/* Loop until we are in the foreground. */
while (tcgetpgrp(shell_terminal) != (shellPgid = getpgrp()))
kill(-shellPgid, SIGTTIN);
/* Ignore interactive and job-control signals. */
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
//signal(SIGCHLD, SIG_IGN);
/* Put ourselves in our own process group. */
shellPgid = getpid();
if (setpgid(shellPgid, shellPgid) < 0) {
perror("Couldn't put the shell in its own process group");
exit(1);
}
/* Grab control of the terminal. */
tcsetpgrp(shell_terminal, shellPgid);
/* Save default terminal attributes for shell. */
tcgetattr(shell_terminal, &shell_tmodes);
}
}

/*read from stdin and lauch command parse.ie processinput*/


int prompt(){
charBuffer=malloc(2048* sizeof (char));
char* delim=" \t\n";
argc=0;
if( fgets(charBuffer,1024,stdin)!=NULL&&run==1)
{
char* temp=strtok(charBuffer,delim);
/*takes the rest of command into the buffer command array and keeps the
count in argc*/
while(temp!=NULL&& argc < 19 ){
argv[argc]=temp;
argc++;
temp=strtok(NULL,delim);
}
argv[argc]=(char*)NULL;
if (argc) processInput(argc, argv);
free(charBuffer);return 1;
}else {
free(charBuffer);
return 0;
}
}
void waitchild(pid_t cpid);
void put_job_in_fg(struct job *j, int cont) {
/* Put the job into the foreground. */
tcsetpgrp(shell_terminal, j->processgroup);
/* Send the job a continue signal, if necessary. */
if (cont) {
tcsetattr(shell_terminal, TCSADRAIN, &j->tmodes);
j->status=1;
if (kill(-j->processgroup, SIGCONT) < 0)
perror("kill (SIGCONT)");
}
/* Wait for it to report. */
waitchild(j->pid);
/* Put the shell back in the foreground. */
tcsetpgrp(shell_terminal, shellPgid);
/* Restore the shell's terminal modes. */
tcgetattr(shell_terminal, &j->tmodes);
tcsetattr(shell_terminal, TCSADRAIN, &shell_tmodes);
}
/* Put a job in the background. If the cont argument is true, send
the process group a SIGCONT signal to wake it up. */
void put_job_in_bg(struct job *j, int cont) {
/* Send the job a continue signal, if necessary. */
if (cont)
if (kill(-j->processgroup, SIGCONT) < 0)
perror("kill (SIGCONT)");
j->status=1;
j->bg=1;
}
/*prints the jobs running and their attributes*/
void printjob(){
struct job* tmp=joblist;
printf("\n::::::::::::::::::::****::::::::::::*****:::::::::::::::::::::::::::::
::::::");
printf("\nTenzin Minishell OS project: list of jobs:last created job is the firs
t on the list");
printf("\nJob no. Process name terminal input output status "
);
while (tmp != NULL) {
int pjobno = tmp->jobno;
char* pterminal = "foreground";
if (tmp->bg == 1) pterminal = "background";
char* pstatus;
switch(tmp->status) {
case 1:pstatus="running";break;
case 2:pstatus="stopped";break;
case 3:pstatus="needs foreground";break;
case 4:pstatus="finished succes";break;
case 5:pstatus="terminated";break;
default:pstatus="unknown";
}
printf("\n%5i | %16s|%10s| %10s| %10s |%10s", pjobno, tmp->jobname, pter
minal, tmp->infilename, tmp->outfilename,pstatus);
tmp = tmp->next;
}
printf("\n::::::::::::::::::::::end:::::::::::::::::::::::::::::::::::::
::::::::::\n");
}
/*put a job into a forground and update its job-bg status*/
void putJobInFg(int pjobno) {
struct job* tmp = joblist;
while ((tmp != NULL) && (tmp->jobno != pjobno)) {
tmp = tmp->next;
}
if (tmp != NULL) {
tmp->bg = 0;
//printf("trying to set pgrp %i as foreground",tmp-> processgroup);
put_job_in_fg(tmp,1);
}
return;
}
void removejob(int pjobno){
struct job* tmp=joblist;
struct job* tmp2;//one step delayed pointer
/*remove the specified job*/
if (pjobno > 0) {
tmp2 = joblist;
while (tmp->jobno != pjobno && tmp->next != NULL) {
tmp2 = tmp; // store the previous value
tmp = tmp->next;
}
if (tmp->jobno == pjobno) {
//job is head of list
if(tmp==joblist){
if (tmp->next == NULL) {
joblist=NULL;
} else{
joblist=tmp->next;}
}//job is not the head
else{
//bridge the list
tmp2->next =tmp->next;
}
fflush(NULL);
if(tmp->status<=3){
kill(-tmp->processgroup,SIGTERM);
}
free(tmp); /*removing the job from job list and print comment*/
printf("\n job %5i removed from list", pjobno);
return;
}else printf("\n job %5i not found in list", pjobno);
}else printf("\n invalid job number");
}
/*transmit the a signal to a processgroup */
void sendsignal(int pjobno,int sig){
struct job* tmp=joblist;
while ((tmp != NULL) && (tmp->jobno != pjobno)) {
tmp = tmp->next;
}
if (tmp!=NULL){
if(tmp->status<=3){
kill(-tmp->processgroup,SIGCONT);
}
kill(-tmp->processgroup,sig);
}else printf("\n job %5i not found", pjobno);
}
void launch_child(struct job* child,int isbg,char **argv );
/*process the tokenised input from prompt() and create job ,
* then fork and split into child and main,setting foreground process group .*/
void processInput(int argc,char* argv[20]){
int processjob=0;//initially false
int n=0;
if (argc>0){
int i;
int tmpin=STDIN_FILENO;//=NULL;
int tmpout=STDOUT_FILENO;//=NULL;*/
char* tmpfilein=" ";
char* tmpfileout=" ";
int killthis;
int bg=0;//normal are fg jobs
char* tmpjobno;
for(i=0;i<argc;i++){
if (strcmp("exit",argv[i])==0) {n=1; break;}
if (strcmp("jobs",argv[i])==0) {n=2; break;}
if (strcmp("kill", argv[i]) == 0) {
n = 3;
if (argv[i + 1] == NULL) {
printf("{n error :type the number of the job to kill eg. kil
l [3] ");
return ;
}
if (isdigit(*argv[i + 1])) {
killthis = atoi(argv[i + 1]);
} else return;
}
if (strcmp("bg",argv[i])==0) {n=4; argv[i]=" ";bg=1;}
if (strcmp("fg",argv[i])==0) {n=5; argv[i]=" ";tmpjobno=argv[i+1];
}
if (strcmp("in", argv[i]) == 0) {
argv[i] = " ";
if (argv[i + 1] != NULL) {
tmpin = 5; //open(argv[i+1],O_RDONLY))!=-1
tmpfilein = argv[i + 1];
argv[i + 1] = " ";
} else {
n = 6;
printf("insufficient argument for in redirection ");
}
}
if (strcmp("out", argv[i]) == 0) {
argv[i] = " ";
if (argv[i + 1] != NULL)//
{
tmpout = 5;
tmpfileout = argv[i + 1];
argv[i + 1] = " ";
} else {
n = 6;
printf("insufficient argument for out redirection");
}
}
if (strcmp("remove",argv[i])==0) {n=7; argv[i]=" ";tmpjobno=arg
v[i+1];
}
}
//check if input &output both are redirected. in that case it may as wel
l be a bg job.
if(tmpin!=STDIN_FILENO&&tmpout!=STDOUT_FILENO){
bg=1;
}
struct job* newjob;
switch(n){
case 1: run=0;return;
case 2: printjob();return;
case 3: sendsignal(killthis,SIGTERM);return;
case 4: newjob=creatjob(argv[0],tmpin,tmpout,bg,tmpfilein,tmpfileout);pr
ocessjob=1; break;
case 5: if (isdigit(*tmpjobno)) {
putJobInFg(atoi(tmpjobno));
return;
} else{ printf("which job to put in foreground?");
return;}
case 7: if (isdigit(*tmpjobno)) {
removejob(atoi(tmpjobno));
return;
} else{ printf("which job to remove?");
return;}
default: newjob=creatjob(argv[0],tmpin,tmpout,bg,tmpfilein,tmpfileout);p
rocessjob=1; //create a job and run it
}
/*creation of jobs, child process etc*/
if (processjob == 1) {
pid_t cpid;
cpid = fork();
if (cpid == 0) {
launch_child(newjob, bg, argv);
} else if (cpid > 0) {// in parent process
/*sleep one sec to speed up child process */
newjob->pid = cpid;
if (newjob->processgroup == 0) {
setpgid(cpid, cpid);
newjob->processgroup=getpgid();
}
/*if (tmpin != STDIN_FILENO)
close(tmpin);
if (tmpout != STDOUT_FILENO)
close(tmpout);*/
if(bg==1){
put_job_in_bg(newjob,0);
}else put_job_in_fg(newjob,0);
} else {
printf("\n\t unable to fork");
exit(8);
}
}/*close for valid job creation */
}/*close for no argument from prompt() */
}
//detect changes in the status of the child processgroups
void removeProcess(pid_t cpid,int rtype){
int pjobno=0;
struct job* tmp=joblist;
struct job* tmp2;//one step delayed pointer
/*remove the specified job*/
if (cpid > 0) {
tmp2 = joblist;
//travers the list
while (tmp->pid != cpid && tmp->next != NULL) {
tmp2 = tmp; // store the previous value
tmp = tmp->next;
}
//found our job
if (tmp->pid == cpid) {
/*job is head of list
if(tmp==joblist){
//only job in list
if (tmp->next == NULL) {
joblist=NULL;
} else{
joblist=tmp->next;}
}//job is not the head
else {
//bridge the list
tmp2->next = tmp->next;
}
//check to see if this was the fgjob
if (strcmp(tmp->outfilename,"")){
}*/
pjobno = tmp->jobno;
//free(tmp); /*removing the job from job list and print comment*/
if (rtype == 1){ tmp->status=4;}
else{ tmp->status=5;}
return;
} else printf("\n process %5i not found in list", cpid);
} else printf("\n invalid process number");
}
void launch_child(struct job* child,int isbg,char **argv ){
if (shell_is_interactive)
{
/* Put the process into the process group and give the process group
the terminal, if appropriate.
This has to be done both by the shell and in the individual
child processes because of potential race conditions. */
pid_t cpid = getpid ();
child->pid=cpid;
if (child->processgroup == 0) child->processgroup = cpid;
setpgid (cpid, child->processgroup);
child->processgroup=getpgid();
if (isbg==0)
tcsetpgrp (shell_terminal, child->processgroup);
/* Set the handling for job control signals back to the default. */
signal (SIGINT, SIG_DFL );
signal (SIGQUIT, SIG_DFL);
signal (SIGTSTP, SIG_DFL);
signal (SIGTTIN, SIG_DFL);
signal (SIGTTOU, SIG_DFL);
signal (SIGCHLD, SIG_DFL);
}
if (child->filein != STDIN_FILENO) {
child->filein=open(child->infilename,O_RDONLY);
if(child->filein==-1){
printf("error opening %s \n",child->infilename);
return ;
}
if (dup2(child->filein,STDIN_FILENO) == -1) {
printf("error in duplicationg input file");
}close(child->filein);
}
if (child->fileout != STDOUT_FILENO) {
child->fileout=open(child->outfilename, O_WRONLY | O_CREAT |
O_APPEND,
(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
if(child->fileout==-1){
printf("error opening %s\n",child->outfilename);
return ;
}
if (dup2(child->fileout,STDOUT_FILENO) == -1) {
printf("error in duplicationg output file");
}close(child->fileout);
}
execvp(argv[0], argv);
fflush(NULL);
/*close for child process*/
}
void waitchild(pid_t cpid){
int status;
pid_t changed_pid=0;
if(cpid!=0){
//a blocking wait for the specifeid cpid
changed_pid=waitpid(cpid,&status,WUNTRACED);
}else changed_pid=waitpid(-1,&status,WNOHANG|WUNTRACED);
if (changed_pid>0)//a change in one child
{
if(WIFEXITED(status)){
// a child exited normally.So we cleen up and restore the shell if neces
sarz
removeProcess(changed_pid,1);
}else if(WIFSIGNALED(status)){
// a child was terminated unexpectedly
removeProcess(changed_pid,2);
}else if(WIFSTOPPED(status)){
//a child was stopped
int sig=0;
sig=WSTOPSIG(status);
struct job* temp=find_job(changed_pid);
if (temp==NULL) return;
switch(sig){
case SIGTTIN:
case SIGTTOU:{temp->status=3;return;}
default: {temp->status=2;return;}
}
}
}
}

/*main of the program. set up initial globals and call prompt() to wait for resp
onse. then process user input() */
int main(){
init_shell();
while (run == 1) {
waitchild(0);
//if the shell is not in fg then there must be a fg job
// we wait till control has return to shell
while(tcgetpgrp(shell_terminal)!=shellPgid){
waitchild(0);
}
printf("\n:~)");
//wait till currant fg job exits.
//poll stdin
int peekstdin=0;
while((peekstdin=fgetc(stdin))==0&&run==1){
//wait for child process
}
ungetc(peekstdin,stdin);
//process commands
prompt();
}
}