/*
* tsh - A tiny shell program with job control
*
* 1600011090 Chen Haoran
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <errno.h>
/* Misc manifest constants */
#define MAXLINE 1024 /* max line size */
#define MAXARGS 128 /* max args on a command line */
#define MAXJOBS 16 /* max jobs at any point in time */
#define MAXJID 1<<16 /* max job ID */
/* Job states */
#define UNDEF 0 /* undefined */
#define FG 1 /* running in foreground */
#define BG 2 /* running in background */
#define ST 3 /* stopped */
/*
* Jobs states: FG (foreground), BG (background), ST (stopped)
* Job state transitions and enabling actions:
* FG -> ST : ctrl-z
* ST -> FG : fg command
* ST -> BG : bg command
* BG -> FG : fg command
* At most 1 job can be in the FG state.
*/
/* Parsing states */
#define ST_NORMAL 0x0 /* next token is an argument */
#define ST_INFILE 0x1 /* next token is the input file */
#define ST_OUTFILE 0x2 /* next token is the output file */
/* Global variables */
extern char **environ; /* defined in libc */
char prompt[] = "tsh> "; /* command line prompt (DO NOT CHANGE) */
int verbose = 0; /* if true, print additional output */
int nextjid = 1; /* next job ID to allocate */
char sbuf[MAXLINE]; /* for composing sprintf messages */
struct job_t { /* The job struct */
pid_t pid; /* job PID */
int jid; /* job ID [1, 2, ...] */
int state; /* UNDEF, BG, FG, or ST */
char cmdline[MAXLINE]; /* command line */
};
struct job_t job_list[MAXJOBS]; /* The job list */
struct cmdline_tokens {
int argc; /* Number of arguments */
char *argv[MAXARGS]; /* The arguments list */
char *infile; /* The input file */
char *outfile; /* The output file */
enum builtins_t { /* Indicates if argv[0] is a builtin command */
BUILTIN_NONE,
BUILTIN_QUIT,
BUILTIN_JOBS,
BUILTIN_BG,
BUILTIN_FG} builtins;
};
/* End global variables */
/* Function prototypes */
void eval(char *cmdline);
void sigchld_handler(int sig);
void sigtstp_handler(int sig);
void sigint_handler(int sig);
/* Here are helper routines that we've provided for you */
int parseline(const char *cmdline, struct cmdline_tokens *tok);
void sigquit_handler(int sig);
void clearjob(struct job_t *job);
void initjobs(struct job_t *job_list);
int maxjid(struct job_t *job_list);
int addjob(struct job_t *job_list, pid_t pid, int state, char *cmdline);
int deletejob(struct job_t *job_list, pid_t pid);
pid_t fgpid(struct job_t *job_list);
struct job_t *getjobpid(struct job_t *job_list, pid_t pid);
struct job_t *getjobjid(struct job_t *job_list, int jid);
int pid2jid(pid_t pid);
void listjobs(struct job_t *job_list, int output_fd);
void usage(void);
void unix_error(char *msg);
void app_error(char *msg);
ssize_t sio_puts(char s[]);
ssize_t sio_putl(long v);
void sio_error(char s[]);
typedef void handler_t(int);
handler_t *Signal(int signum, handler_t *handler);
void redictionIO(struct cmdline_tokens *tok);
void restoreIO(struct cmdline_tokens *tok);
int buildin_cmd(struct cmdline_tokens *tok);
void op_bgfg(struct cmdline_tokens *tok);
/*
* main - The shell's main routine
*/
int
main(int argc, char **argv)
{
char c;
char cmdline[MAXLINE]; /* cmdline for fgets */
int emit_prompt = 1; /* emit prompt (default) */
/* Redirect stderr to stdout (so that driver will get all output
* on the pipe connected to stdout) */
dup2(1, 2);
/* Parse the command line */
while ((c = getopt(argc, argv, "hvp")) != EOF) {
switch (c) {
case 'h': /* print help message */
usage();
break;
case 'v': /* emit additional diagnostic info */
verbose = 1;
break;
case 'p': /* don't print a prompt */
emit_prompt = 0; /* handy for automatic testing */
break;
default:
usage();
}
}
/* Install the signal handlers */
/* These are the ones you will need to implement */
Signal(SIGINT, sigint_handler); /* ctrl-c */
Signal(SIGTSTP, sigtstp_handler); /* ctrl-z */
Signal(SIGCHLD, sigchld_handler); /* Terminated or stopped child */
Signal(SIGTTIN, SIG_IGN);
Signal(SIGTTOU, SIG_IGN);
/* This one provides a clean way to kill the shell */
Signal(SIGQUIT, sigquit_handler);
/* Initialize the job list */
initjobs(job_list);
/* Execute the shell's read/eval loop */
while (1) {
if (emit_prompt) {
printf("%s", prompt);
fflush(stdout);
}
if ((fgets(cmdline, MAXLINE, stdin) == NULL) && ferror(stdin))
app_error("fgets error");
if (feof(stdin)) {
/* End of file (ctrl-d) */
printf ("\n");
fflush(stdout);
fflush(stderr);
exit(0);
}
/* Remove the trailing newline */
cmdline[strlen(cmdline)-1] = '\0';
/* Evaluate the command line */
eval(cmdline);
fflush(stdout);
fflush(stdout);
}
exit(0); /* control never reaches here */
}
/*
* eval - Evaluate the command line that the user has just typed in
*
* If the user has requested a built-in command (quit, jobs, bg or fg)
* then execute it immediately. Otherwise, fork a child process and
* run the job in the context of the child. If the job is running in
* the foreground, wait for it to terminate and then return. Note:
* each child process must have a unique process group ID so that our
* background children don't receive SIGINT (SIGTSTP) from the kernel
* when we type ctrl-c (ctrl-z) at the keyboard.
*/
int infd=-1,outfd=-1,stdIN,stdOUT;
void
eval(char *cmdline)
{
int bg; /* should the job run in bg or fg? */
struct cmdline_tokens tok;
/* Parse command line */
bg = parseline(cmdline, &tok);
if (bg == -1) /* parsing error */
return;
if (tok.argv[0] == NULL) /* ignore empty lines */
return;
/*block set*/
sigset_t mask,pre_mask;
sigemptyset(&mask);
sigfillset(&mask);
//call the function to rediction I/O stream
redictionIO(&tok);
if (!buildin_cmd(&tok))//the cmd is not a buildin_cmd
{
//block the signal
sigprocmask(SIG_BLOCK,&mask,&pre_mask);
volatile sig_atomic_t pid;
// in the child process
if((pid=fork())==0)
{
sigprocmask(SIG_SETMASK,&pre_mask,NULL);
//unblock the signal
setpgid(0, 0);
execve(tok.argv[0],tok.argv,environ) ;
}
//in the parent process
else
{
// get the bg/fg state
int state;
if(bg==1)state=BG;
else state=FG;
addjob(job_list,pid,state,cmdline);
//unblock the parent process
sigprocmask(SIG_SETMASK,&pre_mask,NULL);
if(!bg)
{
//wait until the fg process ends
while(pid==fgpid(job_list))
sigsuspend(&pre_mask);
}
else
{
printf("[%d] (%d) %s\n",pid2jid(pid),pid,cmdline);
}
}
}
//call the function to restore I/O stream
restoreIO(&tok);
return;
}
// the redirction function
void redictionIO(struct cmdline_tokens *tok)
{
stdIN = dup(STDIN_FILENO);
stdOUT = dup(STDOUT_FILENO);
if (tok->infile != NULL) {
infd = open(tok->infile, O_RDWR, 0);
dup2(infd, STDIN