#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("\n"); return; } if ((line = read_line(input)) != NULL) { printf("%s\n", line); } else { printf("\n"); } free(line); fclose(input); } else { printf("\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; }