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
)
187 * parseline - Parse the command line and build the argv array.
189 * Characters enclosed in single quotes are treated as a single
190 * argument. Return true if the user has requested a BG job, false if
191 * the user has requested a FG job.
193 * Implement I/O redirection for extra credit.
195 int parseline(const char *cmdline
, struct cmdline_tokens
*tok
)
197 static char array
[MAXLINE
]; /* holds local copy of command line */
198 const char delims
[10] = " \t\r\n"; /* argument delimiters (white-space) */
199 char *buf
= array
; /* ptr that traverses command line */
200 char *next
; /* ptr to the end of the current arg */
201 char *endbuf
; /* ptr to the end of the cmdline string */
202 int bg
; /* background job? */
206 strcpy(buf
, cmdline
);
207 buf
[strlen(buf
)-1] = ' '; /* replace trailing '\n' with space */
208 while (*buf
&& (*buf
== ' ')) /* ignore leading spaces */
214 /* Build the argv list */
218 delim
= strchr(buf
, '\'');
221 delim
= strchr(buf
, ' ');
225 tok
->argv
[tok
->argc
++] = buf
;
228 while (*buf
&& (*buf
== ' ')) /* ignore spaces */
233 delim
= strchr(buf
, '\'');
236 delim
= strchr(buf
, ' ');
239 tok
->argv
[tok
->argc
] = NULL
;
241 if (tok
->argc
== 0) /* ignore blank line */
244 if (!strcmp(tok
->argv
[0], "quit")) { /* quit command */
245 tok
->builtins
= quit
;
246 } else if (!strcmp(tok
->argv
[0], "jobs")) { /* jobs command */
247 tok
->builtins
= jobs
;
248 } else if (!strcmp(tok
->argv
[0], "bg")) { /* bg command */
250 } else if (!strcmp(tok
->argv
[0], "fg")) { /* fg command */
253 tok
->builtins
= none
;
256 /* should the job run in the background? */
257 if ((bg
= (*tok
->argv
[tok
->argc
-1] == '&')) != 0) {
258 tok
->argv
[--tok
->argc
] = NULL
;
264 * builtin_cmd - If the user has typed a built-in command then execute
267 int builtin_cmd(char **argv
)
269 return 0; /* not a builtin command */
273 * do_bgfg - Execute the builtin bg and fg commands
275 void do_bgfg(char **argv
)
281 * waitfg - Block until process pid is no longer the foreground process
283 void waitfg(pid_t pid
)
293 * sigchld_handler - The kernel sends a SIGCHLD to the shell whenever
294 * a child job terminates (becomes a zombie), or stops because it
295 * received a SIGSTOP or SIGTSTP signal. The handler reaps all
296 * available zombie children, but doesn't wait for any other
297 * currently running children to terminate.
299 void sigchld_handler(int sig
)
305 * sigint_handler - The kernel sends a SIGINT to the shell whenver the
306 * user types ctrl-c at the keyboard. Catch it and send it along
307 * to the foreground job.
309 void sigint_handler(int sig
)
315 * sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever
316 * the user types ctrl-z at the keyboard. Catch it and suspend the
317 * foreground job by sending it a SIGTSTP.
319 void sigtstp_handler(int sig
)
324 /*********************
325 * End signal handlers
326 *********************/
328 /***********************************************
329 * Helper routines that manipulate the job list
330 **********************************************/
332 /* clearjob - Clear the entries in a job struct */
333 void clearjob(struct job_t
*job
) {
337 job
->cmdline
[0] = '\0';
340 /* initjobs - Initialize the job list */
341 void initjobs(struct job_t
*jobs
) {
344 for (i
= 0; i
< MAXJOBS
; i
++)
348 /* maxjid - Returns largest allocated job ID */
349 int maxjid(struct job_t
*jobs
)
353 for (i
= 0; i
< MAXJOBS
; i
++)
354 if (jobs
[i
].jid
> max
)
359 /* addjob - Add a job to the job list */
360 int addjob(struct job_t
*jobs
, pid_t pid
, int state
, char *cmdline
)
367 for (i
= 0; i
< MAXJOBS
; i
++) {
368 if (jobs
[i
].pid
== 0) {
370 jobs
[i
].state
= state
;
371 jobs
[i
].jid
= nextjid
++;
372 if (nextjid
> MAXJOBS
)
374 strcpy(jobs
[i
].cmdline
, cmdline
);
376 printf("Added job [%d] %d %s\n", jobs
[i
].jid
, jobs
[i
].pid
, jobs
[i
].cmdline
);
381 printf("Tried to create too many jobs\n");
385 /* deletejob - Delete a job whose PID=pid from the job list */
386 int deletejob(struct job_t
*jobs
, pid_t pid
)
393 for (i
= 0; i
< MAXJOBS
; i
++) {
394 if (jobs
[i
].pid
== pid
) {
396 nextjid
= maxjid(jobs
)+1;
403 /* fgpid - Return PID of current foreground job, 0 if no such job */
404 pid_t
fgpid(struct job_t
*jobs
) {
407 for (i
= 0; i
< MAXJOBS
; i
++)
408 if (jobs
[i
].state
== FG
)
413 /* getjobpid - Find a job (by PID) on the job list */
414 struct job_t
*getjobpid(struct job_t
*jobs
, pid_t pid
) {
419 for (i
= 0; i
< MAXJOBS
; i
++)
420 if (jobs
[i
].pid
== pid
)
425 /* getjobjid - Find a job (by JID) on the job list */
426 struct job_t
*getjobjid(struct job_t
*jobs
, int jid
)
432 for (i
= 0; i
< MAXJOBS
; i
++)
433 if (jobs
[i
].jid
== jid
)
438 /* pid2jid - Map process ID to job ID */
439 int pid2jid(pid_t pid
)
445 for (i
= 0; i
< MAXJOBS
; i
++)
446 if (job_list
[i
].pid
== pid
) {
447 return job_list
[i
].jid
;
452 /* listjobs - Print the job list */
453 void listjobs(struct job_t
*jobs
)
457 for (i
= 0; i
< MAXJOBS
; i
++) {
458 if (jobs
[i
].pid
!= 0) {
459 printf("[%d] (%d) ", jobs
[i
].jid
, jobs
[i
].pid
);
460 switch (jobs
[i
].state
) {
465 printf("Foreground ");
471 printf("listjobs: Internal error: job[%d].state=%d ",
474 printf("%s", jobs
[i
].cmdline
);
478 /******************************
479 * end job list helper routines
480 ******************************/
483 /***********************
484 * Other helper routines
485 ***********************/
488 * usage - print a help message
492 printf("Usage: shell [-hvp]\n");
493 printf(" -h print this message\n");
494 printf(" -v print additional diagnostic information\n");
495 printf(" -p do not emit a command prompt\n");
500 * unix_error - unix-style error routine
502 void unix_error(char *msg
)
504 fprintf(stdout
, "%s: %s\n", msg
, strerror(errno
));
509 * app_error - application-style error routine
511 void app_error(char *msg
)
513 fprintf(stdout
, "%s\n", msg
);
518 * Signal - wrapper for the sigaction function
520 handler_t
*Signal(int signum
, handler_t
*handler
)
522 struct sigaction action
, old_action
;
524 action
.sa_handler
= handler
;
525 sigemptyset(&action
.sa_mask
); /* block sigs of type being handled */
526 action
.sa_flags
= SA_RESTART
; /* restart syscalls if possible */
528 if (sigaction(signum
, &action
, &old_action
) < 0)
529 unix_error("Signal error");
530 return (old_action
.sa_handler
);
534 * sigquit_handler - The driver program can gracefully terminate the
535 * child shell by sending it a SIGQUIT signal.
537 void sigquit_handler(int sig
)
539 printf("Terminating after receipt of SIGQUIT signal\n");