Created
May 8, 2014 18:54
-
-
Save mpgarate/98bda43ca1bb071b29b0 to your computer and use it in GitHub Desktop.
Simple shell written for NYU CSO course in Spring 2013
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* tsh - A tiny shell program with job control | |
* | |
* Michael Garate | mg3626 | |
* | |
* Book code found here: http://csapp.cs.cmu.edu/public/code.html | |
* | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <ctype.h> | |
#include <signal.h> | |
#include <sys/types.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 */ | |
/* verbose */ | |
#define DEBUG 0 /* printf and flush, verbose, debug */ | |
/* | |
* 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 */ | |
/*Error handling wrappers */ | |
pid_t Fork(void); | |
int Execve(const char *filename, char *const argv[], char *const envp[]); | |
int Kill(pid_t pid, int sig); | |
int Setpgid(pid_t pid, pid_t pgid); | |
int Sigemptyset(sigset_t *set); | |
int Sigaddset(sigset_t *set, int signo); | |
int Sigprocmask(int how, const sigset_t *set, sigset_t *oldset); | |
/* 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); | |
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); | |
int deletejob(struct job_t *jobs, pid_t pid); | |
pid_t fgpid(struct job_t *jobs); | |
struct job_t *getjobpid(struct job_t *jobs, pid_t pid); | |
struct job_t *getjobjid(struct job_t *jobs, int jid); | |
int pid2jid(pid_t pid); | |
void listjobs(struct job_t *jobs); | |
void usage(void); | |
void unix_error(char *msg); | |
void app_error(char *msg); | |
typedef void handler_t(int); | |
handler_t *Signal(int signum, handler_t *handler); | |
/* error wrappers */ | |
/* Based on book code pg 752*/ | |
pid_t Fork(void) | |
{ | |
pid_t pid; | |
if ((pid = fork()) < 0) | |
unix_error("Fork error"); | |
return pid; | |
} | |
int Execve(const char *filename, char *const argv[], char *const envp[]) | |
{ | |
if(execve(filename, argv, envp) < 0) | |
printf("%s: Command not found\n", argv[0]); | |
exit(0); | |
} | |
int Kill(pid_t pid, int sig) | |
{ | |
if (kill(pid, sig) < 0) | |
unix_error("Kill error"); | |
return 0; | |
} | |
int Setpgid(pid_t pid, pid_t pgid) | |
{ | |
if (setpgid(pid, pgid) < 0) | |
unix_error("Setpgid error"); | |
return 0; | |
} | |
int Sigemptyset(sigset_t *set) | |
{ | |
if (sigemptyset(set) < 0) | |
unix_error("Setpgid error"); | |
return 0; | |
} | |
int Sigaddset(sigset_t *set, int signo) | |
{ | |
if (sigaddset(set, signo) < 0) | |
unix_error("Setpgid error"); | |
return 0; | |
} | |
int Sigprocmask(int how, const sigset_t *set, sigset_t *oldset) | |
{ | |
if (sigprocmask(how, set, oldset) < 0) | |
unix_error("Setpgid error"); | |
return 0; | |
} | |
/* | |
* 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); | |
/* 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) /* Based on book code shellex.c and procmask2.c */ | |
{ | |
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; | |
int state = 0; /* 1 is FG, 2 is BG, 3 is STopped, and 0 is undefined */ | |
strcpy(buf, cmdline); | |
bg = parseline(buf, argv); | |
if (argv[0] == NULL) | |
return; /* Ignore empty lines */ | |
if (!builtin_cmd(argv)) { | |
Sigemptyset(&mask); | |
Sigaddset(&mask, SIGCHLD); | |
Sigprocmask(SIG_BLOCK, &mask, NULL); /* Block SIGCHILD */ | |
if ((pid = Fork()) == 0) { /* Child runs user job */ | |
Setpgid(0, 0); /* Give process own group id */ | |
Sigprocmask(SIG_UNBLOCK, &mask, NULL); /* Unblock SIGCHILD for excecve */ | |
Execve(argv[0], argv, environ); | |
} | |
if (bg){ /* Convert bg to state syntax for job control */ | |
state = 2; /* Background */ | |
} | |
else{ | |
state = 1; /* Foreground */ | |
} | |
if (!bg) { | |
addjob(jobs, pid, state, cmdline); /* Add this fg job to the jobs list */ | |
Sigprocmask(SIG_UNBLOCK, &mask, NULL); /* Unblock SIGCHILD */ | |
waitfg(pid); | |
} | |
else{ | |
addjob(jobs, pid, state, cmdline); /* Add this bg job to the jobs list */ | |
struct job_t* job = getjobpid(jobs, pid); /* Point to address of current job information */ | |
Sigprocmask(SIG_UNBLOCK, &mask, NULL); /* Unblock SIGCHILD */ | |
printf("[%d] (%d) %s", job->jid, 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) /* quit, ignore, and 'not builtin cmd' based on book code */ | |
{ | |
if(DEBUG){printf("Begin: builtin_cmd\n"); fflush(stdout);} | |
if (!strcmp(argv[0], "quit")){ /* quit command */ | |
exit(0); | |
} | |
if (!strcmp(argv[0], "&")) /* Ignore singleton & */ | |
return 1; | |
if (!strcmp(argv[0], "jobs")){ /* jobs command & */ | |
listjobs(jobs); | |
return 1; | |
} | |
if (!strcmp(argv[0], "bg")){ /* bg command */ | |
do_bgfg(argv); | |
return 1; | |
} | |
if (!strcmp(argv[0], "fg")){ /* fg command */ | |
do_bgfg(argv); | |
return 1; | |
} | |
return 0; /* Not a builtin command */ | |
} | |
/* | |
* do_bgfg - Execute the builtin bg and fg commands | |
*/ | |
void do_bgfg(char **argv) | |
{ | |
if(DEBUG){printf("Begin: do_bgfg?\n"); fflush(stdout);} | |
char* idString = argv[1]; /* Hold the input id, including % sign (for now) */ | |
int id = -1; /* Hold the input jid or pid */ | |
int is_pid = 1; /* Boolean to remember if pid or jid */ | |
int i; /* Counter for loops */ | |
int found; | |
struct job_t* job; /* Hold current job */ | |
int argc = 1; | |
while(argv[argc]!=NULL && argc < 4){ /* Count the number of arguments. No need to go past 4. */ | |
argc++; | |
} | |
/* Handle invalid inputs and parse input */ | |
if (argc!=2){ /* Print error if more/less than 2 args */ | |
printf("%s command requires PID or %%jobid argument\n", argv[0]); | |
return; | |
} | |
if (idString[0] == '%'){ /* Check for '%' to see if jid */ | |
idString = idString + 1; /* Inc. pointer to skip '%' symbol */ | |
is_pid = 0; /* Remember if not pid */ | |
} | |
for (i=0;idString[i]!=0;i++){ /* Check that string is only digits */ | |
if (!isdigit(idString[0])){ | |
printf("%s: argument must be a PID or %%jobid\n", argv[0]); | |
return; | |
} | |
} | |
id = atoi(idString); /* Cast String to integer */ | |
if(is_pid){ | |
found=0; | |
if (id < 1) printf("(%i): No such process\n", id); /* Immediately deal with impossible PID */ | |
for (i = 0; i < maxjid(jobs); i++){ /* Check that this pid exists in currently active jobs */ | |
if (jobs[i].pid == id) { | |
found = 1; | |
} | |
} | |
if (!found) { | |
printf("(%i): No such process\n", id); | |
return; | |
} | |
job = getjobpid(jobs, id); /* Get current job by pid */ | |
} | |
else{ | |
found=0; | |
if (id < 1) printf("(%i): No such process\n", id); /* Immediately deal with impossible JID */ | |
for (i = 0; i < maxjid(jobs); i++){ /* Check that this jid exists in currently active jobs */ | |
if (jobs[i].jid == id) { | |
found = 1; | |
} | |
} | |
if (!found) { | |
printf("%%%i: No such job\n", id); | |
return; | |
} | |
job = getjobjid(jobs, id); /* Get current job by jid */ | |
} | |
pid_t pid = job->pid; | |
if (!strcmp(argv[0], "bg")){ /* Set state to bg and send SIGCONT signal */ | |
job->state = BG; | |
Kill(-pid, SIGCONT); | |
printf("[%d] (%d) %s", job->jid, pid, job->cmdline); | |
} | |
else if (!strcmp(argv[0], "fg")){ /* Set state to fg, send SIGCONT signal, and enter waitfg() loop */ | |
job->state = FG; | |
Kill(-pid, SIGCONT); | |
waitfg(pid); | |
} | |
return; | |
} | |
/* | |
* waitfg - Block until process pid is no longer the foreground process | |
*/ | |
void waitfg(pid_t pid) | |
{ | |
if(DEBUG){ printf("Begin: waitfg\n"); listjobs(jobs); fflush(stdout);} | |
/* Wait for fg job to terminate */ | |
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) /* Code adapted from pg 761, waitpid1.c and linux man page for waitpid */ | |
{ | |
if(DEBUG){ printf("Caught SIGCHLD\n"); listjobs(jobs); fflush(stdout);} | |
/* Get pgid of fg process */ | |
pid_t pid, w; | |
pid = fgpid(jobs); | |
int status = getpgid(pid); | |
/* Do waitpid() for all exited children. Adapted from man page example code. */ | |
do { | |
w = waitpid(-1, &status,WUNTRACED|WNOHANG); | |
if (w == -1) { | |
perror("waitpid"); | |
exit(EXIT_FAILURE); | |
} | |
if(WIFEXITED(status)){ /* Silently reap exited child */ | |
if(DEBUG){ printf("%i reaped, status=%d\n", w, WEXITSTATUS(status)); fflush(stdout);} | |
deletejob(jobs, w); | |
} | |
else if (WIFSIGNALED(status)){ /* Run deletejob() for terminated jobs */ | |
if(WTERMSIG(status) == 2) /* Only print for signal 2 */ | |
{ | |
printf("Job [%i] (%i) terminated by signal %d\n",pid2jid(w), w, WTERMSIG(status)); | |
} | |
deletejob(jobs, w); | |
} | |
else if (WIFSTOPPED(status) && WSTOPSIG(status)==20){ /* Change job state for stop signal */ | |
int i; | |
for (i = 0; i < MAXJOBS; i++){ /* Get current process and change state */ | |
if (jobs[i].pid == w) { | |
jobs[i].state=ST; | |
printf("Job [%i] (%i) stopped by signal 20\n",pid2jid(w), w); | |
return; | |
} | |
} | |
printf("WIFSTOPPED error\n" ); | |
return; | |
} | |
else if (WIFCONTINUED(status)) { | |
printf("continued\n"); | |
} | |
} while (!WIFEXITED(status) && !WIFSIGNALED(status)); | |
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. | |
*/ | |
void sigint_handler(int sig) /* based on book code pg 779, sigint1.c */ | |
{ | |
if(DEBUG){printf("Caught SIGINT\n"); listjobs(jobs); fflush(stdout);} | |
/* Send SIGINT to -pgid*/ | |
pid_t pid = fgpid(jobs); | |
int pgid = getpgid(pid); | |
if(pgid < 0) unix_error("getpgid error"); /* Catch system call error */ | |
Kill(-pgid, SIGINT); | |
} | |
/* | |
* 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) | |
{ | |
if(DEBUG){printf("Caught SIGTSTP\n"); listjobs(jobs); fflush(stdout);} | |
/* Send SIGTSTP to -pgid*/ | |
pid_t pid = fgpid(jobs); | |
int pgid = getpgid(pid); | |
if(pgid < 0) unix_error("getpgid error"); /* Catch system call error */ | |
Kill(-pgid, SIGTSTP); | |
} | |
/********************* | |
* End signal handlers | |
*********************/ | |
/*********************************************** | |
* 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); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment