2 * tsh - A tiny shell program with job control
4 * <Put your name and login ID here>
12 #include <sys/types.h>
16 /* Misc manifest constants */
17 #define MAXLINE 1024 /* max line size */
18 #define MAXARGS 128 /* max args on a command line */
19 #define MAXJOBS 16 /* max jobs at any point in time */
20 #define MAXJID 1<<16 /* max job ID */
23 #define UNDEF 0 /* undefined */
24 #define FG 1 /* running in foreground */
25 #define BG 2 /* running in background */
26 #define ST 3 /* stopped */
29 #define TNORMAL 0x0 /*argument*/
30 #define TINFILE 0x1 /*input file*/
31 #define TOUTFILE 0x2 /*output file*/
34 * Jobs states: FG (foreground), BG (background), ST (stopped)
35 * Job state transitions and enabling actions:
37 * ST -> FG : fg command
38 * ST -> BG : bg command
39 * BG -> FG : fg command
40 * At most 1 job can be in the FG state.
43 /* Global variables */
44 extern char **environ
; /* defined in libc */
45 char prompt
[] = "tsh> "; /* command line prompt (DO NOT CHANGE) */
46 int verbose
= 0; /* if true, print additional output */
47 int nextjid
= 1; /* next job ID to allocate */
48 char sbuf
[MAXLINE
]; /* for composing sprintf messages */
50 struct job_t
{ /* The job struct */
51 pid_t pid
; /* job PID */
52 int jid
; /* job ID [1, 2, ...] */
53 int state
; /* UNDEF, BG, FG, or ST */
54 char cmdline
[MAXLINE
]; /* command line */
56 struct job_t job_list
[MAXJOBS
]; /* The job list */
58 struct cmdline_tokens
{
59 int argc
; /* Number of arguments */
60 char *argv
[MAXARGS
]; /* The arguments list */
61 char *infile
; /* The input file */
62 char *outfile
; /* The output file */
63 enum builtins_t
/* Indicates if argv[0] is a builtin command */
64 {none
, quit
, jobs
, bg
, fg
} builtins
;
67 /* End global variables */
70 /* Function prototypes */
72 /* Here are the functions that you will implement */
73 void eval(char *cmdline
);
74 int builtin_cmd(char **argv
);
75 void do_bgfg(char **argv
);
76 void waitfg(pid_t pid
);
78 void sigchld_handler(int sig
);
79 void sigtstp_handler(int sig
);
80 void sigint_handler(int sig
);
82 /* Here are helper routines that we've provided for you */
83 int parseline(const char *cmdline
, struct cmdline_tokens
*tok
);
84 void sigquit_handler(int sig
);
86 void clearjob(struct job_t
*job
);
87 void initjobs(struct job_t
*jobs
);
88 int maxjid(struct job_t
*jobs
);
89 int addjob(struct job_t
*jobs
, pid_t pid
, int state
, char *cmdline
);
90 int deletejob(struct job_t
*jobs
, pid_t pid
);
91 pid_t
fgpid(struct job_t
*jobs
);
92 struct job_t
*getjobpid(struct job_t
*jobs
, pid_t pid
);
93 struct job_t
*getjobjid(struct job_t
*jobs
, int jid
);
94 int pid2jid(pid_t pid
);
95 void listjobs(struct job_t
*jobs
);
98 void unix_error(char *msg
);
99 void app_error(char *msg
);
100 typedef void handler_t(int);
101 handler_t
*Signal(int signum
, handler_t
*handler
);
104 * main - The shell's main routine
106 int main(int argc
, char **argv
)
109 char cmdline
[MAXLINE
];
110 int emit_prompt
= 1; /* emit prompt (default) */
112 /* Redirect stderr to stdout (so that driver will get all output
113 * on the pipe connected to stdout) */
116 /* Parse the command line */
117 while ((c
= getopt(argc
, argv
, "hvp")) != EOF
) {
119 case 'h': /* print help message */
122 case 'v': /* emit additional diagnostic info */
125 case 'p': /* don't print a prompt */
126 emit_prompt
= 0; /* handy for automatic testing */
133 /* Install the signal handlers */
135 /* These are the ones you will need to implement */
136 Signal(SIGINT
, sigint_handler
); /* ctrl-c */
137 Signal(SIGTSTP
, sigtstp_handler
); /* ctrl-z */
138 Signal(SIGCHLD
, sigchld_handler
); /* Terminated or stopped child */
140 /* This one provides a clean way to kill the shell */
141 Signal(SIGQUIT
, sigquit_handler
);
143 /* Initialize the job list */
146 /* Execute the shell's read/eval loop */
149 /* Read command line */
151 printf("%s", prompt
);
154 if ((fgets(cmdline
, MAXLINE
, stdin
) == NULL
) && ferror(stdin
))
155 app_error("fgets error");
156 if (feof(stdin
)) { /* End of file (ctrl-d) */
161 /* Evaluate the command line */
167 exit(0); /* control never reaches here */
171 * eval - Evaluate the command line that the user has just typed in
173 * If the user has requested a built-in command (quit, jobs, bg or fg)
174 * then execute it immediately. Otherwise, fork a child process and
175 * run the job in the context of the child. If the job is running in
176 * the foreground, wait for it to terminate and then return. Note:
177 * each child process must have a unique process group ID so that our
178 * background children don't receive SIGINT (SIGTSTP) from the kernel
179 * when we type ctrl-c (ctrl-z) at the keyboard.
181 void eval(char *cmdline
)
183 int state
, child_status
, jid
;
184 struct cmdline_tokens tok
;
186 int fin
, fout
, retval
, temp
;
191 /* Parse command line */
192 state
= parseline(cmdline
, &tok
);
194 if (state
== -1) return;
195 if (tok
.argv
[0] == NULL
) return;
196 sigprocing(SIG_BLOCK
);
198 /* fill the set of sus_mask with all the signals and remove the signals
199 SIGCHLD, SIGINT and SIGTSTP */
200 sigfillset(&sus_mask
);
201 sigdelset(&sus_mask
, SIGCHLD
);
202 sigdelset(&sus_mask
, SIGINT
);
203 sigdelset(&sus_mask
, SIGTSTP
);
205 /* checking if the quit command was given */
206 if (tok
.builtins
== quit
) {
209 /* checking if the command is bg or fg */
210 else if (tok
.builtins
== bg
|| tok
.builtins
== fg
){
211 pidorjid
= tok
.argv
[1];
213 /* check if no jid or pid was provided in the arguments */
214 if(pidorjid
== NULL
){
215 unix_error("JID or PID is required\n");
217 /* check if the argument provided was the jid and not the pid */
219 else if(pidorjid
[0] == '%'){
220 jid
= atoi(pidorjid
+= sizeof(char));
222 if(jid
!= 0 && (j
= getjobjid(job_list
,jid
)) == NULL
){
223 unix_error("No such job exists in the list\n");
228 /* get pid based on jid and check against the job list */
229 else if(atoi(pidorjid
) != 0){
230 if((j
= getjobjid(job_list
,pid2jid(atoi(pidorjid
)))) == NULL
) {
231 unix_error("No such process in the job list\n");
236 unix_error("Please enter a pid or jid as argument\n");
241 /* continue the jobs in the group */
242 if(kill(-(j
->pid
),SIGCONT
) < 0){
246 /* if it is bg then change the state of the job to BG and if the
247 command was fg then change the job state to FG and wait till the
248 jobs in the foreground get done in the job list */
250 if(tok
.builtins
== bg
){
251 printf("[%d] (%d) %s\n", j
->jid
, j
->pid
, j
->cmdline
);
254 else if(tok
.builtins
== fg
){
256 while(j
->pid
== fgpid(job_list
)>0);
259 unix_error("BG/FG error");
263 /* if the command on the shell was neither of the builtin command or if it
264 was job then do the following */
266 /* fork a child process to do the required commands */
267 if((pid
= fork()) == 0){
268 /* setting proces group id for the child */
270 unix_error("Error in setpgid");
272 /* i/o redirection. only works with implementation in parseline function */
274 if(tok.infile != NULL){
275 if ((fin = open(tok.infile, O_RDONLY)) < 0){
276 unix_error("Open of input file failed");
280 if ((retval = close(fin)) < 0) {
281 unix_error("close of input file failed");
285 if(tok.outfile != NULL){
286 if ((fout = open(tok.outfile, O_RDWR|O_CREAT)) < 0){
287 unix_error("Open of output file failed");
291 if ((retval = close(fout)) < 0) {
292 unix_error("close of input file failed");
296 /* jobs command. Notice the this is within the child process */
297 if(tok
.builtins
== jobs
){
301 /* if it is any other command that is not defined in our builtins
302 list then do the following */
306 sigprocing(SIG_UNBLOCK
);
307 /* give the child control over the terminal */
308 if((temp
== tcsetpgrp(0,-pid
))<0)
309 unix_error("Cannot take control of terminal");
311 /* execute the command using execvp */
312 if(execvp(tok
.argv
[0],tok
.argv
)==-1){
313 unix_error("Invalid Command");
318 sigprocing(SIG_UNBLOCK
);
320 /* execute the command using execvp */
321 if(execvp(tok
.argv
[0],tok
.argv
)==-1){
322 unix_error("Invalid Command");
326 /* if the child was running in the foreground */
329 addjob(job_list
,pid
,1,cmdline
); /* add the job to the list */
331 struct job_t
* job
= getjobpid(job_list
, pid
);
332 /* keep waiting for the job in the foreground to finish. Till
333 then suspend all the signals not in sus_mask */
334 while(job
&& job
->state
== FG
) {
335 sigsuspend(&sus_mask
);
337 /* unblock the signals which were blocked earlier in the
339 sigprocing(SIG_UNBLOCK
);
341 /* if the job is completed then delete it from the list */
343 deletejob(job_list
, pid
);
346 /* if child process was running in the background */
348 /* add the job to the job list */
349 addjob(job_list
,pid
,2,cmdline
);
350 /* unblock the signals blocked earlier in the method */
351 sigprocing(SIG_UNBLOCK
);
352 /* print the job id the proces id and the command */
353 printf("[%d] (%d) %s\n",pid2jid(pid
),pid
,cmdline
);
361 * parseline - Parse the command line and build the argv array.
363 * Characters enclosed in single quotes are treated as a single
364 * argument. Return true if the user has requested a BG job, false if
365 * the user has requested a FG job.
367 * Implement I/O redirection for extra credit.
369 int parseline(const char *cmdline
, struct cmdline_tokens
*tok
)
371 static char array
[MAXLINE
]; /* holds local copy of command line */
372 const char delims
[10] = " \t\r\n"; /* argument delimiters (white-space) */
373 char *buf
= array
; /* ptr that traverses command line */
374 char *next
; /* ptr to the end of the current arg */
375 char *endbuf
; /* ptr to the end of the cmdline string */
376 int bg
; /* background job? */
380 strcpy(buf
, cmdline
);
381 buf
[strlen(buf
)-1] = ' '; /* replace trailing '\n' with space */
382 while (*buf
&& (*buf
== ' ')) /* ignore leading spaces */
388 /* Build the argv list */
392 delim
= strchr(buf
, '\'');
395 delim
= strchr(buf
, ' ');
399 tok
->argv
[tok
->argc
++] = buf
;
402 while (*buf
&& (*buf
== ' ')) /* ignore spaces */
407 delim
= strchr(buf
, '\'');
410 delim
= strchr(buf
, ' ');
413 tok
->argv
[tok
->argc
] = NULL
;
415 if (tok
->argc
== 0) /* ignore blank line */
418 if (!strcmp(tok
->argv
[0], "quit")) { /* quit command */
419 tok
->builtins
= quit
;
420 } else if (!strcmp(tok
->argv
[0], "jobs")) { /* jobs command */
421 tok
->builtins
= jobs
;
422 } else if (!strcmp(tok
->argv
[0], "bg")) { /* bg command */
424 } else if (!strcmp(tok
->argv
[0], "fg")) { /* fg command */
427 tok
->builtins
= none
;
430 /* should the job run in the background? */
431 if ((bg
= (*tok
->argv
[tok
->argc
-1] == '&')) != 0) {
432 tok
->argv
[--tok
->argc
] = NULL
;
438 * builtin_cmd - If the user has typed a built-in command then execute
441 int builtin_cmd(char **argv
)
443 return 0; /* not a builtin command */
447 * do_bgfg - Execute the builtin bg and fg commands
449 void do_bgfg(char **argv
)
455 * waitfg - Block until process pid is no longer the foreground process
457 void waitfg(pid_t pid
)
467 * sigchld_handler - The kernel sends a SIGCHLD to the shell whenever
468 * a child job terminates (becomes a zombie), or stops because it
469 * received a SIGSTOP or SIGTSTP signal. The handler reaps all
470 * available zombie children, but doesn't wait for any other
471 * currently running children to terminate.
473 void sigchld_handler(int sig
)
476 int child_status
= -1;
478 /* reap any zombies and handle different statuses */
479 while((pid
= waitpid(-1, &child_status
, WUNTRACED
|WNOHANG
)) > 0){
480 int jid
= pid2jid(pid
);
481 /* handling all the stop signals */
482 if(WIFSTOPPED(child_status
)) {
483 printf("Job [%d] (%d) stopped by signal %d\n",
485 struct job_t
*job
= getjobpid(job_list
, pid
);
489 /* handling all uncaught signals */
490 else if(WIFSIGNALED(child_status
)) {
491 if(WTERMSIG(child_status
) == SIGINT
) {
492 printf("Job [%d] (%d) terminated by signal %d\n",
496 else if(WTERMSIG(child_status
) == SIGTERM
) {
497 printf("Job [%d] (%d) terminated by signal %d\n",
498 pid2jid(pid
),pid
,SIGTERM
);
502 unix_error("Uncaught signal");
505 /* understand why we did this */
506 deletejob(job_list
,pid
);
515 * sigint_handler - The kernel sends a SIGINT to the shell whenver the
516 * user types ctrl-c at the keyboard. Catch it and send it along
517 * to the foreground job.
519 void sigint_handler(int sig
)
522 pid_t pid
= fgpid(job_list
);
523 /* if there is a foreground job then kill it with the signal recieved */
531 * sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever
532 * the user types ctrl-z at the keyboard. Catch it and suspend the
533 * foreground job by sending it a SIGTSTP.
535 void sigtstp_handler(int sig
)
538 pid_t pid
= fgpid(job_list
);
539 /* if there is a foreground job then kill it with the signal recieved */
545 /*********************
546 * End signal handlers
547 *********************/
549 /***********************************************
550 * Helper routines that manipulate the job list
551 **********************************************/
553 /* clearjob - Clear the entries in a job struct */
554 void clearjob(struct job_t
*job
) {
558 job
->cmdline
[0] = '\0';
561 /* initjobs - Initialize the job list */
562 void initjobs(struct job_t
*jobs
) {
565 for (i
= 0; i
< MAXJOBS
; i
++)
569 /* maxjid - Returns largest allocated job ID */
570 int maxjid(struct job_t
*jobs
)
574 for (i
= 0; i
< MAXJOBS
; i
++)
575 if (jobs
[i
].jid
> max
)
580 /* addjob - Add a job to the job list */
581 int addjob(struct job_t
*jobs
, pid_t pid
, int state
, char *cmdline
)
588 for (i
= 0; i
< MAXJOBS
; i
++) {
589 if (jobs
[i
].pid
== 0) {
591 jobs
[i
].state
= state
;
592 jobs
[i
].jid
= nextjid
++;
593 if (nextjid
> MAXJOBS
)
595 strcpy(jobs
[i
].cmdline
, cmdline
);
597 printf("Added job [%d] %d %s\n", jobs
[i
].jid
, jobs
[i
].pid
, jobs
[i
].cmdline
);
602 printf("Tried to create too many jobs\n");
606 /* deletejob - Delete a job whose PID=pid from the job list */
607 int deletejob(struct job_t
*jobs
, pid_t pid
)
614 for (i
= 0; i
< MAXJOBS
; i
++) {
615 if (jobs
[i
].pid
== pid
) {
617 nextjid
= maxjid(jobs
)+1;
624 /* fgpid - Return PID of current foreground job, 0 if no such job */
625 pid_t
fgpid(struct job_t
*jobs
) {
628 for (i
= 0; i
< MAXJOBS
; i
++)
629 if (jobs
[i
].state
== FG
)
634 /* getjobpid - Find a job (by PID) on the job list */
635 struct job_t
*getjobpid(struct job_t
*jobs
, pid_t pid
) {
640 for (i
= 0; i
< MAXJOBS
; i
++)
641 if (jobs
[i
].pid
== pid
)
646 /* getjobjid - Find a job (by JID) on the job list */
647 struct job_t
*getjobjid(struct job_t
*jobs
, int jid
)
653 for (i
= 0; i
< MAXJOBS
; i
++)
654 if (jobs
[i
].jid
== jid
)
659 /* pid2jid - Map process ID to job ID */
660 int pid2jid(pid_t pid
)
666 for (i
= 0; i
< MAXJOBS
; i
++)
667 if (job_list
[i
].pid
== pid
) {
668 return job_list
[i
].jid
;
673 /* listjobs - Print the job list */
674 void listjobs(struct job_t
*jobs
)
678 for (i
= 0; i
< MAXJOBS
; i
++) {
679 if (jobs
[i
].pid
!= 0) {
680 printf("[%d] (%d) ", jobs
[i
].jid
, jobs
[i
].pid
);
681 switch (jobs
[i
].state
) {
686 printf("Foreground ");
692 printf("listjobs: Internal error: job[%d].state=%d ",
695 printf("%s", jobs
[i
].cmdline
);
699 /******************************
700 * end job list helper routines
701 ******************************/
704 /***********************
705 * Other helper routines
706 ***********************/
709 * usage - print a help message
713 printf("Usage: shell [-hvp]\n");
714 printf(" -h print this message\n");
715 printf(" -v print additional diagnostic information\n");
716 printf(" -p do not emit a command prompt\n");
721 * unix_error - unix-style error routine
723 void unix_error(char *msg
)
725 fprintf(stdout
, "%s: %s\n", msg
, strerror(errno
));
730 * app_error - application-style error routine
732 void app_error(char *msg
)
734 fprintf(stdout
, "%s\n", msg
);
739 * Signal - wrapper for the sigaction function
741 handler_t
*Signal(int signum
, handler_t
*handler
)
743 struct sigaction action
, old_action
;
745 action
.sa_handler
= handler
;
746 sigemptyset(&action
.sa_mask
); /* block sigs of type being handled */
747 action
.sa_flags
= SA_RESTART
; /* restart syscalls if possible */
749 if (sigaction(signum
, &action
, &old_action
) < 0)
750 unix_error("Signal error");
751 return (old_action
.sa_handler
);
755 * sigquit_handler - The driver program can gracefully terminate the
756 * child shell by sending it a SIGQUIT signal.
758 void sigquit_handler(int sig
)
760 printf("Terminating after receipt of SIGQUIT signal\n");
764 /* helper function to either block or unblock signals using sigprocmask */
765 void sigprocing(int var
){
767 /* emptying the sigset mask */
768 if(sigemptyset(&mask
)){
769 unix_error("sigemptyset() error");
771 /* adding SIGCHLD, SIGINT and SIGTSTP to the sigset */
772 if(sigaddset(&mask
, SIGCHLD
)){
773 unix_error("sigaddset() error");
776 if(sigaddset(&mask
, SIGINT
)){
777 unix_error("sigaddset() error");
780 if(sigaddset(&mask
, SIGTSTP
)){
781 unix_error("sigaddset() error");
783 /* block or unblock the signals in the mask depending on the argument in
785 if(sigprocmask(var
, &mask
, NULL
)){
786 unix_error("sigprocmask() error");