init
This commit is contained in:
349
hq.c
Normal file
349
hq.c
Normal file
@@ -0,0 +1,349 @@
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user