/** * @file execute

/**
* @file execute.c
*
* @brief Implements interface functions between Quash and the environment and
* functions that interpret an execute commands.
*
* @note As you add things to this file you may want to change the method signature
*/

// NOTE: the following website was used to gather more information
// for system calls. This information was used to add comments
// detailing what was going on in the functions using them.
// https://linux.die.net/man/3/

We Will Write a Custom Essay Specifically
For You For Only $13.90/page!


order now

#include “execute.h”
#include “deque.h”
#include
#include
#include
#include
#include
#include
#include “quash.h”

// Remove this and all expansion calls to it
/**
* @brief Note calls to any function that requires implementation
*/
#define IMPLEMENT_ME() \
fprintf(stderr, “IMPLEMENT ME: %s(line %d): %s()\n”, __FILE__, __LINE__, __FUNCTION__)

// Implement the process id double ended queue structure
IMPLEMENT_DEQUE_STRUCT( PIDDequeue, pid_t );
// Generate the prototypes for the functions of the double ended queue
PROTOTYPE_DEQUE( PIDDequeue, pid_t );
// Implement the process id double ended queue
IMPLEMENT_DEQUE( PIDDequeue, pid_t );

// Struct declaration for a job
typedef struct Job {
int jobId;
char* cmd;
PIDDequeue pid_list;
} Job;

// Implement the job double ended queue structure
IMPLEMENT_DEQUE_STRUCT( JobDequeue, Job );
// Generate the prototypes for the functions of the double ended queue
PROTOTYPE_DEQUE( JobDequeue, Job );
// Implement the job double ended queue
IMPLEMENT_DEQUE( JobDequeue, Job );

// Pipe array declaration
int _pipes22;
// Pope index declaration
int _pipeLoc = 0;
// Declaration for the initiated status of a job
bool isInit = false;
// The number for the job
int jobNum = 1;
// Double ended queue for jobs
JobDequeue jobs;
// Double ended queue for process ids
// this was done to prevent any issues
// that we had coming up when accessing
// the process id list of a job
PIDDequeue pids;

/***************************************************************************
* Interface Functions
***************************************************************************/

// Return a string containing the current working directory.
char* get_current_directory(bool* should_free) {
// Set the boolean to false
*should_free = false;
// Create an empty string to hold the current directory
char* cwd = NULL;
// Set the string to the current directory
cwd = getcwd( NULL, 0 );
// Return the current working directory
return cwd;
}

// Returns the value of an environment variable env_var
const char* lookup_env(const char* env_var) {
// Create a string to hold the environment variable
char* env = NULL;
// Set the string to the environment variable
env = getenv( env_var );
// Return the variable
return env;
}

// Check the status of background jobs
void check_jobs_bg_status() {
// If the job queue is empty then there are no jobs
// to print out, so end execution of this
// function
if ( is_empty_JobDequeue( &jobs ) ) {
return;
}

// Get the number of jobs
int numOfJobs = length_JobDequeue( &jobs );

// Iterate through each one of the jobs
for ( int i = 0; i < numOfJobs; i++ ) {
// Set a local variable to the current job
Job curJob = pop_front_JobDequeue( &jobs );
// Grab the queue of processes
PIDDequeue curJobPids = curJob.pid_list;
// Get the current process id
pid_t curPid = peek_front_PIDDequeue( &curJobPids );

// Get the number of processes
int numOfPids = length_PIDDequeue( &curJobPids );

// Iterate through each of the processes
for ( int j = 0; j < numOfPids; j++ ) {
// Set a local variable to the iterating process id
pid_t curPid2 = pop_front_PIDDequeue( &curJobPids );
// Create a status variable
int status = 0;

// If the status of the process id is 0
// push the iterating process id to
// the end of the process id queue
if ( waitpid( curPid2, &status, WNOHANG ) == 0 ) {
push_back_PIDDequeue( &curJobPids, curPid2 );
}
}

// If there are no process ids for the current job
// print that the job is complete, then
// get rid of the process id queue.
// If there are still processes push the
// current job to the end of the job queue
if ( is_empty_PIDDequeue( &curJobPids ) ) {
// Set a local variable for the job id
int curJobId = curJob.jobId;
// Set a local variable for the job command
char* curJobCmd = curJob.cmd;

// Print that the job has completed
print_job_bg_complete( curJobId, curPid, curJobCmd );
// Free the memory being used by the job command
free( curJobCmd );
// Destroy the process id queue for the current job
destroy_PIDDequeue( &curJobPids );
} else {
// Push the current job to the end of the job queue
push_back_JobDequeue( &jobs, curJob );
}
}
}

// Prints the job id number, the process id of the first process belonging to
// the Job, and the command string associated with this job
void print_job(int job_id, pid_t pid, const char* cmd) {
printf("%d\t%8d\t%s\n", job_id, pid, cmd);
fflush(stdout);
}

// Prints a start up message for background processes
void print_job_bg_start(int job_id, pid_t pid, const char* cmd) {
printf("Background job started: ");
print_job(job_id, pid, cmd);
}

// Prints a completion message followed by the print job
void print_job_bg_complete(int job_id, pid_t pid, const char* cmd) {
printf("Completed: \t");
print_job(job_id, pid, cmd);
}

/***************************************************************************
* Functions to process commands
***************************************************************************/
// Run a program reachable by the path environment variable, relative path, or
// absolute path
void run_generic(GenericCommand cmd) {
// Execute a program with a list of arguments. The `args` array is a NULL
// terminated (last string is always NULL) list of strings. The first element
// in the array is the executable
char* exec = cmd.args0;
char** args = cmd.args;

// Execute the command that is passed in
execvp( exec, args );
// Silenced error
// perror("ERROR: Failed to execute program");
}

// Print strings
void run_echo(EchoCommand cmd) {
// Print an array of strings. The args array is a NULL terminated
// (last string is always NULL) list of strings.
char** str = cmd.args;

// Print the sentence as long as the end (NULL string)
// is not reached
while ( *str != NULL ) {
printf( "%s ", *str );
str++;
}
// Print a new line for corrent formatting
printf( "\n" );
// Flush the buffer before returning
fflush(stdout);
}

// Sets an environment variable
void run_export(ExportCommand cmd) {
// Write an environment variable
const char* env_var = cmd.env_var;
const char* val = cmd.val;

// Set the environment
setenv( env_var, val, 1 );
}

// Changes the current working directory
void run_cd(CDCommand cmd) {
// Get the directory name
const char* dir = cmd.dir;

// Check if the directory is valid
// notify the user of an error if one occurs
if (dir == NULL) {
perror("ERROR: Failed to resolve path");
return;
}

// Character array to hold the path
char* path = NULL;
// Get the path to the directory
path = realpath( dir, NULL );

// Check to see if the path was set
// notify the user of an error if one occurs
if(path == NULL)
{
perror("ERROR: Failed to resolve path");
return;
}

// Change the current directory to that
// of the path that is set above
chdir( path );
// Set the current environment variable "PWD"
// to the new path
setenv( "PWD", path, 1 );
// Deallocate the memory being used by path
// since realpath() is called using NULL
free( path );
}

// Sends a signal to all processes contained in a job
void run_kill(KillCommand cmd) {
int signal = cmd.sig;
int job_id = cmd.job;

// Get the total number of jobs
int numOfJobs = length_JobDequeue( &jobs );
// Increment through each of the jobs
for ( int i = 0; i < numOfJobs; i++ ) {
// Set a local variable for the iterating job
Job curJob = peek_front_JobDequeue( &jobs );
// Get the job id for the iterating job
int curJobId = curJob.jobId;
// Check to see if the current job's id is equal
// to the cmd job id. If it is go through the
// processes of the job and terminate them. Otherwise
// pop off the current job and put the at the end of the
// job queue.
if( curJobId == job_id ) {
// Get the processes of the current job
PIDDequeue curJobPids = curJob.pid_list;
// Get the number of processes for the job
int numOfPids = length_PIDDequeue( &curJobPids );
// Increment through each process
for ( int j = 0; j < numOfPids; j++ ) {
// Get the current process id
pid_t curPid = peek_front_PIDDequeue( &curJobPids );
// Terminate the process
kill( curPid, signal );
// Remove the process from the proccess queue
pop_front_PIDDequeue( &curJobPids );
}
} else {
// Pop off the current job
Job curJobPopped = pop_front_JobDequeue( &jobs );
// Add it back to the end of the job queue
push_back_JobDequeue( &jobs, curJobPopped );
}
}
}

// Prints the current working directory to stdout
void run_pwd() {
// Initialize boolean to false
bool isFree = false;
// Get the current directory
char* directory = get_current_directory( &isFree );
// Print out the current directory
printf( "%s\n", directory );
// Deallocate the memory being used by directory
free( directory );
// Flush the buffer before returning
fflush(stdout);
}

// Prints all background jobs currently in the job list to stdout
void run_jobs() {
// Get the number of jobs
int numOfJobs = length_JobDequeue( &jobs );

// If the job queue is empty then there
// are no jobs to run, so return and
// stop execution of this funtion.
if ( is_empty_JobDequeue( &jobs ) ) {
return;
}

// Iterate through each of the jobs
for ( int i = 0; i < numOfJobs; i++ ) {
// Set a local variable to the iterating job
Job curJob = pop_front_JobDequeue( &jobs );

// Set a variable for the job's id
int curJobId = curJob.jobId;
// Set a variable for the job's command
char* curJobCmd = curJob.cmd;
// Get the process id for the current job
pid_t curJobPid = peek_front_PIDDequeue( &curJob.pid_list );

// Print the current job
print_job( curJobId, curJobPid, curJobCmd );
// Push the current job to the back of the queue
// so that the jobs can be iterated through
push_back_JobDequeue( &jobs, curJob );
}

// Flush the buffer before returning
fflush(stdout);
}

/***************************************************************************
* Functions for command resolution and process setup
***************************************************************************/

/**
* @brief A dispatch function to resolve the correct @a Command variant
* function for child processes.
*
* This version of the function is tailored to commands that should be run in
* the child process of a fork.
*
* @param cmd The Command to try to run
*
* @sa Command
*/
void child_run_command(Command cmd) {
CommandType type = get_command_type(cmd);

switch (type) {
case GENERIC:
run_generic(cmd.generic);
break;

case ECHO:
run_echo(cmd.echo);
break;

case PWD:
run_pwd();
break;

case JOBS:
run_jobs();
break;

case EXPORT:
case CD:
case KILL:
case EXIT:
case EOC:
break;

default:
fprintf(stderr, "Unknown command type: %d\n", type);
}
}

/**
* @brief A dispatch function to resolve the correct @a Command variant
* function for the quash process.
*
* This version of the function is tailored to commands that should be run in
* the parent process (quash).
*
* @param cmd The Command to try to run
*
* @sa Command
*/
void parent_run_command(Command cmd) {
CommandType type = get_command_type(cmd);

switch (type) {
case EXPORT:
run_export(cmd.export);
break;

case CD:
run_cd(cmd.cd);
break;

case KILL:
run_kill(cmd.kill);
break;

case GENERIC:
case ECHO:
case PWD:
case JOBS:
case EXIT:
case EOC:
break;

default:
fprintf(stderr, "Unknown command type: %d\n", type);
}
}

/**
* @brief Creates one new process centered around the @a Command in the @a
* CommandHolder setting up redirects and pipes where needed
*
* @note Processes are not the same as jobs. A single job can have multiple
* processes running under it. This function creates a process that is part of a
* larger job.
*
* @note Not all commands should be run in the child process. A few need to
* change the quash process in some way
*
* @param holder The CommandHolder to try to run
*
* @sa Command CommandHolder
*/
void create_process(CommandHolder holder) {
// Read the flags field from the parser
bool p_in = holder.flags & PIPE_IN;
bool p_out = holder.flags & PIPE_OUT;
bool r_in = holder.flags & REDIRECT_IN;
bool r_out = holder.flags & REDIRECT_OUT;
bool r_app = holder.flags & REDIRECT_APPEND; // This can only be true if r_out
// is true

// Set a variable representing the pipe index
// to be used when creating navigating the
// pipe array
int _pipeIndex = 0;

// Create a local process id and set to
// a new process
pid_t localPID = fork();
// If p_out is true, run pipe() with the pipe array
if ( p_out ) {
// Set the pipe index to the remainder of
// the pipeLoc variable and 2 (for the pipe numbers)
_pipeIndex = _pipeLoc % 2;
// Create the pipes
pipe(_pipes_pipeIndex);
}

// Push the new process to the end
// of the process id queue
push_back_PIDDequeue( &pids, localPID );

// Actions for the child branch of fork
if ( localPID == 0 ) {
// If p_in is true update the pipe index
// and duplicate the file descriptor at the pipe
// index, then close said descriptor
if ( p_in ) {
// Update pipe index
_pipeIndex = (_pipeLoc – 1) % 2;
// Duplicate the file descriptor
dup2( _pipes_pipeIndex0, STDIN_FILENO );
// Close the descriptor
close( _pipes_pipeIndex0 );
}
// If p_out is true update the pipe index
// and duplicate the file descriptor at the pipe
// index, then close said descriptor
if ( p_out ) {
// Update pipe index
_pipeIndex = _pipeLoc % 2;
// Duplicate the file descriptor
dup2( _pipes_pipeIndex1, STDIN_FILENO );
// Close the descriptor
close( _pipes_pipeIndex1 );
}
// If r_in is true then we are reading
// in from a file. Open the file and
// duplicate the file descriptor.
if ( r_in ) {
// Open the file for reading ("r")
FILE* inFile = fopen( holder.redirect_in, "r" );
// Get the integer descriptor and duplicate it
dup2( fileno( inFile ), STDIN_FILENO );
// fclose( inFile );
}
// If r_out is true there are two cases
// 1. Append to the file (adding to an existing one)
// 2. Write to a file (creates a new file with given
// name, or clears it if it exists already)
if ( r_out ) {
// Appending to the file
if ( r_app ) {
// Open the file for appending ("a")
FILE* outFile = fopen( holder.redirect_out, "a" );
// Get the integer descriptor and duplicate it
dup2( fileno( outFile ), STDOUT_FILENO );
// fclose( outFile );
} else {
// Writing to the file
// Open the file for writing ("w")
FILE* outFile = fopen( holder.redirect_out, "w" );
// Get the integer descriptor and duplicate it
dup2( fileno( outFile ), STDOUT_FILENO );
// fclose( outFile );
}
}

// Run the child command
child_run_command( holder.cmd );
// Exit
exit( 0 );
} else {
// Actions for the parent branch of fork
// If p_out is true update the pipe index, then
// close the descriptor
if ( p_out ) {
_pipeIndex = _pipeLoc % 2;

close( _pipes_pipeIndex1 );
}

// Run the parent command
parent_run_command( holder.cmd );
}
}

// Run a list of commands
void run_script(CommandHolder* holders) {
// Check if the job is initiated.
// If it is not then create a new job queue
// and set the isInit flag to true.
if ( !isInit ) {
// set job queue to new job queue
jobs = new_JobDequeue( 1 );
isInit = true;
}

if (holders == NULL)
return;

check_jobs_bg_status();

if (get_command_holder_type(holders0) == EXIT &&
get_command_holder_type(holders1) == EOC) {
end_main_loop();
return;
}

// Set the process id queue to a new queue
pids = new_PIDDequeue( 1 );
CommandType type;

// Run all commands in the `holder` array
for (int i = 0; (type = get_command_holder_type(holdersi)) != EOC; ++i) {
// Set the pipe index to update as the processes are created
_pipeLoc = i;
// Create the process
create_process(holdersi);
}

// If the job is not a background job
if (!(holders0.flags & BACKGROUND)) {
// Iterate through the process ids until the
// process id queue is emtpy
while ( !is_empty_PIDDequeue( &pids ) ) {
// Pop off the first process id
pid_t curPID = pop_front_PIDDequeue( &pids );
// Create a variable for the status
int curStatus = 0;
// Wait for the process to terminate
waitpid( curPID, &curStatus, 0 );
}
// Destroy the process id queue
destroy_PIDDequeue( &pids );
}
// If the job is a background job
else {
// Get the command string
char* command = get_command_string();

// Create a variable for the current job
// and set its members
// Source for below code snippet:
// https://stackoverflow.com/questions/32698293/assign-values-to-structure-variables
Job curJob;
curJob = (Job){ .jobId = jobNum, .cmd = command, .pid_list = pids };
// Increment the number of jobs
jobNum++;

// Set a local variable for the current job id
int curJobId = curJob.jobId;
// Set a local variable for te current process id
pid_t curPID = peek_front_PIDDequeue( &curJob.pid_list );
// Set a local variable for the current command
char* curJobCmd = curJob.cmd;

// Push the current job to the back of
// the job queue
push_back_JobDequeue( &jobs, curJob );
// Notify the user that the job has started
print_job_bg_start(curJobId, curPID, curJobCmd);
}
}