Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //
- // tsh - A tiny shell program with job control
- //
- // <Put your name and login ID here>
- //Numair Baig
- //
- using namespace std;
- #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>
- #include <string>
- #include "globals.h"
- #include "jobs.h"
- #include "helper-routines.h"
- //
- // Needed global variable definitions
- //
- static char prompt[] = "tsh> ";
- int verbose = 0;
- //
- // You need to implement the functions eval, builtin_cmd, do_bgfg,
- // waitfg, sigchld_handler, sigstp_handler, sigint_handler
- //
- // The code below provides the "prototypes" for those functions
- // so that earlier code can refer to them. You need to fill in the
- // function bodies below.
- //
- 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);
- //
- // main - The shell's main routine
- //
- int main(int argc, char **argv)
- {
- 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 */
- char c;
- 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
- //
- for(;;) {
- //
- // Read command line
- //
- if (emit_prompt) {
- printf("%s", prompt);
- fflush(stdout);
- }
- char cmdline[MAXLINE];
- if ((fgets(cmdline, MAXLINE, stdin) == NULL) && ferror(stdin)) {
- app_error("fgets error");
- }
- //
- // End of file? (did user type ctrl-d?)
- //
- if (feof(stdin)) {
- fflush(stdout);
- exit(0);
- }
- //
- // Evaluate 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)
- {
- /* Parse command line */
- //
- // The 'argv' vector is filled in by the parseline
- // routine below. It provides the arguments needed
- // for the execve() routine, which you'll need to
- // use below to launch a process.
- //
- pid_t pid;//keeps track of pid
- char *argv[MAXARGS];
- //
- // The 'bg' variable is TRUE if the job should run
- // in background mode or FALSE if it should run in FG
- //
- int bg = parseline(cmdline, argv);
- sigset_t mask;//block of sigchild
- if (argv[0] == NULL)
- return; /* ignore empty lines */
- // if(strcmp(argv[0],"quit") == 0)//if first command line argument is quit, exit the program
- // {
- // //uses strcmp to avoid pointer cmp w/ string
- // exit(0);//exits program
- // }
- // else if(strcmp(argv[0],"fg") == 0)//if first command line argument is foreground, run a foreground job
- // {
- // return 1;//return for proof of job
- // }
- struct job_t *job;//creates job obj ptr
- if(!builtin_cmd(argv))//if argv vector does not contain built in command
- {
- sigemptyset(&mask);//initializes signal stb to empty set
- sigaddset(&mask, SIGCHLD);//adds sigchild signal to set
- sigprocmask(SIG_BLOCK, &mask, NULL);//blocking until child is created
- if((pid = fork()) == 0)//fork and execute
- {
- sigprocmask(SIG_UNBLOCK, &mask, NULL);
- setpgid(0,0);
- // execvp(argv[0], argv);//executes first argv with full vec vp to specify path
- if(execve(argv[0], argv, environ) < 0)
- {
- printf("%s: Command not found.\n", argv[0]);
- exit(0);//exits - terminates child
- }
- }
- addjob(jobs, pid, bg ? BG : FG, cmdline);//adds job
- sigprocmask(SIG_UNBLOCK, &mask, NULL);//to prevent queue from clogging
- //has job and pid
- //turnary op checks if background process, if not, then foreground process
- if(!bg)//if not background
- {
- //wait(NULL);//blocks calling process until signal is received - waits for child
- waitfg(pid);
- }
- else//if is background
- {
- job = getjobpid(jobs, pid); //gets job pid
- printf("[%d] (%d) %s/n", job->jid, pid, cmdline);//print based on desired statement from rtest
- //[1] (83) ./myspin 1 &
- }
- }
- return;
- }
- /////////////////////////////////////////////////////////////////////////////
- //
- // builtin_cmd - If the user has typed a built-in command then execute
- // it immediately. The command name would be in argv[0] and
- // is a C string. We've cast this to a C++ string type to simplify
- // string comparisons; however, the do_bgfg routine will need
- // to use the argv array as well to look for a job number.
- //
- int builtin_cmd(char **argv) //built in command function
- {
- string cmd(argv[0]);
- if(cmd == "quit")//if first command line argument is quit
- {
- exit(0);//exits shell - performs a quit
- }
- else if(cmd == "fg")//if first command is to run a foreground process
- {
- do_bgfg(argv);//runs background foreground
- return 1;//returns 1 to verify process performed
- }
- else if(cmd == "bg")//if first command is to run a background process
- {
- //move process to background and quit shell
- do_bgfg(argv);//runs background foreground
- return 1;//return 1 to verify process performed
- }
- else if(cmd == "jobs")//if first command is to view jobs
- {
- listjobs(jobs);//lists the jobs
- return 1;//return 1 to verify process performed
- }
- return 0; /* not a builtin command */
- }
- /////////////////////////////////////////////////////////////////////////////
- //
- // do_bgfg - Execute the builtin bg and fg commands
- //
- void do_bgfg(char **argv)
- {
- struct job_t *jobp=NULL;
- /* Ignore command if no argument */
- if (argv[1] == NULL) {
- printf("%s command requires PID or %%jobid argument\n", argv[0]);
- return;
- }
- /* Parse the required PID or %JID arg */
- if (isdigit(argv[1][0])) {
- pid_t pid = atoi(argv[1]);
- if (!(jobp = getjobpid(jobs, pid))) {
- printf("(%d): No such process\n", pid);
- return;
- }
- }
- else if (argv[1][0] == '%') {
- int jid = atoi(&argv[1][1]);
- if (!(jobp = getjobjid(jobs, jid))) {
- printf("%s: No such job\n", argv[1]);
- return;
- }
- }
- else {
- printf("%s: argument must be a PID or %%jobid\n", argv[0]);
- return;
- }
- //
- // You need to complete rest. At this point,
- // the variable 'jobp' is the job pointer
- // for the job ID specified as an argument.
- //
- // Your actions will depend on the specified command
- // so we've converted argv[0] to a string (cmd) for
- // your benefit.
- //
- string cmd(argv[0]);
- int pid = jobp->pid;
- //kill with SIGCONT
- kill(-pid, SIGCONT);// - kills whole process group
- if(cmd != "fg")//check if command is foreground
- {
- jobp->state = BG;//set jobp state to background
- printf("[%d] (%d) %s", jobp->jid, jobp->pid, jobp->cmdline);//command of job
- }
- else
- {
- jobp->state = FG;//set jobp state
- waitfg(pid);
- }
- return;
- }
- /////////////////////////////////////////////////////////////////////////////
- //
- // waitfg - Block until process pid is no longer the foreground process
- //
- void waitfg(pid_t pid)
- {
- struct job_t *job = getjobpid(jobs, pid);//gets job using pid
- if(pid != 0 && job != NULL)//to avoid seg fault
- {
- while(job->state == FG)//if job is a foreground job
- {
- sleep(1);//wait then return
- }
- }
- 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)
- {
- pid_t pid;
- int status;
- //struct job_t *job;//using job struct
- while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)//WNOHANG doesn't wait for process
- {//WUNTRACED check for stop signal
- //job = getjobpid(jobs, pid);
- if(WIFSTOPPED(status))//signal not modified
- {
- getjobpid(jobs, pid)->state = ST;//set jobpid state to stop
- int jid = pid2jid(pid);//find pid return jid
- printf("Job [%d] (%d) stopped by signal %d\n", jid, pid, WSTOPSIG(status));
- }
- else if(WIFSIGNALED(status))//looks for error signal
- {
- int jid = pid2jid(pid);//gets jid from pid
- printf("Job [%d] (%d) terminated by signal %d\n", jid, pid, WTERMSIG(status));
- deletejob(jobs, pid);//deletes job after termination
- }
- else if(WIFEXITED(status))
- {
- deletejob(jobs, pid);//deletes job after termination
- }
- }
- 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) //passes sig int signal
- {
- int pid = fgpid(jobs);//gets pid of foreground
- if(pid != 0)//if not a child
- {//to kill child use sigchild
- int pass = kill(-pid, sig); //send signal to foreground
- //return -1 for failure
- if(pass < 0)
- {
- unix_error("kill error");//if attempt to kill doesn't exist
- }
- }
- 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) //passes sig stop signal
- {
- int pid = fgpid(jobs);//gets pid of foreground
- if(pid != 0)//if not a child
- {
- int pass = kill(-pid, sig); //send signal to foreground
- if(pass < 0)
- {
- unix_error("kill error");
- }
- }
- return;
- }
- /*********************
- * End signal handlers
- *********************/
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement