tutorial slide
[tsh-lab.git] / solution.c
blobb06b0c7fc87e0df3b56b72d00f6939927b8496eb
1 /*
2 * tsh - A tiny shell program with job control
3 *
4 * <Put your name and login ID here>
5 */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <signal.h>
12 #include <sys/types.h>
13 #include <sys/wait.h>
14 #include <errno.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 */
22 /* Job states */
23 #define UNDEF 0 /* undefined */
24 #define FG 1 /* running in foreground */
25 #define BG 2 /* running in background */
26 #define ST 3 /* stopped */
28 /*Parse states */
29 #define TNORMAL 0x0 /*argument*/
30 #define TINFILE 0x1 /*input file*/
31 #define TOUTFILE 0x2 /*output file*/
33 /*
34 * Jobs states: FG (foreground), BG (background), ST (stopped)
35 * Job state transitions and enabling actions:
36 * FG -> ST : ctrl-z
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);
97 void usage(void);
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)
108 char c;
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) */
114 dup2(1, 2);
116 /* Parse the command line */
117 while ((c = getopt(argc, argv, "hvp")) != EOF) {
118 switch (c) {
119 case 'h': /* print help message */
120 usage();
121 break;
122 case 'v': /* emit additional diagnostic info */
123 verbose = 1;
124 break;
125 case 'p': /* don't print a prompt */
126 emit_prompt = 0; /* handy for automatic testing */
127 break;
128 default:
129 usage();
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 */
144 initjobs(job_list);
146 /* Execute the shell's read/eval loop */
147 while (1) {
149 /* Read command line */
150 if (emit_prompt) {
151 printf("%s", prompt);
152 fflush(stdout);
154 if ((fgets(cmdline, MAXLINE, stdin) == NULL) && ferror(stdin))
155 app_error("fgets error");
156 if (feof(stdin)) { /* End of file (ctrl-d) */
157 fflush(stdout);
158 exit(0);
161 /* Evaluate the command line */
162 eval(cmdline);
163 fflush(stdout);
164 fflush(stdout);
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;
185 sigset_t sus_mask;
186 int fin, fout, retval, temp;
187 pid_t pid;
188 struct job_t *j;
189 char *pidorjid;
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) {
207 exit(0);
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");
235 else{
236 unix_error("Please enter a pid or jid as argument\n");
239 return;
241 /* continue the jobs in the group */
242 if(kill(-(j->pid),SIGCONT) < 0){
243 return;
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);
252 j->state = BG;
254 else if(tok.builtins == fg){
255 j->state = FG;
256 while(j->pid == fgpid(job_list)>0);
258 else{
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 */
265 else{
266 /* fork a child process to do the required commands */
267 if((pid = fork()) == 0){
268 /* setting proces group id for the child */
269 if(setpgid(0,0) < 0)
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");
278 dup2(fin,0);
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");
289 dup2(fout,1);
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){
298 listjobs(job_list);
299 exit(0);
301 /* if it is any other command that is not defined in our builtins
302 list then do the following */
303 else{
304 /* foreground */
305 if(state ==0){
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");
316 /* background */
317 else if(state == 1){
318 sigprocing(SIG_UNBLOCK);
320 /* execute the command using execvp */
321 if(execvp(tok.argv[0],tok.argv)==-1){
322 unix_error("Invalid Command");
325 else{
326 /* if the child was running in the foreground */
327 if(state == 0){
328 child_status = 0;
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
338 method */
339 sigprocing(SIG_UNBLOCK);
341 /* if the job is completed then delete it from the list */
342 if(!job)
343 deletejob(job_list, pid);
346 /* if child process was running in the background */
347 else if(state == 1){
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);
356 return;
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? */
377 int parse_state;
378 char *delim;
380 strcpy(buf, cmdline);
381 buf[strlen(buf)-1] = ' '; /* replace trailing '\n' with space */
382 while (*buf && (*buf == ' ')) /* ignore leading spaces */
383 buf++;
385 tok->infile = NULL;
386 tok->outfile = NULL;
388 /* Build the argv list */
389 tok->argc = 0;
390 if (*buf == '\'') {
391 buf++;
392 delim = strchr(buf, '\'');
394 else {
395 delim = strchr(buf, ' ');
398 while (delim) {
399 tok->argv[tok->argc++] = buf;
400 *delim = '\0';
401 buf = delim + 1;
402 while (*buf && (*buf == ' ')) /* ignore spaces */
403 buf++;
405 if (*buf == '\'') {
406 buf++;
407 delim = strchr(buf, '\'');
409 else {
410 delim = strchr(buf, ' ');
413 tok->argv[tok->argc] = NULL;
415 if (tok->argc == 0) /* ignore blank line */
416 return 1;
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 */
423 tok->builtins = bg;
424 } else if (!strcmp(tok->argv[0], "fg")) { /* fg command */
425 tok->builtins = fg;
426 } else {
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;
434 return bg;
438 * builtin_cmd - If the user has typed a built-in command then execute
439 * it immediately.
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)
451 return;
455 * waitfg - Block until process pid is no longer the foreground process
457 void waitfg(pid_t pid)
459 return;
462 /*****************
463 * Signal handlers
464 *****************/
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)
475 pid_t pid;
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",
484 jid,pid,SIGTSTP);
485 struct job_t *job = getjobpid(job_list, pid);
486 job->state = ST;
487 return;
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",
493 jid,pid,SIGINT);
496 else if(WTERMSIG(child_status) == SIGTERM) {
497 printf("Job [%d] (%d) terminated by signal %d\n",
498 pid2jid(pid),pid,SIGTERM);
501 else{
502 unix_error("Uncaught signal");
505 /* understand why we did this */
506 deletejob(job_list,pid);
507 child_status = 0;
510 return;
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)
521 /*foreground job */
522 pid_t pid = fgpid(job_list);
523 /* if there is a foreground job then kill it with the signal recieved */
524 if(pid != 0)
525 kill(-pid,sig);
526 return;
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)
537 /*foreground job */
538 pid_t pid = fgpid(job_list);
539 /* if there is a foreground job then kill it with the signal recieved */
540 if(pid != 0)
541 kill(-pid,sig);
542 return;
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) {
555 job->pid = 0;
556 job->jid = 0;
557 job->state = UNDEF;
558 job->cmdline[0] = '\0';
561 /* initjobs - Initialize the job list */
562 void initjobs(struct job_t *jobs) {
563 int i;
565 for (i = 0; i < MAXJOBS; i++)
566 clearjob(&jobs[i]);
569 /* maxjid - Returns largest allocated job ID */
570 int maxjid(struct job_t *jobs)
572 int i, max=0;
574 for (i = 0; i < MAXJOBS; i++)
575 if (jobs[i].jid > max)
576 max = jobs[i].jid;
577 return max;
580 /* addjob - Add a job to the job list */
581 int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline)
583 int i;
585 if (pid < 1)
586 return 0;
588 for (i = 0; i < MAXJOBS; i++) {
589 if (jobs[i].pid == 0) {
590 jobs[i].pid = pid;
591 jobs[i].state = state;
592 jobs[i].jid = nextjid++;
593 if (nextjid > MAXJOBS)
594 nextjid = 1;
595 strcpy(jobs[i].cmdline, cmdline);
596 if(verbose){
597 printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline);
599 return 1;
602 printf("Tried to create too many jobs\n");
603 return 0;
606 /* deletejob - Delete a job whose PID=pid from the job list */
607 int deletejob(struct job_t *jobs, pid_t pid)
609 int i;
611 if (pid < 1)
612 return 0;
614 for (i = 0; i < MAXJOBS; i++) {
615 if (jobs[i].pid == pid) {
616 clearjob(&jobs[i]);
617 nextjid = maxjid(jobs)+1;
618 return 1;
621 return 0;
624 /* fgpid - Return PID of current foreground job, 0 if no such job */
625 pid_t fgpid(struct job_t *jobs) {
626 int i;
628 for (i = 0; i < MAXJOBS; i++)
629 if (jobs[i].state == FG)
630 return jobs[i].pid;
631 return 0;
634 /* getjobpid - Find a job (by PID) on the job list */
635 struct job_t *getjobpid(struct job_t *jobs, pid_t pid) {
636 int i;
638 if (pid < 1)
639 return NULL;
640 for (i = 0; i < MAXJOBS; i++)
641 if (jobs[i].pid == pid)
642 return &jobs[i];
643 return NULL;
646 /* getjobjid - Find a job (by JID) on the job list */
647 struct job_t *getjobjid(struct job_t *jobs, int jid)
649 int i;
651 if (jid < 1)
652 return NULL;
653 for (i = 0; i < MAXJOBS; i++)
654 if (jobs[i].jid == jid)
655 return &jobs[i];
656 return NULL;
659 /* pid2jid - Map process ID to job ID */
660 int pid2jid(pid_t pid)
662 int i;
664 if (pid < 1)
665 return 0;
666 for (i = 0; i < MAXJOBS; i++)
667 if (job_list[i].pid == pid) {
668 return job_list[i].jid;
670 return 0;
673 /* listjobs - Print the job list */
674 void listjobs(struct job_t *jobs)
676 int i;
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) {
682 case BG:
683 printf("Running ");
684 break;
685 case FG:
686 printf("Foreground ");
687 break;
688 case ST:
689 printf("Stopped ");
690 break;
691 default:
692 printf("listjobs: Internal error: job[%d].state=%d ",
693 i, jobs[i].state);
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
711 void usage(void)
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");
717 exit(1);
721 * unix_error - unix-style error routine
723 void unix_error(char *msg)
725 fprintf(stdout, "%s: %s\n", msg, strerror(errno));
726 exit(1);
730 * app_error - application-style error routine
732 void app_error(char *msg)
734 fprintf(stdout, "%s\n", msg);
735 exit(1);
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");
761 exit(1);
764 /* helper function to either block or unblock signals using sigprocmask */
765 void sigprocing(int var){
766 sigset_t mask;
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
784 the function */
785 if(sigprocmask(var, &mask, NULL)){
786 unix_error("sigprocmask() error");
789 return;