Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <errno.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdnoreturn.h>
- #include <string.h>
- #include <stdbool.h>
- #include <wait.h>
- #include <stdarg.h>
- #include <unistd.h>
- #include <ctype.h>
- #include "instrs.h"
- #define CHECK(op) do { if ((op) == -1) raler (1, #op); } while (0)
- #define POOLW_INDEX(index, size) (index+1) % size
- #define CONTAINER_DEF_SIZE 1024
- #define AVAILABLE_INSTRUCTIONS 12
- #define D_IN 0
- #define D_OUT 1
- #define CH_PID 0
- #define CH_INSTR 1
- enum pipe_mode {
- PIPE_R, PIPE_W
- };
- struct config {
- size_t pipePoolSize;
- size_t childPoolSize;
- size_t childMaxPoolSize;
- int mainId;
- int *childPool[2];
- int **pipePool;
- };
- struct execconfig {
- char *command;
- char **args; //flexible array member
- int argc;
- int reserved;
- };
- noreturn void raler(int syserr, const char *msg, ...) {
- va_list ap;
- va_start (ap, msg);
- vfprintf(stderr, msg, ap);
- fprintf(stderr, "\n");
- va_end (ap);
- if (syserr == 1)
- perror("");
- exit(EXIT_FAILURE);
- }
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- noreturn void usage(char *progname) {
- fprintf(stderr, "usage: %s [instructions]\n", progname);
- exit(EXIT_FAILURE);
- }
- /**
- * @brief hanndle child exit, if exit reason is != 0 => pid, errno == ECHILD => -1,
- * otherwise 0 (false) means child exit EXIT_SUCCESS
- *
- * @return
- */
- pid_t childEndHandler() {
- int status = 0;
- pid_t pid = 0;
- if ((pid = wait(&status)) && errno == ECHILD)
- return -1;
- CHECK(pid);
- if ((WIFEXITED(status) && WEXITSTATUS(status) != 0) || WIFSIGNALED(status)) {
- return pid;
- }
- return false;
- }
- /**
- * @brief moves data from one descriptor to another (fd_src to fd_dest)
- * @param fd_src
- * @param fd_dest
- */
- void moveData_fd(int fd_src, int fd_dest) {
- ssize_t readAmount = 0;
- char buffer[CONTAINER_DEF_SIZE];
- while ((readAmount = read(fd_src, buffer, CONTAINER_DEF_SIZE)) > 0) {
- CHECK(write(fd_dest, buffer, readAmount));
- }
- CHECK(readAmount);
- }
- /**
- * @brief reserves memmory for execconfig structure
- * @param amount
- * @return
- */
- struct execconfig *execconfigReserve(int amount) {
- // config = malloc(sizeof(struct execconfig));
- struct execconfig *config = malloc(sizeof(struct execconfig));
- if (config == NULL)
- raler(1, "config allocation error\n");
- config->args = malloc(sizeof(char *) * amount);
- if (config->args == NULL)
- raler(1, "config args allocation error\n");
- config->argc = 0;
- config->reserved = amount;
- return config;
- }
- /**
- * @brief adds arg to execconfig in argc place then increments argc
- * @param execconfig
- * @param arg
- */
- void execconfigAddArg(struct execconfig *execconfig, char *arg) {
- execconfig->args[execconfig->argc++] = arg;
- }
- /**
- * @brief return addrss of pointer of &(char* args[argc])
- * @param execconfig
- * @return
- */
- char **execconfigGetSlot(struct execconfig *execconfig) {
- return &execconfig->args[execconfig->argc];
- }
- //todo: refactor
- /**
- * @brief free execconfig arg, (never used cause exec* replace memory & free memory)
- * @param execconfig
- */
- void execconfigFree(struct execconfig *execconfig) {
- for (int i = 0; i < execconfig->argc; ++i) {
- printf("addr %p\n", execconfig->args[i]);
- // free(execconfig->args[i]);
- }
- free(execconfig);
- }
- /**
- * @brief checks if given str is a number by cehckking each char c with isDigit(c)
- * @param str
- * @return
- */
- bool isStrNumber(char *str) {
- int i = 0;
- for (char c = str[i]; c != '\0'; ++i, c = str[i]) {
- if (!isdigit(c))
- return false;
- }
- return true;
- }
- /**
- * @brief Reserves i * sizeof(char) bytes and stores it in buffer
- * @param buffer is an address of a pointer
- * @param i amount of bytes to reserve
- */
- void reserve(char **buffer, size_t i) {
- *buffer = calloc(i, sizeof(char));
- if (*buffer == NULL)
- raler(EXIT_FAILURE, "Reserve error\n");
- }
- /**
- * @brief sets command into config, plus sets args[0] to command
- * This function has to be called before other execconfigAddArg() functions
- * @param config pointer to config structure
- * @param command command to be stored
- */
- void execconfigSetCommand(struct execconfig *config, char *command) {
- config->command = command;
- execconfigAddArg(config, config->command);
- }
- /**
- * @brief create & configure execconfig structure according to given instruction
- * @param instruction instruction
- * @return pointer to allocated execconfig structure
- */
- struct execconfig *configAccordingInstruction(instruction_t *instruction) {
- struct execconfig *config = NULL;
- switch (instruction->type) {
- case INSTR_LOWERCASE:
- config = execconfigReserve(3);
- execconfigSetCommand(config, "tr");
- execconfigAddArg(config, "A-Z");
- execconfigAddArg(config, "a-z");
- break;
- case INSTR_UPPERCASE:
- config = execconfigReserve(3);
- execconfigSetCommand(config, "tr");
- execconfigAddArg(config, "a-z");
- execconfigAddArg(config, "A-Z");
- break;
- case INSTR_INVERT_CASE:
- config = execconfigReserve(3);
- execconfigSetCommand(config, "tr");
- execconfigAddArg(config, "a-zA-Z");
- execconfigAddArg(config, "A-Za-z");
- break;
- case INSTR_FILTER:
- config = execconfigReserve(3);
- execconfigSetCommand(config, "grep");
- execconfigAddArg(config, "-F");
- execconfigAddArg(config, instruction->argv[0]);
- break;
- case INSTR_EXCLUDE:
- config = execconfigReserve(3);
- execconfigSetCommand(config, "grep");
- execconfigAddArg(config, "-vF");
- execconfigAddArg(config, instruction->argv[0]);
- break;
- case INSTR_COUNT_WORDS:
- config = execconfigReserve(2);
- execconfigSetCommand(config, "wc");
- execconfigAddArg(config, "-w");
- break;
- case INSTR_COUNT_LINES:
- config = execconfigReserve(2);
- execconfigSetCommand(config, "wc");
- execconfigAddArg(config, "-l");
- break;
- case INSTR_COUNT_BYTES:
- config = execconfigReserve(2);
- execconfigSetCommand(config, "wc");
- execconfigAddArg(config, "-c");
- break;
- case INSTR_REPLACE: {
- config = execconfigReserve(2);
- execconfigSetCommand(config, "sed");
- size_t size = strlen(instruction->argv[0]) + strlen(instruction->argv[1]) + strlen("s///g") + 1;
- reserve((execconfigGetSlot(config)), size);
- CHECK(snprintf(*execconfigGetSlot(config), size, "s/%s/%s/g", instruction->argv[0], instruction->argv[1]));
- config->argc++;
- }
- break;
- case INSTR_TAKE:
- if (!isStrNumber(instruction->argv[0]))
- raler(1, "Error instruction param. instruction parameter is not a digit\n");
- config = execconfigReserve(3);
- execconfigSetCommand(config, "head");
- execconfigAddArg(config, "-n");
- execconfigAddArg(config, instruction->argv[0]);
- break;
- case INSTR_SKIP:
- if (!isStrNumber(instruction->argv[0]))
- raler(1, "Error instruction param. instruction parameter is not a digit\n");
- config = execconfigReserve(3);
- execconfigSetCommand(config, "tail");
- execconfigAddArg(config, "-n");
- // +1 => '\0', +1 for '+', +1 in case that adding 1 number x becomes x+1 chars wide
- size_t size = strlen(instruction->argv[0]) + 3;
- reserve((execconfigGetSlot(config)), size);
- int num = atoi(instruction->argv[0]) + 1;
- CHECK(snprintf(*execconfigGetSlot(config), size, "+%d", num));
- config->argc++;
- break;
- case INSTR_LAST:
- if (!isStrNumber(instruction->argv[0]))
- raler(1, "Error instruction param. instruction parameter is not a digit\n");
- config = execconfigReserve(3);
- execconfigSetCommand(config, "tail");
- execconfigAddArg(config, "-n");
- execconfigAddArg(config, instruction->argv[0]);
- break;
- }
- //set last argument to NULL conforming execvp prototype
- execconfigAddArg(config, NULL);
- return config;
- }
- /**
- * @brief executes execvp according to given instruction
- * @param instruction
- */
- void invokeTask(instruction_t *instruction) {
- // no free execconfig cause exec* release allocated memory
- struct execconfig *config = configAccordingInstruction(instruction);
- execvp(config->command, config->args);
- raler(EXIT_FAILURE, "Execvp error");
- }
- /**
- * @brief free pool
- * @param pool
- * @param size
- */
- void freePipePool(int **pool, int size) {
- for (int i = 0; i < size; i++) {
- free(pool[i]);
- }
- free(pool);
- }
- /**
- * @brief allocates memory for pipePool & calls pipe(pipePool[i])
- * @param size size of pool to allocate
- * @return
- */
- int **allocatePipePool(size_t size) {
- int **pipePool = malloc(sizeof(int *) * size);
- if (pipePool == NULL)
- raler(EXIT_FAILURE, "Error pipePool creation failed");
- for (size_t i = 0; i < size; i++) {
- pipePool[i] = malloc(sizeof(int) * 2);
- if (pipePool[i] == NULL)
- raler(EXIT_FAILURE, "Error pipePool creation failed");
- CHECK(pipe(pipePool[i]));
- }
- return pipePool;
- }
- /**
- * @biref configures pool according to given id |
- * conversation is in one direction
- * @param config
- * @param id
- */
- void configurePipePool(struct config *config, size_t id) {
- for (size_t i = 0; i < config->pipePoolSize; ++i) {
- if (i == id) {
- CHECK(close(config->pipePool[id][PIPE_W]));
- continue;
- } else if (i == (id + 1) % config->pipePoolSize) {
- CHECK(close(config->pipePool[(id + 1) % config->pipePoolSize][PIPE_R]));
- } else {
- CHECK(close(config->pipePool[i][PIPE_R]));
- CHECK(close(config->pipePool[i][PIPE_W]));
- }
- }
- }
- /**
- * @brief allocates memory for child pool
- * @param childPool
- * @param amount
- */
- void prepareChildPool(int **childPool, size_t amount) {
- *childPool = malloc(sizeof(int) * amount);
- if (*childPool == NULL)
- raler(EXIT_FAILURE, "ERROR: Failed to allocate childPool");
- }
- /**
- * @brief config main config structure according to given length
- * here pipePool will be allocatesd
- * @param config
- * @param length
- */
- void configure(struct config *config, size_t length) {
- config->mainId = (int) length;
- config->pipePoolSize = length + 1;
- config->pipePool = allocatePipePool(config->pipePoolSize);
- config->childPoolSize = 0;
- config->childMaxPoolSize = length;
- prepareChildPool(&config->childPool[CH_PID], config->childMaxPoolSize);
- prepareChildPool(&config->childPool[CH_INSTR], config->childMaxPoolSize);
- }
- /**
- * @biref terminates all child who are registered into the child pool.
- * @param config
- */
- void killChilds(struct config *config) {
- for (; 0 < config->childPoolSize; config->childPoolSize--) {
- kill(SIGTERM, config->childPool[CH_PID][config->childPoolSize - 1]);
- }
- //todo: [optional] implement sigkill after some amount of time; using sigalarm
- }
- /**
- * @brief checks if given process ends correctly
- * @param config
- * @param pid
- * @return
- */
- bool checkChildEndPerm(struct config *config, pid_t pid) {
- for (size_t i = 0; i < config->childPoolSize; i++) {
- if (
- (config->childPool[i][CH_PID] == pid &&
- config->childPool[i][CH_INSTR] == INSTR_FILTER) ||
- config->childPool[i][CH_INSTR] == INSTR_EXCLUDE) {
- return true;
- }
- }
- return false;
- }
- int main(int argc, char *argv[]) {
- if (argc != 2)
- usage(argv[0]);
- // On parse le fichier d'instructions
- instructions_t instructions = instrs_parse(argv[1]);
- struct config config = {0};
- configure(&config, instructions.length);
- if (instructions.length == 0) {
- moveData_fd(D_IN, D_OUT);
- instrs_free(instructions);
- exit(EXIT_SUCCESS);
- }
- // On parcourt les instructions, affichant leur type et leurs arguments
- for (size_t i = 0; i < instructions.length; i++) {
- instruction_t *instr = &instructions.list[i];
- //#############################################################################################################
- // test(instr);
- //#############################################################################################################
- pid_t pid;
- switch (pid = fork()) {
- case -1:
- killChilds(&config);
- raler(EXIT_FAILURE, "Fork failed");
- case 0:
- //configure config.pipePool
- configurePipePool(&config, i);
- //redirections
- CHECK(dup2(config.pipePool[i][PIPE_R], D_IN));
- CHECK(dup2(config.pipePool[POOLW_INDEX(i, config.pipePoolSize)][PIPE_W], D_OUT));
- close(config.pipePool[i][PIPE_R]);
- close(config.pipePool[POOLW_INDEX(i, config.pipePoolSize)][PIPE_W]);
- //invoke transform task
- invokeTask(instr);
- break;
- default:
- config.childPool[CH_PID][i] = pid;
- config.childPool[CH_INSTR][i] = instr->type;
- config.childPoolSize++;
- break;
- }
- }
- int oldInOut_fd[2];
- oldInOut_fd[D_IN] = dup(D_IN);
- oldInOut_fd[D_OUT] = dup(D_OUT);
- configurePipePool(&config, config.mainId);
- moveData_fd(oldInOut_fd[D_IN], config.pipePool[
- POOLW_INDEX(config.mainId, config.pipePoolSize)
- ][PIPE_W]
- );
- CHECK(close(config.pipePool[
- POOLW_INDEX(config.mainId, config.pipePoolSize)
- ][PIPE_W])
- );
- pid_t pid = 0;
- for (size_t i = 0; i < config.childPoolSize && pid != -1; i++) {
- if ((pid = childEndHandler()) && pid != -1 && !checkChildEndPerm(&config, pid)) {
- instrs_free(instructions);
- killChilds(&config);
- raler(EXIT_FAILURE, "Error transform");
- }
- }
- moveData_fd(config.pipePool[config.mainId][PIPE_R], oldInOut_fd[D_OUT]);
- // On finit par libérer la structure avec les instructions
- instrs_free(instructions);
- return EXIT_SUCCESS;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement