diff --git a/Makefile b/Makefile index cc98a1e..a269ab9 100644 --- a/Makefile +++ b/Makefile @@ -43,10 +43,10 @@ release: all # debug target valgrind: - @valgrind --leak-check=full --show-leak-kinds=all --trace-children=yes --track-origins=yes $(BIN_DIR)/$(TARGET) ls -la + @sudo valgrind --leak-check=full --show-leak-kinds=all --trace-children=no --track-origins=yes $(BIN_DIR)/$(TARGET) /bin/ls -la /bin run: - $(BIN_DIR)/$(TARGET) ls -la + sudo $(BIN_DIR)/$(TARGET) /bin/ls -la / clean: @rm -f $(OBJ_DIR)/*.o diff --git a/src/container.c b/src/container.c index 0d1c044..a18c0a3 100644 --- a/src/container.c +++ b/src/container.c @@ -1,95 +1,110 @@ +#ifndef _CONTAINER +#define _CONTAINER + #include "prototype.h" #define __USE_GNU #define _GNU_SOURCE + #include #include -#include -#include #include -#include #include -#include -#include -#include +#include +#include #define STACKSIZE (1024 * 1024) static char child_stack[STACKSIZE]; /** - * child_exec is the func that will be executed as the result of clone + * Child_exec is the func that will be executed as the result of clone */ -int child_exec(void *stuff) +int container_create(void *stuff) { + char rootfs_path[] = "./busybox"; container_t *c = (container_t *)stuff; - struct clone_args *cloneArgs = &c->cloneArgs; + struct clone_args *cloneArgs = &(c->cloneArgs); + + if (pivot_root(rootfs_path) != 0) + { + error_handler_container(c, "failed to pivot root"); + } + + // mount dev, sys, proc into pivot root fs + remount(); if (sethostname(cloneArgs->hostname, strlen(cloneArgs->hostname)) != 0) { - container_error_handler(c, "fail to set new hostname"); + error_handler_container(c, "fail to set new hostname"); } - if (execvp(cloneArgs->argv[0], cloneArgs->argv) != 0) + // run container command + if (run_subprocess_async(cloneArgs->argv) != 0) { - container_error_handler(c, "failed to execvp arguments\n"); + error_handler_container(c, "failed to execvp arguments"); } - // we should never reach here! - exit(EXIT_FAILURE); + + free(c); // free twice don't know why but maybe clone lost + c = NULL; + + // we end container + return EXIT_SUCCESS; } /** - * container constructor initialize container_t object + * Container constructor initialize container_t object */ container_t *initialize_container(struct clone_args cloneArgs) { container_t *c = (container_t *)malloc(sizeof(container_t)); // in order new net ns, new mount ns, new hostname ns, new pid ns, child ns - c->_cloneFlags = CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | SIGCHLD; + c->_cloneFlags = CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWCGROUP; c->cloneArgs = cloneArgs; return c; } /** - * launch command into container + * Launch command into container */ -pid_t run(container_t *c) +pid_t run_container(container_t *c) { - // the result of this call is that our child_exec will be run in another + // the result of this call is that our child_exec will be run_container in another // process returning it's pid - pid_t pid = clone(child_exec, child_stack + STACKSIZE, c->_cloneFlags, c); + pid_t pid = clone(container_create, child_stack + STACKSIZE, c->_cloneFlags | SIGCHLD, c); if (pid < 0) { - container_error_handler(c, "failed to run clone"); + error_handler_container(c, "failed to run_container clone"); } // lets wait on our child process here before we, the parent, exits if (waitpid(pid, NULL, 0) == -1) { - container_error_handler(c, "failed to wait pid %d", pid); - exit(EXIT_FAILURE); + error_handler_container(c, "failed to wait pid %d", pid); } + return pid; } /** - * handle error occured during container creation + * Handle error occured during container creation * this methode permit to call container_destroy * before printing error * take at last parameter variadic argument */ -void container_error_handler(container_t *c, const char *error_msg, ...) +void error_handler_container(container_t *c, const char *error_msg, ...) { va_list arg; va_start(arg, error_msg); destroy_container(c); - error(error_msg, arg); - va_end(arg); + last_error(error_msg, arg); } /** - * container destructor + * Container destructor */ void destroy_container(container_t *c) { free(c); } + +#endif diff --git a/src/data.c b/src/data.c new file mode 100644 index 0000000..ed77411 --- /dev/null +++ b/src/data.c @@ -0,0 +1,71 @@ +/** + * This file group all function which interact with data structure + */ + +#ifndef _DATA +#define _DATA + +#include "prototype.h" + +#include + +/** + * Take array and lengh and fill it with character '\0' + * Returned array should be free + */ +char *new_array(size_t size) +{ + char *arr = (char *)malloc(size * sizeof(char) + 1); + if (arr == NULL) + { + error("fatal : failed to alloc array"); + } + // fill allocated array with '\0' + init_array(arr, size); + return arr; +} + +void init_array(char *arr, size_t size) +{ + for (size_t i = 0; i < size; i++) + { + arr[i] = '\0'; + } +} + +/** + * Read file descriptor without already defined size + * Returned array should be free + */ +char *read_infinite_fd(int fd) +{ + int batch_size = 1024; + char *stdout_buffer = new_array(batch_size); + + // copy read pipefd to buffer + int already_read = read(fd, stdout_buffer, batch_size); + int current_read = 0; + do + { + already_read += current_read; + char *stdout_buffer_temp = (char *)realloc(stdout_buffer, already_read + batch_size * sizeof(char)); + // if realloc faile davoid memory leak free primary pointer + if (stdout_buffer_temp == NULL) + { + free(stdout_buffer); + error("fatal : failed to realloc during pipe child reading"); + } + else + { + stdout_buffer = stdout_buffer_temp; + // fill new allocated part with '\0' + init_array(stdout_buffer + already_read, batch_size); + } + + current_read = 0; + } while ((current_read = read(fd, stdout_buffer + already_read, batch_size)) != 0); + + return stdout_buffer; +} + +#endif diff --git a/src/errorUtils.c b/src/error.c similarity index 79% rename from src/errorUtils.c rename to src/error.c index 14115af..88dc755 100644 --- a/src/errorUtils.c +++ b/src/error.c @@ -13,29 +13,43 @@ #define BREAKLINE "\n" /** - * print error message and exit program with error + * print error message */ void error(const char *sys_msg, va_list arg) { - FILE *fd = fopen("/dev/stderr", "w"); char *errorTxt = strerror(errno); + char errorNum[5]; + sprintf(errorNum, " (%d)", errno); + + FILE *fd = fopen("/dev/stderr", "w"); // + 1 is due to strcpy on sys_msg which add \0 at the end of the string char *concatenated_msg = malloc( (sizeof(*(errorTxt)) * strlen(errorTxt)) + (sizeof(*(sys_msg)) * strlen(sys_msg) + 1) + strlen(SEPARATOR) + + strlen(errorNum) + strlen(BREAKLINE)); strcpy(concatenated_msg, sys_msg); strcat(concatenated_msg, SEPARATOR); strcat(concatenated_msg, errorTxt); + strcat(concatenated_msg, errorNum); strcat(concatenated_msg, BREAKLINE); vfprintf(fd, concatenated_msg, arg); fclose(fd); free(concatenated_msg); + va_end(arg); +} + +/** + * print error message and exit program with error + */ +void last_error(const char *sys_msg, va_list arg) +{ + error(sys_msg, arg); exit(EXIT_FAILURE); } diff --git a/src/main.c b/src/main.c index 4a7d122..491165d 100644 --- a/src/main.c +++ b/src/main.c @@ -2,7 +2,7 @@ #include "prototype.h" /** - * Main program, run container + * Main program, run_container container */ int main(int argc, char *const argv[]) { @@ -16,8 +16,7 @@ int main(int argc, char *const argv[]) cloneArgs.hostname = "container-hostname"; container_t *c = initialize_container(cloneArgs); - run(c); - + run_container(c); destroy_container(c); exit(EXIT_SUCCESS); diff --git a/src/prototype.h b/src/prototype.h index 74e5913..be1a0c3 100644 --- a/src/prototype.h +++ b/src/prototype.h @@ -8,12 +8,26 @@ #include "./type.h" // public functions - -void error(const char *sys_msg, ...); - +// container.c container_t *initialize_container(struct clone_args); -pid_t run(container_t *c); -void container_error_handler(container_t *c, const char *error_msg, ...); +pid_t run_container(container_t *c); +void error_handler_container(container_t *c, const char *error_msg, ...); void destroy_container(container_t *c); +// error.c +void error(const char *sys_msg, ...); +void last_error(const char *sys_msg, ...); + +// system.c +int run_subprocess_async(char *const *argv); +int run_subprocess(char *const *argv); +int pivot_root(char *mount_dir); +int clean_pivot_root(char *a); +void remount(); + +// data.c +char *new_array(size_t size); +void init_array(char *arr, size_t size); +char *read_infinite_fd(int fd); + #endif diff --git a/src/system.c b/src/system.c new file mode 100644 index 0000000..d095f0b --- /dev/null +++ b/src/system.c @@ -0,0 +1,164 @@ +/** + * This file group all function which interact with system operation + */ + +#ifndef _SYSTEM +#define _SYSTEM + +#include "prototype.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define OLDROOT "/.old" + +/** + * Wrapper for pivot root syscall + */ +int pivot_root(char *mount_dir) +{ + char *inner_mount_dir = malloc( + (sizeof(*(mount_dir)) * strlen(mount_dir) + 1) + + strlen(OLDROOT)); + strcpy(inner_mount_dir, mount_dir); + strcat(inner_mount_dir, OLDROOT); + + if (mount("/", "/", "bind", MS_REC | MS_PRIVATE, NULL)) + { + error("error remount / with MS_PRIVATE"); + } + + if (mount(mount_dir, mount_dir, "bind", MS_BIND | MS_REC, NULL) < 0) + { + error("error mount"); + } + if (mkdir(inner_mount_dir, 0755) != 0) + { + if (errno != EEXIST) + { + error("error mkdir"); + } + } + + int syscall_result = syscall(SYS_pivot_root, mount_dir, inner_mount_dir); + + // deal with old mount directory + if (umount2(OLDROOT, MNT_DETACH) != 0) + { + error("umount old directory failed"); + } + if (rmdir(inner_mount_dir) != 0) + { + error("rm old directory failed"); + } + free(inner_mount_dir); + return syscall_result; +} + +/** + * Umount pivot root folder + */ +int clean_pivot_root(char *a) +{ + if (umount2(a, MNT_DETACH) != 0) + { + error("umount pivot_root directory failed"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +/** + * Mount tmpfs into /dev and remount proc into /proc + */ +void remount() +{ + // remount devtmpfs device to /dev with type devtmpfs + if (mount(NULL, "/dev", "devtmpfs", MS_NOSUID | MS_STRICTATIME, NULL)) + { + error("failed mount tmpfs to /dev"); + } + + // remount proc device to /proc with type proc + if (mount(NULL, "/proc", "proc", MS_MGC_VAL, NULL)) + { + error("failed to re mount proc into /proc"); + } + + // remount sys device to /sys with type sysfs + if (mount(NULL, "/sys", "sysfs", MS_MGC_VAL, NULL)) + { + error("failed to re mount proc into /proc"); + } +} + +/** + * Run external binary + */ +int run_subprocess(char *const *argv) +{ + errno = execvp(argv[0], argv); + return errno; +} + +/** + * Run external binary into new sub process + */ +int run_subprocess_async(char *const *argv) +{ + pid_t pid; + + // pipe to permit child => parent comunication + int pipefd[2]; + pipe(pipefd); + + if ((pid = fork()) == -1) + { + error("fork() subprocess error"); + } + else if (pid == 0) + { + close(pipefd[0]); // close reading end in the child + + printf("[CHILD] : running\n"); + dup2(pipefd[1], 1); // send stdout to the pipe + dup2(pipefd[1], 2); // send stderr to the pipe + + close(pipefd[1]); // this descriptor is no longer needed + + errno = execvp(argv[0], argv); + return errno; + } + else + { + int child_status; + + close(pipefd[1]); // close the write end of the pipe in the parent + + waitpid(pid, &child_status, 0); + if (WIFEXITED(child_status)) + { + char *stdout_buffer = read_infinite_fd(pipefd[0]); + printf("[PARENT] child stdout : \n%s\n", stdout_buffer); + free(stdout_buffer); + + close(pipefd[0]); // close the descriptor is no longer needed + + int es = WEXITSTATUS(child_status); + printf("[PARENT] Exit status was %d\n", es); + + return es; + } + } + // reach when wait child failed + return EXIT_FAILURE; +} + +#endif