fix pivot root, add remount for dev, sys and proc
This commit is contained in:
parent
58431adc32
commit
8ab9a46d96
4
Makefile
4
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
|
||||
|
@ -1,95 +1,110 @@
|
||||
#ifndef _CONTAINER
|
||||
#define _CONTAINER
|
||||
|
||||
#include "prototype.h"
|
||||
|
||||
#define __USE_GNU
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <sched.h>
|
||||
#include <stdarg.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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
|
||||
|
71
src/data.c
Normal file
71
src/data.c
Normal file
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* This file group all function which interact with data structure
|
||||
*/
|
||||
|
||||
#ifndef _DATA
|
||||
#define _DATA
|
||||
|
||||
#include "prototype.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* Take array and lengh and fill it with character '\0'
|
||||
* <b>Returned array should be</b> 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
|
||||
* <b>Returned array should be</b> 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
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
164
src/system.c
Normal file
164
src/system.c
Normal file
@ -0,0 +1,164 @@
|
||||
/**
|
||||
* This file group all function which interact with system operation
|
||||
*/
|
||||
|
||||
#ifndef _SYSTEM
|
||||
#define _SYSTEM
|
||||
|
||||
#include "prototype.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <string.h>
|
||||
|
||||
#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
|
Loading…
Reference in New Issue
Block a user