tutorial slide
[tsh-lab.git] / tsh.c
blob6d9813a2b7a1467a8ea7afd39e181c45a45285f6
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 return;
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? */
203 int parse_state;
204 char *delim;
206 strcpy(buf, cmdline);
207 buf[strlen(buf)-1] = ' '; /* replace trailing '\n' with space */
208 while (*buf && (*buf == ' ')) /* ignore leading spaces */
209 buf++;
211 tok->infile = NULL;
212 tok->outfile = NULL;
214 /* Build the argv list */
215 tok->argc = 0;
216 if (*buf == '\'') {
217 buf++;
218 delim = strchr(buf, '\'');
220 else {
221 delim = strchr(buf, ' ');
224 while (delim) {
225 tok->argv[tok->argc++] = buf;
226 *delim = '\0';
227 buf = delim + 1;
228 while (*buf && (*buf == ' ')) /* ignore spaces */
229 buf++;
231 if (*buf == '\'') {
232 buf++;
233 delim = strchr(buf, '\'');
235 else {
236 delim = strchr(buf, ' ');
239 tok->argv[tok->argc] = NULL;
241 if (tok->argc == 0) /* ignore blank line */
242 return 1;
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 */
249 tok->builtins = bg;
250 } else if (!strcmp(tok->argv[0], "fg")) { /* fg command */
251 tok->builtins = fg;
252 } else {
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;
260 return bg;
264 * builtin_cmd - If the user has typed a built-in command then execute
265 * it immediately.
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)
277 return;
281 * waitfg - Block until process pid is no longer the foreground process
283 void waitfg(pid_t pid)
285 return;
288 /*****************
289 * Signal handlers
290 *****************/
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)
301 return;
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)
311 return;
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)
321 return;
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) {
334 job->pid = 0;
335 job->jid = 0;
336 job->state = UNDEF;
337 job->cmdline[0] = '\0';
340 /* initjobs - Initialize the job list */
341 void initjobs(struct job_t *jobs) {
342 int i;
344 for (i = 0; i < MAXJOBS; i++)
345 clearjob(&jobs[i]);
348 /* maxjid - Returns largest allocated job ID */
349 int maxjid(struct job_t *jobs)
351 int i, max=0;
353 for (i = 0; i < MAXJOBS; i++)
354 if (jobs[i].jid > max)
355 max = jobs[i].jid;
356 return max;
359 /* addjob - Add a job to the job list */
360 int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline)
362 int i;
364 if (pid < 1)
365 return 0;
367 for (i = 0; i < MAXJOBS; i++) {
368 if (jobs[i].pid == 0) {
369 jobs[i].pid = pid;
370 jobs[i].state = state;
371 jobs[i].jid = nextjid++;
372 if (nextjid > MAXJOBS)
373 nextjid = 1;
374 strcpy(jobs[i].cmdline, cmdline);
375 if(verbose){
376 printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline);
378 return 1;
381 printf("Tried to create too many jobs\n");
382 return 0;
385 /* deletejob - Delete a job whose PID=pid from the job list */
386 int deletejob(struct job_t *jobs, pid_t pid)
388 int i;
390 if (pid < 1)
391 return 0;
393 for (i = 0; i < MAXJOBS; i++) {
394 if (jobs[i].pid == pid) {
395 clearjob(&jobs[i]);
396 nextjid = maxjid(jobs)+1;
397 return 1;
400 return 0;
403 /* fgpid - Return PID of current foreground job, 0 if no such job */
404 pid_t fgpid(struct job_t *jobs) {
405 int i;
407 for (i = 0; i < MAXJOBS; i++)
408 if (jobs[i].state == FG)
409 return jobs[i].pid;
410 return 0;
413 /* getjobpid - Find a job (by PID) on the job list */
414 struct job_t *getjobpid(struct job_t *jobs, pid_t pid) {
415 int i;
417 if (pid < 1)
418 return NULL;
419 for (i = 0; i < MAXJOBS; i++)
420 if (jobs[i].pid == pid)
421 return &jobs[i];
422 return NULL;
425 /* getjobjid - Find a job (by JID) on the job list */
426 struct job_t *getjobjid(struct job_t *jobs, int jid)
428 int i;
430 if (jid < 1)
431 return NULL;
432 for (i = 0; i < MAXJOBS; i++)
433 if (jobs[i].jid == jid)
434 return &jobs[i];
435 return NULL;
438 /* pid2jid - Map process ID to job ID */
439 int pid2jid(pid_t pid)
441 int i;
443 if (pid < 1)
444 return 0;
445 for (i = 0; i < MAXJOBS; i++)
446 if (job_list[i].pid == pid) {
447 return job_list[i].jid;
449 return 0;
452 /* listjobs - Print the job list */
453 void listjobs(struct job_t *jobs)
455 int i;
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) {
461 case BG:
462 printf("Running ");
463 break;
464 case FG:
465 printf("Foreground ");
466 break;
467 case ST:
468 printf("Stopped ");
469 break;
470 default:
471 printf("listjobs: Internal error: job[%d].state=%d ",
472 i, jobs[i].state);
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
490 void usage(void)
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");
496 exit(1);
500 * unix_error - unix-style error routine
502 void unix_error(char *msg)
504 fprintf(stdout, "%s: %s\n", msg, strerror(errno));
505 exit(1);
509 * app_error - application-style error routine
511 void app_error(char *msg)
513 fprintf(stdout, "%s\n", msg);
514 exit(1);
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");
540 exit(1);