CSAPP 实验V ShellLab

前面的实验都要在 shell 中运行,这次我们直接做一个自己的 shell!这之后无论是异常还是信号,都难不倒我们啦。

更新历史

  • 24.03.14:初稿

系列

任务目标

ShellLab实验,理解程序控制和信号,完成一个简单Shell。

相关内容

  • tsh> /bin/ls -l -d&后台运行

  • int main(int argc, char *argv[])

    • argc:参数个数 argv[]:从左往右的参数

    • ctrl-c SIGINT终止信号,ctrl-zSIGTSTP停止信号,SIGCONT继续信号

    • jobs:列出所有作业

    • bg <job>:后台暂停的作业开始运行

    • fg <job>:前台暂停的作业开始运行

    • kill <job>:杀死作业

  • tsh的规格

    • 提示符为tsh>

    • 输入命令为name arguments:如果name是内置命令,直接执行,否则视为可执行文件路径,并调用初始子进程运行

    • 不需要支持|<>

    • 信号需要传递到所有子进程

    • 命令末尾带&,后台运行

    • 进程PID%5,作业JID5

    • 需要支持内置命令quitjobsbg <job>fg <job>

    • 如果僵尸进程没有收到信号就终止了,需要打印出异常信息

  • 检查作业

    • tshref:样例tsh,可以对比自己的shell

    • sdriver.pl:打印输入的shell命令和输出信息
      ./sdriver.pl -t trace01.txt -s ./tsh -a "-p"-t追踪文件,-s指定shell,-a "-p"不输出提示符

    • 也可以使用make test01测试自己的shell,make rtest01测试样例shell

    • trace01.txt序号从01-16,越小测试越简单

    • tshref.out所有的输出信息以供参考

  • 提示

    • 根据追踪文件来设计shell,依次从简单开始,按照输出完成设计

    • 要检测出kill -pid的错误

    • 多使用下列函数

      • pid_t waitpid (pid_t pid, int* statusp, int options)

        成功时返回子进程的PID,

        options = WNOHANG时立即返回0 表示没有任何子进程终止

        options = WUNTRACED时,接收到SIGSTOP信号,终止或暂停。

        如果发生其他错误返回-1。

      • kill:kill -s <信号>

        用指定信号形式终止进程,可以杀死可以暂停

        -s,会将s的进程组全部执行

      • int fork(void):

        创建一个子进程,子进程返回0,父进程返回子进程的ID,调用一次返回两次,先返回子进程,再返回父进程

      • int execve(const char *filename, char *const argv[], char *const envp[]);:

        参数为新程序的路径名称,命令行参数数组,环境变量参数数组

        替换进程的状态和上下文,切换另一个进程

        执行成功不会返回到调用,错误返回-1

      • int setpgid(pid_t pid, pid_t pgid):

        给pid进程设置为pgid进程组识别码,如果pid为0设置当前进程,pgid为0,用当前组识别码取代,返回为组识别码,-1错误

      • sigprocmash:

        int sigprocmask(int how, const sigset_t set, sigset_t oldset);
        参数:
        how:用于指定信号修改的方式,可能选择有三种:
        SIG_BLOCK //加入信号到进程屏蔽。
        SIG_UNBLOCK //从进程屏蔽里将信号删除。
        SIG_SETMASK //将set的值设定为新的进程屏蔽。

        set:为指向信号集的指针,在此专指新设的信号集,如果仅想读取现在的屏蔽值,可将其置为NULL。
        oldset:也是指向信号集的指针,在此存放原来的信号集。
        返回说明:
        成功执行时,返回0。失败返回-1,errno被设为EINVAL。

      • exit(int status)

        终止进程,正常返回状态0

      • 尽量不要在自己的shell中使用more, less, vi, emacs,多使用/bin/ls,/bin/ps, /bin/echo

      • 如果在Unix shell中像自己的shell输入 ctrl-c:fork之后执行之前,子进程先调用setpgid(0, 0),放入一个新的进程组,保证Unix shell的进程组中只有shell。不会把命令传递给所有Unix shell的进程组,只传递给shell。

      • 建议在waitfg的循环中用sleep函数,在sigchld_handler中调用waitpid

  • 常用信号量

题目

第一部分

trace01-03比较简单,用课本中代码写入就可以,修改一下fork和execve封装。

第二部分

trace04

根据标准输出

./sdriver.pl -t trace04.txt -s ./tsh -a "-p"
#
# trace04.txt - Run a background job.
#
tsh> ./myspin 1 &
[1] (26252) ./myspin 1 &

在主函数执行中增加添加作业函数addjob,修改一下原来的输出格式,printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline);

问题:

tsh> ./myspin 1 &
[2] (920) ./myspin 1 &

作业从[2]标号开始,可能是重复添加作业了。查看输出后,发现echo函数添加作业后,运行结束时没有删除作业,导致作业号多一个。

解决办法:

在waitpid之后,增加一个删除作业函数deletejob

trace05

增加打印jobs列表

trace06

#
# trace06.txt - Forward SIGINT to foreground job.
#
/bin/echo -e tsh> ./myspin 4
./myspin 4

SLEEP 2
INT

SLEEP命令不是输入进去的字符串,主要是获取作业的终止状态

相关内容

  • waitpid返回状态status

    • WIFEXITED(status):非0正常结束,退出状态WEXITSTATUS(status)

    • WIFSIGNALED(status):非0异常终止,退出的信号编号WTERMSIG(status)

    • WIFSTOPPED(status):非0暂停状态,暂停的信号编号WSTOPSIG(status)

    • WIFCONTINUED(status):非0 暂停后以继续运行

  • options变量

    • WNOHANG:没有子进程终止时立即返回,而不会阻塞。

    • WUNTRACED:挂起,返回终止或暂停的进程pid

    • WCONTINUED:挂起,返回一个正在运行的进程终止,或一个暂停的收到SIGCONT信号继续执行。

  • 信号量相关

    • sigfillset(&mask_all);返回当前进程的信号阻塞集合

    • sigemptyset(&mask_one);初试空值

    • sigaddset(&mask_one, SIGCHLD);,添加一位阻塞位

    • sigprocmask(int how, sigset_t *set, sigset_t *oldset)

      how: SIG_SETMASK赋值

            `SIG_BLOCK`阻塞 做或运算   与原来的集合
      
            `SIG_UNBLOCK` 解除  做与运算
      

      set,操作集合

      oldset,保存原集合,可以为NULL

对add函数和del函数阻塞信号。

检测INT键,绑定响应函数,用kill杀死进程,系统发出SIGCHLD信号,在SIGCHLD响应函数中删除作业。

trace07

#
# trace07.txt - Forward SIGINT only to foreground job.
#
/bin/echo -e tsh> ./myspin 4 \046
./myspin 4 &

/bin/echo -e tsh> ./myspin 5
./myspin 5

SLEEP 2
INT

/bin/echo tsh> jobs
jobs

06已经做完了,输出完整。

trace08

/bin/echo -e tsh> ./myspin 4 \046
./myspin 4 &

/bin/echo -e tsh> ./myspin 5
./myspin 5

SLEEP 2
TSTP

/bin/echo tsh> jobs
jobs

完成前台进程暂停信号响应。kill函数可以暂停进程。

代码可以正常暂停,但是暂停之后不会终止,无法退出。

解决:

用kill(-pid,sig);函数对进程组整体操作。

trace09

问题:

需要完成bg内置命令,暂停程序后台运行。

不太清楚中间一次输出信息,为什么不需要换行。

trace10

完成fg内置命令

trace11-13

输出信息与样例shell相同,但和答案不同。

trace14

问题:

简单的错误处理,处理输入输出格式错误。

trace15-16

运行结果都对

代码

不处理行数问题了,没有评分程序。只有所有例子都输出相同,不太确定是不是都是正确的。

/*
* tsh - A tiny shell program with job control
*
* <Put your name and login ID here>
*/
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.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.
*/

/* 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 jobs[MAXJOBS]; /* The job list */
/* End global variables */


/* Function prototypes */

//补充函数
pid_t Fork(void); //封装fork
void Execve(const char *filename, char *const argv[], char *const environ[]); //封装execv函数


/* Here are the functions that you will implement */
void eval(char *cmdline); //分析命令
int builtin_cmd(char **argv);
void do_bgfg(char **argv);
void waitfg(pid_t pid);

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, char **argv); //返回bg标志 1 后台
void sigquit_handler(int sig);

void clearjob(struct job_t *job); //清除队列中的一个作业
void initjobs(struct job_t *jobs); //初始化
int maxjid(struct job_t *jobs); //返回以分配的最大作业号
int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline); //添加一个作业 返回1 满了返回0
int deletejob(struct job_t *jobs, pid_t pid); //删除队列中一个作业 成功返回1
pid_t fgpid(struct job_t *jobs); //返回一个前台运行的作业号 没有返回0
struct job_t *getjobpid(struct job_t *jobs, pid_t pid); //使用pid查找一个作业 没找到 返回空
struct job_t *getjobjid(struct job_t *jobs, int jid); //用jid查找一个作业
int pid2jid(pid_t pid); // 用pid 返回 jid, 没找到返回0
void listjobs(struct job_t *jobs); //打印作业队列

void usage(void); //打印帮助菜单
void unix_error(char *msg); // unix风格打印错误信息
void app_error(char *msg); // app风格的错误信息
typedef void handler_t(int); //一个int参数,无返回
handler_t *Signal(int signum, handler_t *handler); //信号包装器

/*
* main - The shell's main routine
*/
int main(int argc, char **argv) {
char c;
char cmdline[MAXLINE];
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); //把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 */

/* This one provides a clean way to kill the shell */
Signal(SIGQUIT, sigquit_handler);

/* Initialize the job list */
initjobs(jobs);

/* Execute the shell's read/eval loop */
while (1) {
/* Read command line */
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) */
fflush(stdout);
exit(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.
*/
void eval(char *cmdline) {
char *argv[MAXARGS]; /*Argument list execve() */
char buf[MAXLINE]; /*Holds modified command line */
int bg; /*Should the job run in bg or fg? */
pid_t pid; /*Process id */

sigset_t mask_all, mask_one, prev_one;
sigfillset(&mask_all);
sigemptyset(&mask_one);
sigaddset(&mask_one, SIGCHLD);

strcpy(buf, cmdline);
bg = parseline(buf, argv);
if (argv[0] == NULL)
return; /* Ignore empty lines */

if (!builtin_cmd(argv)) {
sigprocmask(SIG_BLOCK, &mask_one, &prev_one); //阻塞SIGCHLD

if ((pid = Fork()) == 0) { /* Child runs user job */
setpgid(0, 0); //单独加入一个组
sigprocmask(SIG_SETMASK, &prev_one, NULL); //解除SIGCHLD阻塞 赋值新的mask
Execve(argv[0], argv, environ); //执行函数不会返回 会结束运行
} /* Parent waits for foreground job to terminate */
//添加作业 FG = 0 + 1 = 1 BG = 1 + 1 = 2
sigprocmask(SIG_BLOCK, &mask_all, NULL); //应该是重置作用
addjob(jobs, pid, bg + 1, cmdline);
sigprocmask(SIG_SETMASK, &prev_one, NULL);

if (!bg) {
waitfg(pid); //等待前台运行结束
}
else {
printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline);
}
}
return;
}

/*
* parseline - Parse the command line and build the argv array.
*
* Characters enclosed in single quotes are treated as a single
* argument. Return true if the user has requested a BG job, false if
* the user has requested a FG job.
*/
int parseline(const char *cmdline, char **argv) {
static char array[MAXLINE]; /* holds local copy of command line */
char *buf = array; /* ptr that traverses command line */
char *delim; /* points to first space delimiter */
int argc; /* number of args */
int bg; /* background job? */

strcpy(buf, cmdline);
buf[strlen(buf) - 1] = ' '; /* replace trailing '\n' with space */
while (*buf && (*buf == ' ')) /* ignore leading spaces */
buf++;

/* Build the argv list */
argc = 0;
if (*buf == '\'') {
buf++;
delim = strchr(buf, '\'');
}
else {
delim = strchr(buf, ' ');
}

while (delim) {
argv[argc++] = buf;
*delim = '\0';
buf = delim + 1;
while (*buf && (*buf == ' ')) /* ignore spaces */
buf++;

if (*buf == '\'') {
buf++;
delim = strchr(buf, '\'');
}
else {
delim = strchr(buf, ' ');
}
}
argv[argc] = NULL;

if (argc == 0) /* ignore blank line */
return 1;

/* should the job run in the background? */
if ((bg = (*argv[argc - 1] == '&')) != 0) {
argv[--argc] = NULL;
}
return bg;
}

/*
* builtin_cmd - If the user has typed a built-in command then execute
* it immediately.
*/
int builtin_cmd(char **argv) {
if (!strcmp(argv[0], "quit"))
exit(0);
if (!strcmp(argv[0], "jobs")) {
listjobs(jobs);
return 1;
}
if (!strcmp(argv[0], "bg") || !strcmp(argv[0], "fg")) {
do_bgfg(argv);
return 1;
}
if (!strcmp(argv[0], "&"))
return 1;
return 0; /* not a builtin command */
}

/*
* do_bgfg - Execute the builtin bg and fg commands
*/
void do_bgfg(char **argv) {
int doid;
pid_t pid;
pid_t t_pid;
if(argv[1] == NULL){
printf("%s command requires PID or %%jobid argument\n", argv[0]);
return;
}

int jflag = 0; //组标记
if(argv[1][0] == '%'){
jflag = 1;
if(isdigit(argv[1][1]) == 0){
printf("%s: argument must be a PID or %%jobid\n", argv[0]);
return;
}
doid = atoi(&argv[1][1]);
}
else{
if(isdigit(argv[1][0]) == 0){
printf("%s: argument must be a PID or %%jobid\n", argv[0]);
return;
}
doid = atoi(&argv[1][0]);
}


struct job_t * job;
if(jflag){
job = getjobjid(jobs, doid); //jid
if(job == NULL){
printf("%d: No such job\n", doid);
return;
}
}
else{
job = getjobpid(jobs, doid); //pid
if(job == NULL){
printf("(%d): No such process\n", doid);
return;
}
}
if (!strcmp(argv[0], "bg")) {


kill(-job->pid, SIGCONT); //发送继续命令
job->state = BG;
printf("[%d] (%d) %s",job->jid,job->pid,job->cmdline); //不清楚为什么不换行
}
else{

t_pid = fgpid(jobs); //找当前的前台作业
if(job->state == ST){
kill(-job->pid, SIGCONT); //发送继续命令
}
job->state = FG;
pid = job->pid;
if(t_pid != 0){ //有前台作业 转为后台
job = getjobpid(jobs, doid);
job->state = BG;
}
waitfg(pid); //等待前台运行
}
return;
}

/*
* waitfg - Block until process pid is no longer the foreground process
*/
/*
同一时间只有一个fg作业

*/
void waitfg(pid_t pid) {
while(pid == fgpid(jobs))
sleep(1);
return;
}

/*****************
* Signal handlers
*****************/

/*
* sigchld_handler - The kernel sends a SIGCHLD to the shell whenever
* a child job terminates (becomes a zombie), or stops because it
* received a SIGSTOP or SIGTSTP signal. The handler reaps all
* available zombie children, but doesn't wait for any other
* currently running children to terminate.
*/
/*
响应 停止信号 处理函数
回收所有僵尸进程
*/
void sigchld_handler(int sig) {
int olderrno = errno;
sigset_t mask_all,prev_all;
pid_t pid;

int status; //获取返回状态
sigfillset(&mask_all);
//回收所有的僵尸进程
while((pid = waitpid(-1,&status,WNOHANG | WUNTRACED))>0){ //如果都没终止返回0 有一个被终止 返回终止pid
if (WIFEXITED(status)) { //正常终止
sigprocmask(SIG_BLOCK, &mask_all, &prev_all);
deletejob(jobs, pid);
sigprocmask(SIG_SETMASK, &prev_all, NULL);
}
if(WIFSIGNALED(status)) { //正常终止
sigprocmask(SIG_BLOCK, &mask_all, &prev_all);
printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status));
deletejob(jobs, pid);
sigprocmask(SIG_SETMASK, &prev_all, NULL);
}
if(WIFSTOPPED(status)) { //正常暂停
sigprocmask(SIG_BLOCK, &mask_all, &prev_all);
printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(pid), pid, WSTOPSIG(status));
struct job_t * fgjob = getjobpid(jobs, pid); //查找前台进程指针
fgjob->state = ST;
sigprocmask(SIG_SETMASK, &prev_all, NULL);
}
}
errno = olderrno;
return;
}

/*
* sigint_handler - The kernel sends a SIGINT to the shell whenver the
* user types ctrl-c at the keyboard. Catch it and send it along
* to the foreground job.
*/
/*
SIGINT 信号只会传递给前台进程
*/
void sigint_handler(int sig) {
int olderrno = errno;
pid_t pid;
pid = fgpid(jobs);
kill(-pid, sig); //发送给前台作业
errno = olderrno;
return;
}

/*
* sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever
* the user types ctrl-z at the keyboard. Catch it and suspend the
* foreground job by sending it a SIGTSTP.
*/
void sigtstp_handler(int sig) {
int olderrno = errno;
pid_t pid;
pid = fgpid(jobs);
kill(-pid, sig); //发送给前台作业
errno = olderrno;
return;
}

/*********************
* End signal handlers
*********************/




//补充函数
pid_t Fork(void) {
pid_t pid;

if ((pid = fork()) < 0)
unix_error("Fork error");
return pid;
}


void Execve(const char *filename, char *const argv[], char *const environ[]) {
if (execve(filename, argv, environ) < 0) {
printf("%s: Command not found.\n", filename);
exit(0);
}
}


/***********************************************
* Helper routines that manipulate the job list
**********************************************/

/* clearjob - Clear the entries in a job struct */
void clearjob(struct job_t *job) {
job->pid = 0;
job->jid = 0;
job->state = UNDEF;
job->cmdline[0] = '\0';
}

/* initjobs - Initialize the job list */
void initjobs(struct job_t *jobs) {
int i;

for (i = 0; i < MAXJOBS; i++)
clearjob(&jobs[i]);
}

/* maxjid - Returns largest allocated job ID */
int maxjid(struct job_t *jobs) {
int i, max = 0;

for (i = 0; i < MAXJOBS; i++)
if (jobs[i].jid > max)
max = jobs[i].jid;
return max;
}

/* addjob - Add a job to the job list */
int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline) {
int i;

if (pid < 1)
return 0;

for (i = 0; i < MAXJOBS; i++) {
if (jobs[i].pid == 0) {
jobs[i].pid = pid;
jobs[i].state = state;
jobs[i].jid = nextjid++;
if (nextjid > MAXJOBS)
nextjid = 1;
strcpy(jobs[i].cmdline, cmdline);
if (verbose) {
printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline);
}
return 1;
}
}
printf("Tried to create too many jobs\n");
return 0;
}

/* deletejob - Delete a job whose PID=pid from the job list */
int deletejob(struct job_t *jobs, pid_t pid) {
int i;

if (pid < 1)
return 0;

for (i = 0; i < MAXJOBS; i++) {
if (jobs[i].pid == pid) {
clearjob(&jobs[i]);
nextjid = maxjid(jobs) + 1;
return 1;
}
}
return 0;
}

/* fgpid - Return PID of current foreground job, 0 if no such job */
pid_t fgpid(struct job_t *jobs) {
int i;

for (i = 0; i < MAXJOBS; i++)
if (jobs[i].state == FG)
return jobs[i].pid;
return 0;
}

/* getjobpid - Find a job (by PID) on the job list */
struct job_t *getjobpid(struct job_t *jobs, pid_t pid) {
int i;

if (pid < 1)
return NULL;
for (i = 0; i < MAXJOBS; i++)
if (jobs[i].pid == pid)
return &jobs[i];
return NULL;
}

/* getjobjid - Find a job (by JID) on the job list */
struct job_t *getjobjid(struct job_t *jobs, int jid) {
int i;

if (jid < 1)
return NULL;
for (i = 0; i < MAXJOBS; i++)
if (jobs[i].jid == jid)
return &jobs[i];
return NULL;
}

/* pid2jid - Map process ID to job ID */
int pid2jid(pid_t pid) {
int i;

if (pid < 1)
return 0;
for (i = 0; i < MAXJOBS; i++)
if (jobs[i].pid == pid) {
return jobs[i].jid;
}
return 0;
}

/* listjobs - Print the job list */
void listjobs(struct job_t *jobs) {
int i;

for (i = 0; i < MAXJOBS; i++) {
if (jobs[i].pid != 0) {
printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);
switch (jobs[i].state) {
case BG:
printf("Running ");
break;
case FG:
printf("Foreground ");
break;
case ST:
printf("Stopped ");
break;
default:
printf("listjobs: Internal error: job[%d].state=%d ",
i, jobs[i].state);
}
printf("%s", jobs[i].cmdline);
}
}
}
/******************************
* end job list helper routines
******************************/


/***********************
* Other helper routines
***********************/

/*
* usage - print a help message
*/
void usage(void) {
printf("Usage: shell [-hvp]\n");
printf(" -h print this message\n");
printf(" -v print additional diagnostic information\n");
printf(" -p do not emit a command prompt\n");
exit(1);
}

/*
* unix_error - unix-style error routine
*/
void unix_error(char *msg) {
fprintf(stdout, "%s: %s\n", msg, strerror(errno));
exit(1);
}

/*
* app_error - application-style error routine
*/
void app_error(char *msg) {
fprintf(stdout, "%s\n", msg);
exit(1);
}

/*
* Signal - wrapper for the sigaction function
*/
handler_t *Signal(int signum, handler_t *handler) {
struct sigaction action, old_action;

action.sa_handler = handler;
sigemptyset(&action.sa_mask); /* block sigs of type being handled */
action.sa_flags = SA_RESTART; /* restart syscalls if possible */

if (sigaction(signum, &action, &old_action) < 0)
unix_error("Signal error");
return (old_action.sa_handler);
}

/*
* sigquit_handler - The driver program can gracefully terminate the
* child shell by sending it a SIGQUIT signal.
*/
void sigquit_handler(int sig) {
printf("Terminating after receipt of SIGQUIT signal\n");
exit(1);
}