Advertisement
1fractal

Untitled

Apr 25th, 2021
1,158
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 15.00 KB | None | 0 0
  1. #include <errno.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <stdnoreturn.h>
  5. #include <string.h>
  6. #include <stdbool.h>
  7. #include <wait.h>
  8. #include <stdarg.h>
  9. #include <unistd.h>
  10. #include <ctype.h>
  11.  
  12. #include "instrs.h"
  13.  
  14. #define CHECK(op) do { if ((op) == -1) raler (1, #op); } while (0)
  15. #define POOLW_INDEX(index, size) (index+1) % size
  16. #define CONTAINER_DEF_SIZE 1024
  17. #define AVAILABLE_INSTRUCTIONS 12
  18. #define D_IN 0
  19. #define D_OUT 1
  20.  
  21. #define CH_PID 0
  22. #define CH_INSTR 1
  23.  
  24. enum pipe_mode {
  25.     PIPE_R, PIPE_W
  26. };
  27.  
  28. struct config {
  29.     size_t pipePoolSize;
  30.     size_t childPoolSize;
  31.     size_t childMaxPoolSize;
  32.     int mainId;
  33.     int *childPool[2];
  34.     int **pipePool;
  35. };
  36.  
  37. struct execconfig {
  38.     char *command;
  39.     char **args;     //flexible array member
  40.     int argc;
  41.     int reserved;
  42.  
  43. };
  44.  
  45. noreturn void raler(int syserr, const char *msg, ...) {
  46.     va_list ap;
  47.  
  48.     va_start (ap, msg);
  49.     vfprintf(stderr, msg, ap);
  50.     fprintf(stderr, "\n");
  51.     va_end (ap);
  52.  
  53.     if (syserr == 1)
  54.         perror("");
  55.  
  56.     exit(EXIT_FAILURE);
  57. }
  58.  
  59. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  60. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  61.  
  62. noreturn void usage(char *progname) {
  63.     fprintf(stderr, "usage: %s [instructions]\n", progname);
  64.     exit(EXIT_FAILURE);
  65. }
  66.  
  67. /**
  68.  * @brief hanndle child exit, if exit reason is != 0 => pid, errno == ECHILD => -1,
  69.  * otherwise 0 (false) means child exit EXIT_SUCCESS
  70.  *
  71.  * @return
  72.  */
  73. pid_t childEndHandler() {
  74.     int status = 0;
  75.     pid_t pid = 0;
  76.     if ((pid = wait(&status)) && errno == ECHILD)
  77.         return -1;
  78.     CHECK(pid);
  79.     if ((WIFEXITED(status) && WEXITSTATUS(status) != 0) || WIFSIGNALED(status)) {
  80.         return pid;
  81.     }
  82.  
  83.     return false;
  84. }
  85.  
  86. /**
  87.  * @brief moves data from one descriptor to another (fd_src to fd_dest)
  88.  * @param fd_src
  89.  * @param fd_dest
  90.  */
  91. void moveData_fd(int fd_src, int fd_dest) {
  92.     ssize_t readAmount = 0;
  93.     char buffer[CONTAINER_DEF_SIZE];
  94.     while ((readAmount = read(fd_src, buffer, CONTAINER_DEF_SIZE)) > 0) {
  95.         CHECK(write(fd_dest, buffer, readAmount));
  96.     }
  97.     CHECK(readAmount);
  98. }
  99.  
  100. /**
  101.  * @brief reserves memmory for execconfig structure
  102.  * @param amount
  103.  * @return
  104.  */
  105. struct execconfig *execconfigReserve(int amount) {
  106. //    config = malloc(sizeof(struct execconfig));
  107.     struct execconfig *config = malloc(sizeof(struct execconfig));
  108.     if (config == NULL)
  109.         raler(1, "config allocation error\n");
  110.  
  111.  
  112.     config->args = malloc(sizeof(char *) * amount);
  113.     if (config->args == NULL)
  114.         raler(1, "config args allocation error\n");
  115.  
  116.     config->argc = 0;
  117.     config->reserved = amount;
  118.     return config;
  119. }
  120.  
  121. /**
  122.  * @brief adds arg to execconfig in argc place then increments argc
  123.  * @param execconfig
  124.  * @param arg
  125.  */
  126. void execconfigAddArg(struct execconfig *execconfig, char *arg) {
  127.     execconfig->args[execconfig->argc++] = arg;
  128. }
  129.  
  130. /**
  131.  * @brief return addrss of pointer of &(char* args[argc])
  132.  * @param execconfig
  133.  * @return
  134.  */
  135. char **execconfigGetSlot(struct execconfig *execconfig) {
  136.     return &execconfig->args[execconfig->argc];
  137. }
  138.  
  139. //todo: refactor
  140. /**
  141.  * @brief free execconfig arg, (never used cause exec* replace memory & free memory)
  142.  * @param execconfig
  143.  */
  144. void execconfigFree(struct execconfig *execconfig) {
  145.     for (int i = 0; i < execconfig->argc; ++i) {
  146.         printf("addr %p\n", execconfig->args[i]);
  147. //        free(execconfig->args[i]);
  148.     }
  149.     free(execconfig);
  150. }
  151.  
  152. /**
  153.  * @brief checks if given str is a number by cehckking each char c with isDigit(c)
  154.  * @param str
  155.  * @return
  156.  */
  157. bool isStrNumber(char *str) {
  158.     int i = 0;
  159.     for (char c = str[i]; c != '\0'; ++i, c = str[i]) {
  160.         if (!isdigit(c))
  161.             return false;
  162.     }
  163.     return true;
  164. }
  165.  
  166. /**
  167.  * @brief Reserves i * sizeof(char) bytes and stores it in buffer
  168.  * @param buffer  is an address of a pointer
  169.  * @param i amount of bytes to reserve
  170.  */
  171. void reserve(char **buffer, size_t i) {
  172.     *buffer = calloc(i, sizeof(char));
  173.     if (*buffer == NULL)
  174.         raler(EXIT_FAILURE, "Reserve error\n");
  175. }
  176.  
  177. /**
  178.  * @brief sets command into config, plus sets  args[0] to command
  179.  * This function has to be called before other execconfigAddArg() functions
  180.  * @param config pointer to config structure
  181.  * @param command command to be stored
  182.  */
  183. void execconfigSetCommand(struct execconfig *config, char *command) {
  184.     config->command = command;
  185.     execconfigAddArg(config, config->command);
  186. }
  187.  
  188. /**
  189.  * @brief create & configure execconfig structure according to given instruction
  190.  * @param instruction instruction
  191.  * @return pointer to allocated execconfig structure
  192.  */
  193. struct execconfig *configAccordingInstruction(instruction_t *instruction) {
  194.     struct execconfig *config = NULL;
  195.  
  196.     switch (instruction->type) {
  197.  
  198.         case INSTR_LOWERCASE:
  199.             config = execconfigReserve(3);
  200.             execconfigSetCommand(config, "tr");
  201.             execconfigAddArg(config, "A-Z");
  202.             execconfigAddArg(config, "a-z");
  203.             break;
  204.  
  205.         case INSTR_UPPERCASE:
  206.             config = execconfigReserve(3);
  207.             execconfigSetCommand(config, "tr");
  208.             execconfigAddArg(config, "a-z");
  209.             execconfigAddArg(config, "A-Z");
  210.             break;
  211.  
  212.         case INSTR_INVERT_CASE:
  213.             config = execconfigReserve(3);
  214.             execconfigSetCommand(config, "tr");
  215.             execconfigAddArg(config, "a-zA-Z");
  216.             execconfigAddArg(config, "A-Za-z");
  217.             break;
  218.  
  219.         case INSTR_FILTER:
  220.             config = execconfigReserve(3);
  221.             execconfigSetCommand(config, "grep");
  222.             execconfigAddArg(config, "-F");
  223.             execconfigAddArg(config, instruction->argv[0]);
  224.             break;
  225.  
  226.         case INSTR_EXCLUDE:
  227.             config = execconfigReserve(3);
  228.             execconfigSetCommand(config, "grep");
  229.             execconfigAddArg(config, "-vF");
  230.             execconfigAddArg(config, instruction->argv[0]);
  231.             break;
  232.  
  233.         case INSTR_COUNT_WORDS:
  234.             config = execconfigReserve(2);
  235.             execconfigSetCommand(config, "wc");
  236.             execconfigAddArg(config, "-w");
  237.             break;
  238.  
  239.         case INSTR_COUNT_LINES:
  240.             config = execconfigReserve(2);
  241.             execconfigSetCommand(config, "wc");
  242.             execconfigAddArg(config, "-l");
  243.             break;
  244.  
  245.         case INSTR_COUNT_BYTES:
  246.             config = execconfigReserve(2);
  247.             execconfigSetCommand(config, "wc");
  248.             execconfigAddArg(config, "-c");
  249.             break;
  250.  
  251.         case INSTR_REPLACE: {
  252.             config = execconfigReserve(2);
  253.             execconfigSetCommand(config, "sed");
  254.             size_t size = strlen(instruction->argv[0]) + strlen(instruction->argv[1]) + strlen("s///g") + 1;
  255.             reserve((execconfigGetSlot(config)), size);
  256.             CHECK(snprintf(*execconfigGetSlot(config), size, "s/%s/%s/g", instruction->argv[0], instruction->argv[1]));
  257.             config->argc++;
  258.         }
  259.             break;
  260.  
  261.         case INSTR_TAKE:
  262.             if (!isStrNumber(instruction->argv[0]))
  263.                 raler(1, "Error instruction param. instruction parameter is not a digit\n");
  264.  
  265.             config = execconfigReserve(3);
  266.             execconfigSetCommand(config, "head");
  267.             execconfigAddArg(config, "-n");
  268.             execconfigAddArg(config, instruction->argv[0]);
  269.             break;
  270.  
  271.         case INSTR_SKIP:
  272.             if (!isStrNumber(instruction->argv[0]))
  273.                 raler(1, "Error instruction param. instruction parameter is not a digit\n");
  274.  
  275.             config = execconfigReserve(3);
  276.             execconfigSetCommand(config, "tail");
  277.             execconfigAddArg(config, "-n");
  278.  
  279.             // +1 => '\0', +1 for '+', +1 in case that adding 1 number x becomes x+1 chars wide
  280.             size_t size = strlen(instruction->argv[0]) + 3;
  281.             reserve((execconfigGetSlot(config)), size);
  282.             int num = atoi(instruction->argv[0]) + 1;
  283.  
  284.             CHECK(snprintf(*execconfigGetSlot(config), size, "+%d", num));
  285.  
  286.             config->argc++;
  287.             break;
  288.  
  289.         case INSTR_LAST:
  290.             if (!isStrNumber(instruction->argv[0]))
  291.                 raler(1, "Error instruction param. instruction parameter is not a digit\n");
  292.  
  293.             config = execconfigReserve(3);
  294.             execconfigSetCommand(config, "tail");
  295.             execconfigAddArg(config, "-n");
  296.             execconfigAddArg(config, instruction->argv[0]);
  297.             break;
  298.     }
  299.     //set last argument to NULL conforming execvp  prototype
  300.     execconfigAddArg(config, NULL);
  301.     return config;
  302. }
  303.  
  304. /**
  305.  * @brief executes execvp according to given instruction
  306.  * @param instruction
  307.  */
  308. void invokeTask(instruction_t *instruction) {
  309.     // no free execconfig cause exec* release allocated memory
  310.     struct execconfig *config = configAccordingInstruction(instruction);
  311.     execvp(config->command, config->args);
  312.     raler(EXIT_FAILURE, "Execvp error");
  313. }
  314.  
  315. /**
  316.  * @brief free pool
  317.  * @param pool
  318.  * @param size
  319.  */
  320. void freePipePool(int **pool, int size) {
  321.     for (int i = 0; i < size; i++) {
  322.         free(pool[i]);
  323.     }
  324.     free(pool);
  325. }
  326.  
  327. /**
  328.  * @brief allocates memory for pipePool & calls pipe(pipePool[i])
  329.  * @param size size of pool to allocate
  330.  * @return
  331.  */
  332. int **allocatePipePool(size_t size) {
  333.  
  334.     int **pipePool = malloc(sizeof(int *) * size);
  335.     if (pipePool == NULL)
  336.         raler(EXIT_FAILURE, "Error pipePool creation failed");
  337.  
  338.     for (size_t i = 0; i < size; i++) {
  339.         pipePool[i] = malloc(sizeof(int) * 2);
  340.         if (pipePool[i] == NULL)
  341.             raler(EXIT_FAILURE, "Error pipePool creation failed");
  342.  
  343.         CHECK(pipe(pipePool[i]));
  344.     }
  345.     return pipePool;
  346. }
  347.  
  348. /**
  349.  * @biref configures pool according to given id |
  350.  * conversation is in one direction
  351.  * @param config
  352.  * @param id
  353.  */
  354. void configurePipePool(struct config *config, size_t id) {
  355.     for (size_t i = 0; i < config->pipePoolSize; ++i) {
  356.         if (i == id) {
  357.             CHECK(close(config->pipePool[id][PIPE_W]));
  358.             continue;
  359.         } else if (i == (id + 1) % config->pipePoolSize) {
  360.             CHECK(close(config->pipePool[(id + 1) % config->pipePoolSize][PIPE_R]));
  361.         } else {
  362.             CHECK(close(config->pipePool[i][PIPE_R]));
  363.             CHECK(close(config->pipePool[i][PIPE_W]));
  364.         }
  365.     }
  366. }
  367.  
  368. /**
  369.  * @brief allocates memory for child pool
  370.  * @param childPool
  371.  * @param amount
  372.  */
  373. void prepareChildPool(int **childPool, size_t amount) {
  374.     *childPool = malloc(sizeof(int) * amount);
  375.     if (*childPool == NULL)
  376.         raler(EXIT_FAILURE, "ERROR: Failed to allocate childPool");
  377. }
  378.  
  379. /**
  380.  * @brief config main config structure according to given length
  381.  * here pipePool will be allocatesd
  382.  * @param config
  383.  * @param length
  384.  */
  385. void configure(struct config *config, size_t length) {
  386.     config->mainId = (int) length;
  387.     config->pipePoolSize = length + 1;
  388.     config->pipePool = allocatePipePool(config->pipePoolSize);
  389.     config->childPoolSize = 0;
  390.     config->childMaxPoolSize = length;
  391.  
  392.     prepareChildPool(&config->childPool[CH_PID], config->childMaxPoolSize);
  393.     prepareChildPool(&config->childPool[CH_INSTR], config->childMaxPoolSize);
  394. }
  395.  
  396. /**
  397.  * @biref terminates all child who are registered into the child pool.
  398.  * @param config
  399.  */
  400. void killChilds(struct config *config) {
  401.     for (; 0 < config->childPoolSize; config->childPoolSize--) {
  402.         kill(SIGTERM, config->childPool[CH_PID][config->childPoolSize - 1]);
  403.     }
  404.     //todo: [optional] implement sigkill after some amount of time; using sigalarm
  405. }
  406.  
  407. /**
  408.  * @brief checks if given process ends correctly
  409.  * @param config
  410.  * @param pid
  411.  * @return
  412.  */
  413. bool checkChildEndPerm(struct config *config, pid_t pid) {
  414.     for (size_t i = 0; i < config->childPoolSize; i++) {
  415.         if (
  416.                 (config->childPool[i][CH_PID] == pid &&
  417.                  config->childPool[i][CH_INSTR] == INSTR_FILTER) ||
  418.                 config->childPool[i][CH_INSTR] == INSTR_EXCLUDE) {
  419.             return true;
  420.         }
  421.     }
  422.     return false;
  423. }
  424.  
  425. int main(int argc, char *argv[]) {
  426.     if (argc != 2)
  427.         usage(argv[0]);
  428.  
  429.     // On parse le fichier d'instructions
  430.     instructions_t instructions = instrs_parse(argv[1]);
  431.  
  432.     struct config config = {0};
  433.     configure(&config, instructions.length);
  434.  
  435.     if (instructions.length == 0) {
  436.         moveData_fd(D_IN, D_OUT);
  437.         instrs_free(instructions);
  438.         exit(EXIT_SUCCESS);
  439.     }
  440.  
  441.     // On parcourt les instructions, affichant leur type et leurs arguments
  442.     for (size_t i = 0; i < instructions.length; i++) {
  443.  
  444.         instruction_t *instr = &instructions.list[i];
  445.  
  446.         //#############################################################################################################
  447. //        test(instr);
  448.         //#############################################################################################################
  449.  
  450.         pid_t pid;
  451.         switch (pid = fork()) {
  452.             case -1:
  453.                 killChilds(&config);
  454.                 raler(EXIT_FAILURE, "Fork failed");
  455.  
  456.             case 0:
  457.                 //configure config.pipePool
  458.                 configurePipePool(&config, i);
  459.  
  460.                 //redirections
  461.                 CHECK(dup2(config.pipePool[i][PIPE_R], D_IN));
  462.                 CHECK(dup2(config.pipePool[POOLW_INDEX(i, config.pipePoolSize)][PIPE_W], D_OUT));
  463.  
  464.                 close(config.pipePool[i][PIPE_R]);
  465.                 close(config.pipePool[POOLW_INDEX(i, config.pipePoolSize)][PIPE_W]);
  466.  
  467.                 //invoke transform task
  468.                 invokeTask(instr);
  469.                 break;
  470.  
  471.             default:
  472.                 config.childPool[CH_PID][i] = pid;
  473.                 config.childPool[CH_INSTR][i] = instr->type;
  474.                 config.childPoolSize++;
  475.                 break;
  476.  
  477.         }
  478.     }
  479.     int oldInOut_fd[2];
  480.     oldInOut_fd[D_IN] = dup(D_IN);
  481.     oldInOut_fd[D_OUT] = dup(D_OUT);
  482.  
  483.  
  484.     configurePipePool(&config, config.mainId);
  485.  
  486.     moveData_fd(oldInOut_fd[D_IN], config.pipePool[
  487.                         POOLW_INDEX(config.mainId, config.pipePoolSize)
  488.                 ][PIPE_W]
  489.     );
  490.  
  491.     CHECK(close(config.pipePool[
  492.                         POOLW_INDEX(config.mainId, config.pipePoolSize)
  493.                 ][PIPE_W])
  494.     );
  495.  
  496.     pid_t pid = 0;
  497.     for (size_t i = 0; i < config.childPoolSize && pid != -1; i++) {
  498.         if ((pid = childEndHandler()) && pid != -1 && !checkChildEndPerm(&config, pid)) {
  499.  
  500.             instrs_free(instructions);
  501.             killChilds(&config);
  502.             raler(EXIT_FAILURE, "Error transform");
  503.         }
  504.     }
  505.  
  506.     moveData_fd(config.pipePool[config.mainId][PIPE_R], oldInOut_fd[D_OUT]);
  507.  
  508.     // On finit par libérer la structure avec les instructions
  509.     instrs_free(instructions);
  510.  
  511.     return EXIT_SUCCESS;
  512. }
  513.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement