350 lines
8.6 KiB
C
350 lines
8.6 KiB
C
#include "hq.h"
|
|
|
|
int get_valid_number(char *rawNumber) {
|
|
if (strlen(rawNumber) == 0) {
|
|
return -1;
|
|
}
|
|
|
|
for (int i = 0; i < strlen(rawNumber); i++) {
|
|
if (!isdigit(rawNumber[i]) && !isspace(rawNumber[i])) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return atoi(rawNumber);
|
|
}
|
|
|
|
// Not in job.c as requires get_valid_number
|
|
struct Job *get_job_from_raw_id(char *rawJobId) {
|
|
int jobId = get_valid_number(rawJobId);
|
|
struct Job *job = get_job_from_id(jobId);
|
|
return job;
|
|
}
|
|
|
|
char **prepare_args_for_exec(char **arguments, int numberArgs) {
|
|
char **newArguments = malloc(sizeof(char *) * (numberArgs + 1));
|
|
|
|
for (int i = 0; i < numberArgs; i++) {
|
|
newArguments[i] = arguments[i];
|
|
}
|
|
newArguments[numberArgs] = '\0';
|
|
|
|
return newArguments;
|
|
}
|
|
|
|
void handle_child_fds(struct Job *job) {
|
|
// close unused filedescriptors
|
|
close(job->readFd[0]);
|
|
close(job->writeFd[1]);
|
|
|
|
// redirect process stdin and stdout to pipe
|
|
dup2(job->writeFd[0], STDIN_FILENO);
|
|
dup2(job->readFd[1], STDOUT_FILENO);
|
|
|
|
// close fds
|
|
close(job->readFd[1]);
|
|
close(job->writeFd[0]);
|
|
}
|
|
|
|
int execute_command(char *command, char **arguments, int numberArgs) {
|
|
char **nullpArguments = NULL;
|
|
int value = 0;
|
|
|
|
nullpArguments = prepare_args_for_exec(arguments, numberArgs);
|
|
value = execvp(command, nullpArguments);
|
|
free(nullpArguments);
|
|
|
|
return value;
|
|
}
|
|
|
|
void handle_spawn(char **arguments, int numberArgs) {
|
|
struct Job *job = (struct Job *)malloc(sizeof(struct Job));
|
|
memset(job, 0, sizeof(struct Job));
|
|
|
|
if (numberArgs < 1) {
|
|
printf("Error: Insufficient arguments\n");
|
|
return;
|
|
}
|
|
|
|
// Set command name and command status
|
|
job->command = strdup(arguments[0]);
|
|
job->status = (char *)malloc(sizeof(char) * (strlen("running") + 1));
|
|
strcpy(job->status, "running");
|
|
|
|
// Setup pipes
|
|
if (pipe(job->readFd) || pipe(job->writeFd)) {
|
|
perror("Pipe:");
|
|
}
|
|
|
|
int pid = fork();
|
|
if (!pid) {
|
|
handle_child_fds(job);
|
|
|
|
if (execute_command(job->command, arguments, numberArgs) == -1) {
|
|
exit(EXIT_CHLD_ERR);
|
|
}
|
|
exit(EXIT_OK);
|
|
} else {
|
|
// close unused filedescriptors
|
|
close(job->readFd[1]);
|
|
close(job->writeFd[0]);
|
|
|
|
job->pid = pid;
|
|
job->jobId = add_job(job);
|
|
printf("New Job ID [%d] created\n", job->jobId);
|
|
}
|
|
}
|
|
|
|
void report_job(struct Job *job) {
|
|
printf("[%d] %s:%s\n", job->jobId, job->command, job->status);
|
|
}
|
|
|
|
void handle_report(char **arguments, int numberArgs) {
|
|
if (numberArgs > 0) {
|
|
struct Job *job = get_job_from_raw_id(arguments[0]);
|
|
|
|
if (!job) {
|
|
printf("Error: Invalid job\n");
|
|
return;
|
|
}
|
|
|
|
printf("[Job] cmd:status\n");
|
|
report_job(job);
|
|
} else {
|
|
printf("[Job] cmd:status\n");
|
|
for (int i = 0; i < programJobs.length; i++) {
|
|
struct Job *currentJob = programJobs.jobs[i];
|
|
report_job(currentJob);
|
|
}
|
|
}
|
|
}
|
|
|
|
void handle_signal(char **arguments, int numberArgs) {
|
|
if (numberArgs < 2) {
|
|
printf("Error: Insufficient arguments\n");
|
|
return;
|
|
}
|
|
|
|
struct Job *job = get_job_from_raw_id(arguments[0]);
|
|
if (!job) {
|
|
printf("Error: Invalid job\n");
|
|
return;
|
|
}
|
|
|
|
int signal = get_valid_number(arguments[1]);
|
|
if (signal < 1 || signal > 31) {
|
|
printf("Error: Invalid signal\n");
|
|
return;
|
|
}
|
|
|
|
kill(job->pid, signal);
|
|
}
|
|
|
|
void handle_sleep(char **arguments, int numberArgs) {
|
|
char *endPtr;
|
|
struct timespec sleepTime;
|
|
memset(&sleepTime, 0, sizeof(struct timespec));
|
|
|
|
if (numberArgs < 1) {
|
|
printf("Error: Insufficient arguments\n");
|
|
return;
|
|
}
|
|
|
|
int errno = 0;
|
|
sleepTime.tv_sec = strtod(arguments[0], &endPtr);
|
|
if (errno != 0 || strlen(arguments[0]) == 0 || strcmp(endPtr, "") != 0) {
|
|
printf("Error: Invalid sleep time\n");
|
|
return;
|
|
}
|
|
|
|
// sleep is interrupted by sigchld
|
|
// https://stackoverflow.com/questions/17118105
|
|
while (nanosleep(&sleepTime, &sleepTime)) {
|
|
;
|
|
}
|
|
}
|
|
|
|
void handle_send(char **arguments, int numberArgs) {
|
|
if (numberArgs < 2) {
|
|
printf("Error: Insufficient arguments\n");
|
|
return;
|
|
}
|
|
|
|
struct Job *job = get_job_from_raw_id(arguments[0]);
|
|
if (!job) {
|
|
printf("Error: Invalid job\n");
|
|
return;
|
|
}
|
|
|
|
dprintf(job->writeFd[1], "%s\n", arguments[1]);
|
|
}
|
|
|
|
void handle_recieve(char **arguments, int numberArgs) {
|
|
if (numberArgs < 1) {
|
|
printf("Error: Insufficient arguments\n");
|
|
return;
|
|
}
|
|
|
|
struct Job *job = get_job_from_raw_id(arguments[0]);
|
|
if (!job) {
|
|
printf("Error: Invalid job\n");
|
|
return;
|
|
}
|
|
|
|
if (is_ready(job->readFd[0])) {
|
|
FILE *input = NULL;
|
|
char *line = NULL;
|
|
|
|
if ((input = fdopen(job->readFd[0], "r")) == NULL) {
|
|
printf("<EOF>\n");
|
|
return;
|
|
}
|
|
|
|
if ((line = read_line(input)) != NULL) {
|
|
printf("%s\n", line);
|
|
} else {
|
|
printf("<EOF>\n");
|
|
}
|
|
|
|
free(line);
|
|
fclose(input);
|
|
} else {
|
|
printf("<no input>\n");
|
|
}
|
|
}
|
|
|
|
void handle_eof(char **arguments, int numberArgs) {
|
|
if (numberArgs < 1) {
|
|
printf("Error: Insufficient arguments\n");
|
|
return;
|
|
}
|
|
|
|
struct Job *job = get_job_from_raw_id(arguments[0]);
|
|
if (!job) {
|
|
printf("Error: Invalid job\n");
|
|
return;
|
|
}
|
|
|
|
close(job->writeFd[1]);
|
|
}
|
|
|
|
void handle_cleanup(void) {
|
|
for (int i = 0; i < programJobs.length; i++) {
|
|
struct Job *currentJob = programJobs.jobs[i];
|
|
|
|
// Close all pipes and kill
|
|
close(currentJob->writeFd[1]);
|
|
close(currentJob->readFd[0]);
|
|
kill(currentJob->pid, SIGKILL);
|
|
}
|
|
// Don't need to wait, done in update_status signal handler
|
|
// If I wait here then the status does not get updated
|
|
}
|
|
|
|
void handle_commands(char *command, char **arguments, int numberArgs) {
|
|
// I really don't like using so many if statements but I don't know of
|
|
// another way
|
|
|
|
if (strcmp(command, "spawn") == 0) {
|
|
handle_spawn(arguments, numberArgs);
|
|
} else if (strcmp(command, "report") == 0) {
|
|
handle_report(arguments, numberArgs);
|
|
} else if (strcmp(command, "signal") == 0) {
|
|
handle_signal(arguments, numberArgs);
|
|
} else if (strcmp(command, "sleep") == 0) {
|
|
handle_sleep(arguments, numberArgs);
|
|
} else if (strcmp(command, "send") == 0) {
|
|
handle_send(arguments, numberArgs);
|
|
} else if (strcmp(command, "rcv") == 0) {
|
|
handle_recieve(arguments, numberArgs);
|
|
} else if (strcmp(command, "eof") == 0) {
|
|
handle_eof(arguments, numberArgs);
|
|
} else if (strcmp(command, "cleanup") == 0) {
|
|
handle_cleanup();
|
|
} else {
|
|
printf("Error: Invalid command\n");
|
|
}
|
|
}
|
|
|
|
void process_input(char *input) {
|
|
int numberTokens = 0;
|
|
char *foo = strdup(input);
|
|
char **tokens = split_space_not_quote(foo, &numberTokens);
|
|
|
|
// Check if empty command
|
|
if (numberTokens == 0) {
|
|
return;
|
|
}
|
|
|
|
// Get command and it's arguments
|
|
char *command = strdup(tokens[0]);
|
|
char **arguments = NULL;
|
|
if (numberTokens > 1) {
|
|
arguments = &tokens[1];
|
|
}
|
|
|
|
handle_commands(command, arguments, numberTokens - 1);
|
|
|
|
free(tokens);
|
|
free(foo);
|
|
free(command);
|
|
}
|
|
|
|
void build_status(char **dest, char *prefix, int state) {
|
|
size_t needed_size = snprintf(NULL, 0, "%s(%d)", prefix, state);
|
|
*dest = (char *)realloc(*dest, needed_size + sizeof(char));
|
|
sprintf(*dest, "%s(%d)", prefix, state);
|
|
}
|
|
|
|
void update_status(int s) {
|
|
int pid;
|
|
int state;
|
|
while ((pid = waitpid(-1, &state, WNOHANG)) > 0) {
|
|
struct Job *job = get_job_from_pid(pid);
|
|
if (!job) {
|
|
return;
|
|
}
|
|
|
|
if (WIFSIGNALED(state)) {
|
|
build_status(&(job->status), "signalled", WTERMSIG(state));
|
|
} else {
|
|
build_status(&(job->status), "exited", WEXITSTATUS(state));
|
|
}
|
|
}
|
|
}
|
|
|
|
void setup_signal_handlers(void) {
|
|
signal(SIGINT, SIG_IGN);
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
struct sigaction sa;
|
|
memset(&sa, 0, sizeof(sa));
|
|
sa.sa_handler = update_status;
|
|
sa.sa_flags = SA_RESTART;
|
|
|
|
sigaction(SIGCHLD, &sa, 0);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
setup_signal_handlers();
|
|
|
|
programJobs.jobs = NULL;
|
|
programJobs.length = 0;
|
|
|
|
printf("> ");
|
|
fflush(stdout);
|
|
|
|
char *line = NULL;
|
|
while ((line = read_line(stdin)) != NULL) {
|
|
process_input(line);
|
|
|
|
printf("> ");
|
|
fflush(stdout);
|
|
free(line);
|
|
}
|
|
|
|
handle_cleanup();
|
|
deinit_jobs();
|
|
return EXIT_OK;
|
|
}
|