├── LVL 2 └── VBC │ ├── Argo │ ├── test.json │ ├── argo │ ├── argo.h │ ├── main.c │ └── argo.c │ ├── subject.txt │ ├── given_code.c │ └── vbc.c ├── README.md └── LVL 1 ├── ft_popen.c ├── picoshell.c └── sandbox.c /LVL 2/VBC/Argo/test.json: -------------------------------------------------------------------------------- 1 | {"recursion":{"recursion":{"recursion":{"recursion":"recursion"}}}} -------------------------------------------------------------------------------- /LVL 2/VBC/Argo/argo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lastftw/42-Exam-rank-4/HEAD/LVL 2/VBC/Argo/argo -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 42-Exam-rank-4 2 | Answer for exam rank 4 (42 School) 3 | 4 | 5 | If anyone have better options / optimisations or even if you had passed the exam and have the corrected exercices, let me know ! 6 | 7 | DISCORD : Lastdady 8 | -------------------------------------------------------------------------------- /LVL 2/VBC/Argo/argo.h: -------------------------------------------------------------------------------- 1 | #ifndef ARGO_H 2 | # define ARGO_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | typedef struct json { 12 | enum { 13 | MAP, 14 | INTEGER, 15 | STRING 16 | } type; 17 | union { 18 | struct { 19 | struct pair *data; 20 | size_t size; 21 | } map; 22 | int integer; 23 | char *string; 24 | }; 25 | } json; 26 | 27 | typedef struct pair { 28 | char *key; 29 | json value; 30 | } pair; 31 | 32 | void free_json(json j); 33 | int argo(json *dst, FILE *stream); 34 | int peek(FILE *stream); 35 | void unexpected(FILE *stream); 36 | int accept(FILE *stream, char c); 37 | int expect(FILE *stream, char c); 38 | 39 | #endif -------------------------------------------------------------------------------- /LVL 2/VBC/subject.txt: -------------------------------------------------------------------------------- 1 | // Assignment name: vbc 2 | // Expected files: *.c *h 3 | // Allowed functions: malloc, calloc, realloc, free, printf, isdigit, write 4 | 5 | // Write a program that prints the result of a mathematical expression given as argument. 6 | // It must handle addition, multiplication and parenthesis. All values are between 0 and 9 included. 7 | // In case of an unexpected symbol, you must print "Unexpected token '%c'\n". 8 | // If the expression ends unexpectedly, you must print "Unexpected end of input\n". 9 | // The same rule applies if finding an unexpected '(' or ')'. 10 | // In case of a syscall failure, you must exit with 1. 11 | 12 | // Examples: 13 | // $> ./vbc '1' | cat -e 14 | // 1$ 15 | // $> ./vbc '2+3' | cat -e 16 | // 5$ 17 | // $> ./vbc '3*4+5' | cat -e 18 | // 17$ 19 | // $> ./vbc '3+4*5' | cat -e 20 | // 23$ 21 | // $> ./vbc '(3+4)*5' | cat -e 22 | // 35$ 23 | // $> ./vbc '(((((2+2)*2+2)*2+2)*2+2)*2+2)*2' | cat -e 24 | // 188$ 25 | // $> ./vbc '1+' 26 | // Unexpected end of input 27 | // $> ./vbc '1+2)' | cat -e 28 | // Unexpected token ')'$ 29 | 30 | // File provided: vbc.c, see below. -------------------------------------------------------------------------------- /LVL 2/VBC/Argo/main.c: -------------------------------------------------------------------------------- 1 | #include "argo.h" 2 | 3 | 4 | 5 | void free_json(json j) 6 | { 7 | switch (j.type) 8 | { 9 | case MAP: 10 | for (size_t i = 0; i < j.map.size; i++) 11 | { 12 | free(j.map.data[i].key); 13 | free_json(j.map.data[i].value); 14 | } 15 | free(j.map.data); 16 | break ; 17 | case STRING: 18 | free(j.string); 19 | break ; 20 | default: 21 | break ; 22 | } 23 | } 24 | 25 | void serialize(json j) 26 | { 27 | switch (j.type) 28 | { 29 | case INTEGER: 30 | printf("%d", j.integer); 31 | break ; 32 | case STRING: 33 | putchar('"'); 34 | for (int i = 0; j.string[i]; i++) 35 | { 36 | if (j.string[i] == '\\' || j.string[i] == '"') 37 | putchar('\\'); 38 | putchar(j.string[i]); 39 | } 40 | putchar('"'); 41 | break ; 42 | case MAP: 43 | putchar('{'); 44 | for (size_t i = 0; i < j.map.size; i++) 45 | { 46 | if (i != 0) 47 | putchar(','); 48 | serialize((json){.type = STRING, .string = j.map.data[i].key}); 49 | putchar(':'); 50 | serialize(j.map.data[i].value); 51 | } 52 | putchar('}'); 53 | break ; 54 | } 55 | } 56 | 57 | int main(int argc, char **argv) 58 | { 59 | if (argc != 2) 60 | return 1; 61 | char *filename = argv[1]; 62 | FILE *stream = fopen(filename, "r"); 63 | json file; 64 | if (argo(&file, stream) != 1) 65 | { 66 | free_json(file); 67 | return 1; 68 | } 69 | serialize(file); 70 | free_json(file); 71 | printf("\n"); 72 | } -------------------------------------------------------------------------------- /LVL 2/VBC/given_code.c: -------------------------------------------------------------------------------- 1 | //This file is given at the exam 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct node { 8 | enum { 9 | ADD, 10 | MULTI, 11 | VAL 12 | } type; 13 | int val; 14 | struct node *l; 15 | struct node *r; 16 | } node; 17 | 18 | node *new_node(node n) 19 | { 20 | node *ret = calloc(1, sizeof(n)); 21 | if (!ret) 22 | return (NULL); 23 | *ret = n; 24 | return (ret); 25 | } 26 | 27 | void destroy_tree(node *n) 28 | { 29 | if (!n) 30 | return ; 31 | if (n->type != VAL) 32 | { 33 | destroy_tree(n->l); 34 | destroy_tree(n->r); 35 | } 36 | free(n); 37 | } 38 | 39 | void unexpected(char c) 40 | { 41 | if (c) 42 | printf("Unexpected token '%c'\n", c); 43 | else 44 | printf("Unexpected end of file\n"); 45 | } 46 | 47 | int accept(char **s, char c) 48 | { 49 | if (**s) 50 | { 51 | (*s)++; 52 | return (1); 53 | } 54 | return (0); 55 | } 56 | 57 | int expect(char **s, char c) 58 | { 59 | if (accept(s, c)) 60 | return (1); 61 | unexpected(**s); 62 | return (0); 63 | } 64 | 65 | //... 66 | 67 | node *parse_expr(char *s) 68 | { 69 | //... 70 | 71 | if (*s) 72 | { 73 | destroy_tree(ret); 74 | return (NULL); 75 | } 76 | return (ret); 77 | } 78 | 79 | int eval_tree(node *tree) 80 | { 81 | switch (tree->type) 82 | { 83 | case ADD: 84 | return (eval_tree(tree->l) + eval_tree(tree->r)); 85 | case MULTI: 86 | return (eval_tree(tree->l) * eval_tree(tree->r)); 87 | case VAL: 88 | return (tree->val); 89 | } 90 | } 91 | 92 | int main(int argc, char **argv) 93 | { 94 | if (argc != 2) 95 | return (1); 96 | node *tree = parse_expr(argv[1]); 97 | if (!tree) 98 | return (1); 99 | printf("%d\n", eval_tree(tree)); 100 | destroy_tree(tree); 101 | } -------------------------------------------------------------------------------- /LVL 1/ft_popen.c: -------------------------------------------------------------------------------- 1 | /* Allowed functions: pipe, fork, dup2, execvp, close, exit 2 | 3 | write the following function: 4 | 5 | int ft_popen(const char file, charconst argv[], char type) 6 | 7 | The function must launch the executable file with the arguments argv (using execvp). 8 | If the type is 'r' the function must return a file descriptor connected to the output of the command. 9 | If the type is 'w' the function must return a file descriptor connected to the input of the command. 10 | In case of error or invalid parameter the function must return -1. 11 | 12 | example: 13 | 14 | int main() { 15 | int fd = ft_popen("ls", (char const[]){"ls", NULL}, 'r'); 16 | 17 | charline; 18 | while(line = get_next_line(fd)) 19 | ft_putstr(line); 20 | } 21 | 22 | Hint: Do not leak file descriptors! */ 23 | 24 | #include 25 | #include 26 | 27 | int ft_popen(const char *file, char *const av[], int type) 28 | { 29 | if(!file || !av || (type != 'r' && type !='w' )) 30 | return -1; 31 | 32 | int fd[2]; 33 | if (pipe(fd) < 0) 34 | return -1; 35 | pid_t pid = fork(); 36 | if (pid < 0) { 37 | close(fd[1]); 38 | close(fd[0]); 39 | return -1; 40 | } 41 | if(pid == 0) { 42 | if(type == 'r') { 43 | close(fd[0]); 44 | if(dup2(fd[1], STDOUT_FILENO) < 0) 45 | exit (-1); 46 | } else { 47 | close(fd[1]); 48 | if(dup2(fd[0], STDIN_FILENO) < 0) 49 | exit (-1); 50 | } 51 | close(fd[0]); 52 | close(fd[1]); 53 | execvp(file, av); 54 | exit (-1); 55 | } 56 | if (type == 'r') { 57 | close(fd[1]); 58 | return (fd[0]); 59 | } else { 60 | close(fd[0]); 61 | return (fd[1]); 62 | } 63 | } 64 | 65 | 66 | /*#include 67 | #include 68 | int main() 69 | { 70 | //int fd = open("texte", O_RDONLY); 71 | int fd = ft_popen("lss", (char *const[]){"ls", NULL}, 'r'); 72 | 73 | char buf[1]; 74 | while(read(fd, buf, 1)) 75 | write(1, buf, 1); 76 | 77 | close(fd); 78 | return (0); 79 | }*/ -------------------------------------------------------------------------------- /LVL 1/picoshell.c: -------------------------------------------------------------------------------- 1 | /* Assignment name: picoshell 2 | Expected files: picoshell.c 3 | Allowed functions: close, fork, wait, exit, execvp, dup2, pipe 4 | ___ 5 | 6 | Write the following function: 7 | 8 | int picoshell(char *cmds[]); 9 | 10 | The goal of this function is to execute a pipeline. It must execute each 11 | commands [sic] of cmds and connect the output of one to the input of the 12 | next command (just like a shell). 13 | 14 | Cmds contains a null-terminated list of valid commands. Each rows [sic] 15 | of cmds are an argv array directly usable for a call to execvp. The first 16 | arguments [sic] of each command is the command name or path and can be passed 17 | directly as the first argument of execvp. 18 | 19 | If any error occur [sic], The function must return 1 (you must of course 20 | close all the open fds before). otherwise the function must wait all child 21 | processes and return 0. You will find in this directory a file main.c which 22 | contain [sic] something to help you test your function. 23 | 24 | 25 | Examples: 26 | ./picoshell /bin/ls "|" /usr/bin/grep picoshell 27 | picoshell 28 | ./picoshell echo 'squalala' "|" cat "|" sed 's/a/b/g' 29 | squblblb/ 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | int picoshell(char **cmds[]) 37 | { 38 | int i = 0; 39 | int fd[2]; 40 | int in_fd = 0; // stdin par défaut (0) 41 | int ret = 0; 42 | pid_t pid; 43 | int status; 44 | 45 | while (cmds[i]) 46 | { 47 | if (cmds[i + 1]) // Si ce n’est pas la dernière commande 48 | { 49 | if (pipe(fd) == -1) 50 | return 1; 51 | } 52 | else 53 | { 54 | fd[0] = -1; 55 | fd[1] = -1; 56 | } 57 | 58 | pid = fork(); 59 | if (pid < 0) 60 | { 61 | if (fd[0] != -1) 62 | close(fd[0]); 63 | if (fd[1] != -1) 64 | close(fd[1]); 65 | if (in_fd != 0) 66 | close(in_fd); 67 | return 1; 68 | } 69 | if (pid == 0) // Enfant 70 | { 71 | if (in_fd != 0) 72 | { 73 | if (dup2(in_fd, 0) == -1) 74 | exit(1); 75 | close(in_fd); 76 | } 77 | if (fd[1] != -1) // si ce n'est pas le dernier 78 | { 79 | if (dup2(fd[1], 1) == -1) 80 | exit(1); 81 | close(fd[1]); 82 | close(fd[0]); 83 | } 84 | execvp(cmds[i][0], cmds[i]); 85 | exit(1); // execvp échoué 86 | } 87 | else // Parent 88 | { 89 | if (in_fd != 0) 90 | close(in_fd); 91 | if (fd[1] != -1) 92 | close(fd[1]); 93 | in_fd = fd[0]; 94 | i++; 95 | } 96 | } 97 | 98 | while (wait(&status) > 0) 99 | { 100 | if (WIFEXITED(status) && WEXITSTATUS(status) != 0) 101 | ret = 1; 102 | else if (!WIFEXITED(status)) 103 | ret = 1; 104 | } 105 | 106 | return ret; 107 | } 108 | -------------------------------------------------------------------------------- /LVL 2/VBC/vbc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include // MOD: on utilise pour calloc/free au lieu de 3 | #include 4 | 5 | typedef struct node { 6 | enum { 7 | ADD, 8 | MULTI, 9 | VAL 10 | } type; 11 | int val; 12 | struct node *l; 13 | struct node *r; 14 | } node; 15 | 16 | node *new_node(node n) 17 | { 18 | node *ret = calloc(1, sizeof(*ret)); // MODIFIED: sizeof(*ret) instead sizeof(n) 19 | if (!ret) 20 | return NULL; 21 | *ret = n; 22 | return ret; 23 | } 24 | 25 | void destroy_tree(node *n) 26 | { 27 | if (!n) 28 | return; 29 | if (n->type != VAL) { 30 | destroy_tree(n->l); 31 | destroy_tree(n->r); 32 | } 33 | free(n); 34 | } 35 | 36 | void unexpected(char c) 37 | { 38 | if (c) 39 | printf("Unexpected token '%c'\n", c); 40 | else 41 | printf("Unexpected end of input\n"); // MODIFIED: correct message according to the subject 42 | } 43 | 44 | /* MODIFIED: accept/expect remises à la signature d’origine, sans global */ 45 | int accept(char **s, char c) 46 | { 47 | if (**s == c) { 48 | (*s)++; 49 | return 1; 50 | } 51 | return 0; 52 | } 53 | 54 | int expect(char **s, char c) 55 | { 56 | if (accept(s, c)) 57 | return 1; 58 | unexpected(**s); 59 | return 0; 60 | } 61 | 62 | /* ADDED: déclarations des fonctions de parsing récursif */ 63 | static node *parse_expr_r(char **s); 64 | static node *parse_term (char **s); 65 | static node *parse_factor (char **s); 66 | 67 | /* ADDED: parsing d’un facteur (chiffre ou parenthèse) */ 68 | static node *parse_factor(char **s) 69 | { 70 | if (isdigit((unsigned char)**s)) { 71 | node n = { .type = VAL, .val = **s - '0', .l = NULL, .r = NULL }; 72 | (*s)++; 73 | return new_node(n); 74 | } 75 | if (accept(s, '(')) { 76 | node *e = parse_expr_r(s); 77 | if (!e) 78 | return NULL; 79 | if (!expect(s, ')')) { // MOD: vérification de la parenthèse fermante 80 | destroy_tree(e); 81 | return NULL; 82 | } 83 | return e; 84 | } 85 | unexpected(**s); 86 | return NULL; 87 | } 88 | 89 | /* ADDED: parsing d’un terme (multiplications) */ 90 | static node *parse_term(char **s) 91 | { 92 | node *left = parse_factor(s); 93 | if (!left) 94 | return NULL; 95 | while (accept(s, '*')) { 96 | node *right = parse_factor(s); 97 | if (!right) { 98 | destroy_tree(left); 99 | return NULL; 100 | } 101 | node n = { .type = MULTI, .l = left, .r = right }; 102 | left = new_node(n); 103 | if (!left) 104 | return NULL; 105 | } 106 | return left; 107 | } 108 | 109 | /* ADDED: parsing d’une expression (additions) */ 110 | /* THIS FUNCTION IS A COPY PASTE OF PARSE_TERM, YOU JUST HAVE TO REPLACE '*' by '+' !!!!!*/ 111 | static node *parse_expr_r(char **s) 112 | { 113 | node *left = parse_term(s); 114 | if (!left) 115 | return NULL; 116 | while (accept(s, '+')) { 117 | node *right = parse_term(s); 118 | if (!right) { 119 | destroy_tree(left); 120 | return NULL; 121 | } 122 | node n = { .type = ADD, .l = left, .r = right }; 123 | left = new_node(n); 124 | if (!left) 125 | return NULL; 126 | } 127 | return left; 128 | } 129 | 130 | /* MODIFIED: parse_expr initialise un pointeur local et vérifie la fin de la chaîne */ 131 | node *parse_expr(char *s) 132 | { 133 | char *p = s; 134 | node *ret = parse_expr_r(&p); 135 | if (!ret) 136 | return NULL; 137 | if (*p) { 138 | unexpected(*p); 139 | destroy_tree(ret); 140 | return NULL; 141 | } 142 | return ret; 143 | } 144 | 145 | int eval_tree(node *tree) 146 | { 147 | switch (tree->type) { 148 | case ADD: return eval_tree(tree->l) + eval_tree(tree->r); 149 | case MULTI: return eval_tree(tree->l) * eval_tree(tree->r); 150 | case VAL: return tree->val; 151 | } 152 | return 0; // should no happen, but who know's ? 153 | } 154 | 155 | int main(int argc, char **argv) 156 | { 157 | if (argc != 2) 158 | return 1; 159 | node *tree = parse_expr(argv[1]); 160 | if (!tree) 161 | return 1; 162 | printf("%d\n", eval_tree(tree)); 163 | destroy_tree(tree); 164 | return 0; 165 | } 166 | -------------------------------------------------------------------------------- /LVL 2/VBC/Argo/argo.c: -------------------------------------------------------------------------------- 1 | #include "argo.h" 2 | #include // malloc, realloc, free 3 | 4 | /* ---------- PARTIE FOURNIE PAR L’EXAMEN (argo.c initial) ---------- */ 5 | 6 | int peek(FILE *stream) 7 | { 8 | int c = getc(stream); 9 | ungetc(c, stream); 10 | return c; 11 | } 12 | 13 | void unexpected(FILE *stream) 14 | { 15 | if (peek(stream) != EOF) 16 | printf("unexpected token '%c'\n", peek(stream)); 17 | else 18 | printf("unexpected end of input\n"); 19 | } 20 | 21 | int accept(FILE *stream, char c) 22 | { 23 | if (peek(stream) == c) 24 | { 25 | (void)getc(stream); 26 | return 1; 27 | } 28 | return 0; 29 | } 30 | 31 | int expect(FILE *stream, char c) 32 | { 33 | if (accept(stream, c)) 34 | return 1; 35 | unexpected(stream); 36 | return 0; 37 | } 38 | 39 | 40 | /* ---------- PARTIE AJOUTÉE : PARSING JSON (sans free_json) ---------- */ 41 | 42 | /* forward declarations */ 43 | static int parse_value(json *dst, FILE *stream); 44 | static int parse_integer(json *dst, FILE *stream); 45 | static int parse_string(json *dst, FILE *stream); 46 | static int parse_map(json *dst, FILE *stream); 47 | 48 | /* 49 | * argo : point d'entrée de l'analyse. 50 | * Appelle parse_value, puis vérifie qu'on est bien en fin de flux. 51 | * En cas d'erreur intermédiaire, on fait appel à free_json(dst) 52 | * qui est fourni par le main.c. 53 | */ 54 | int argo(json *dst, FILE *stream) 55 | { 56 | if (parse_value(dst, stream) == -1) 57 | return -1; 58 | if (peek(stream) != EOF) 59 | { 60 | unexpected(stream); 61 | free_json(*dst); /* <-- appelle la version de main.c */ 62 | return -1; 63 | } 64 | return 1; 65 | } 66 | 67 | static int parse_value(json *dst, FILE *stream) 68 | { 69 | int c = peek(stream); 70 | 71 | if (c == '"') 72 | return parse_string(dst, stream); 73 | if (c == '{') 74 | return parse_map(dst, stream); 75 | if (c == '-' || isdigit(c)) 76 | return parse_integer(dst, stream); 77 | 78 | unexpected(stream); 79 | return -1; 80 | } 81 | 82 | static int parse_integer(json *dst, FILE *stream) 83 | { 84 | int c; 85 | long val = 0; 86 | int sign = 1; 87 | 88 | if (peek(stream) == '-') 89 | { 90 | sign = -1; 91 | getc(stream); 92 | } 93 | if (!isdigit(peek(stream))) 94 | { 95 | unexpected(stream); 96 | return -1; 97 | } 98 | while (isdigit(peek(stream))) 99 | { 100 | c = getc(stream); 101 | val = val * 10 + (c - '0'); 102 | } 103 | dst->type = INTEGER; 104 | dst->integer = (int)(sign * val); 105 | return 1; 106 | } 107 | 108 | static int parse_string(json *dst, FILE *stream) 109 | { 110 | size_t cap = 16, len = 0; 111 | char *buf; 112 | int c; 113 | 114 | if (!accept(stream, '"')) 115 | return -1; 116 | buf = malloc(cap); 117 | if (!buf) 118 | return -1; 119 | 120 | while ((c = getc(stream)) != EOF && c != '"') 121 | { 122 | if (c == '\\') 123 | { 124 | c = getc(stream); 125 | if (c != '"' && c != '\\') 126 | { 127 | free(buf); 128 | unexpected(stream); 129 | return -1; 130 | } 131 | } 132 | if (len + 1 >= cap) 133 | { 134 | cap *= 2; 135 | buf = realloc(buf, cap); 136 | if (!buf) 137 | return -1; 138 | } 139 | buf[len++] = (char)c; 140 | } 141 | if (c != '"') 142 | { 143 | free(buf); 144 | unexpected(stream); 145 | return -1; 146 | } 147 | buf[len] = '\0'; 148 | 149 | dst->type = STRING; 150 | dst->string = buf; 151 | return 1; 152 | } 153 | 154 | static int parse_map(json *dst, FILE *stream) 155 | { 156 | pair *arr; 157 | size_t cap = 4, count = 0; 158 | json tmp_val, tmp_key; 159 | size_t i; 160 | 161 | if (!accept(stream, '{')) 162 | return -1; 163 | 164 | if (accept(stream, '}')) 165 | { 166 | dst->type = MAP; 167 | dst->map.data = NULL; 168 | dst->map.size = 0; 169 | return 1; 170 | } 171 | 172 | arr = malloc(cap * sizeof(*arr)); 173 | if (!arr) 174 | return -1; 175 | 176 | for (;;) 177 | { 178 | if (parse_string(&tmp_key, stream) == -1) 179 | goto err; 180 | if (!expect(stream, ':')) 181 | { 182 | free(tmp_key.string); 183 | goto err; 184 | } 185 | if (parse_value(&tmp_val, stream) == -1) 186 | { 187 | free(tmp_key.string); 188 | goto err; 189 | } 190 | 191 | if (count == cap) 192 | { 193 | cap *= 2; 194 | arr = realloc(arr, cap * sizeof(*arr)); 195 | if (!arr) 196 | return -1; 197 | } 198 | arr[count].key = tmp_key.string; 199 | arr[count].value = tmp_val; 200 | count++; 201 | 202 | if (accept(stream, ',')) 203 | continue; 204 | break; 205 | } 206 | 207 | if (!expect(stream, '}')) 208 | goto err; 209 | 210 | dst->type = MAP; 211 | dst->map.data = arr; 212 | dst->map.size = count; 213 | return 1; 214 | 215 | err: 216 | for (i = 0; i < count; i++) 217 | { 218 | free(arr[i].key); 219 | free_json(arr[i].value); /* <-- appelle aussi celle de main.c */ 220 | } 221 | free(arr); 222 | return -1; 223 | } 224 | -------------------------------------------------------------------------------- /LVL 1/sandbox.c: -------------------------------------------------------------------------------- 1 | /* 2 | Assignment name : sandbox 3 | Expected files : sandbox.c 4 | Allowed functions : fork, waitpid, exit, alarm, sigaction, kill, 5 | printf, strsignal, errno 6 | =============================================================================== 7 | 8 | Write the following function: 9 | 10 | #include 11 | int sandbox(void (*f)(void), unsigned int timeout, bool verbose) 12 | 13 | This function must test if the function f is a nice function or a bad function, 14 | you will return 1 if f is nice , 0 if f is bad or -1 in case of an error in 15 | your function. 16 | 17 | A function is considered bad if it is terminated or stopped by a signal 18 | (segfault, abort...), if it exit with any other exit code than 0 or if it 19 | times out. 20 | 21 | If verbose is true, you must write the appropriate message among the following: 22 | 23 | "Nice function!\n" 24 | "Bad function: exited with code \n" 25 | "Bad function: \n" 26 | "Bad function: timed out after seconds\n" 27 | 28 | You must not leak processes (even in zombie state, this will be checked using 29 | wait). 30 | 31 | We will test your code with very bad functions. 32 | */ 33 | 34 | // sandbox.c 35 | 36 | #include // fork(), alarm(), _exit() 37 | #include // waitpid(), WIFEXITED, WIFSIGNALED, WEXITSTATUS, WTERMSIG 38 | #include // sigaction, SIGALRM, SIGKILL, struct sigaction 39 | #include // bool 40 | #include // printf() 41 | #include // exit() 42 | #include // errno, EINTR 43 | #include // strsignal() 44 | 45 | // Handler vide pour interrompre waitpid() sur SIGALRM 46 | static void do_nothing(int sig) 47 | { 48 | (void)sig; 49 | } 50 | 51 | int sandbox(void (*f)(void), unsigned int timeout, bool verbose) 52 | { 53 | pid_t pid = fork(); 54 | if (pid < 0) 55 | return -1; 56 | 57 | if (pid == 0) 58 | { 59 | // --- Processus enfant --- 60 | alarm(timeout); 61 | f(); 62 | _exit(0); 63 | } 64 | 65 | // --- Processus parent --- 66 | struct sigaction sa = {0}; 67 | sa.sa_handler = do_nothing; 68 | sigemptyset(&sa.sa_mask); 69 | sa.sa_flags = 0; 70 | if (sigaction(SIGALRM, &sa, NULL) < 0) 71 | return -1; 72 | 73 | alarm(timeout); 74 | 75 | int status; 76 | pid_t r = waitpid(pid, &status, 0); 77 | if (r < 0) 78 | { 79 | if (errno == EINTR) 80 | { 81 | // Timeout : kill et récolte de l'enfant 82 | kill(pid, SIGKILL); 83 | waitpid(pid, NULL, 0); 84 | if (verbose) 85 | printf("Bad function: timed out after %u seconds\n", timeout); 86 | return 0; 87 | } 88 | return -1; 89 | } 90 | 91 | // Annuler l'alarme restante 92 | alarm(0); 93 | 94 | if (WIFEXITED(status)) 95 | { 96 | int code = WEXITSTATUS(status); 97 | if (code == 0) 98 | { 99 | if (verbose) 100 | printf("Nice function!\n"); 101 | return 1; 102 | } 103 | if (verbose) 104 | printf("Bad function: exited with code %d\n", code); 105 | return 0; 106 | } 107 | 108 | if (WIFSIGNALED(status)) 109 | { 110 | int sig = WTERMSIG(status); 111 | if (sig == SIGALRM) 112 | { 113 | if (verbose) 114 | printf("Bad function: timed out after %u seconds\n", timeout); 115 | } 116 | else 117 | { 118 | if (verbose) 119 | printf("Bad function: %s\n", strsignal(sig)); 120 | } 121 | return 0; 122 | } 123 | 124 | // Cas imprévu 125 | return -1; 126 | } 127 | 128 | 129 | /*void ok_f(void) 130 | { 131 | printf("noice. "); 132 | } 133 | 134 | void inf_f(void) 135 | { 136 | while (1) 137 | ; 138 | } 139 | 140 | void bad_exit(void) 141 | { 142 | exit(41); 143 | } 144 | 145 | void suicide_f(void) 146 | { 147 | kill(getpid(), SIGKILL); 148 | sleep(10); 149 | } 150 | 151 | void fast_print_f(void) 152 | { 153 | printf("fast_print_f executed\n"); 154 | } 155 | 156 | void abort_f(void) 157 | { 158 | abort(); 159 | } 160 | 161 | // 7. Fonction qui fait un stop avec SIGSTOP (ne se termine pas) 162 | void stop_f(void) 163 | { 164 | raise(SIGSTOP); 165 | // Le processus sera stoppé et jamais repris => timeout dans sandbox 166 | sleep(10); // Juste pour être sûr si repris 167 | } 168 | 169 | void segfault_f(void) 170 | { 171 | int *p = NULL; 172 | *p = 42; 173 | } 174 | 175 | void cancel_alarm(void) 176 | { 177 | struct sigaction ca; 178 | ca.sa_handler = SIG_IGN; 179 | sigaction(SIGALRM, &ca, NULL); 180 | 181 | sleep(5); 182 | printf("f waited 5 seconds, should be terminated before by alarm set in parent process if timeout < 5\n"); 183 | } 184 | 185 | void leak_f(void) 186 | { 187 | int *p = NULL; 188 | *p = 4; 189 | } 190 | 191 | void test_func(void (*f)(void), unsigned int timeout, const char* name) 192 | { 193 | printf("==== Test : %s (timeout %us) ====\n", name, timeout); 194 | int res = sandbox(f, timeout, true); 195 | printf("Result = %d\n\n", res); 196 | } 197 | 198 | int main(void) 199 | { 200 | test_func(ok_f, 2, "ok_f (nice function, immediate return)"); 201 | 202 | test_func(inf_f, 2, "inf_f (infinite loop, should timeout)"); 203 | 204 | test_func(bad_exit, 2, "bad_exit (exit with code 41)"); 205 | 206 | test_func(cancel_alarm, 2, "cancel_alarm (ignore SIGALRM, sleep 5s)"); 207 | 208 | test_func(segfault_f, 2, "segfault_f (segmentation fault)"); 209 | 210 | test_func(abort_f, 2, "abort_f (abort signal SIGABRT)"); 211 | 212 | test_func(stop_f, 2, "stop_f (stops self with SIGSTOP, never continues)"); 213 | 214 | test_func(suicide_f, 2, "suicide_f (kills self with SIGKILL)"); 215 | 216 | test_func(fast_print_f, 2, "fast_print_f (quick print + return 0)"); 217 | 218 | return 0; 219 | }*/ --------------------------------------------------------------------------------