Compare commits

...

16 Commits

Author SHA1 Message Date
Albert S. ea66ef76eb exile_flags_to_landlock(): Cover more with ALL_WRITE, except devices
More consistent with mount(), where MS_NODEV disallows those.

We may need to introduce a flag that simply allows everything
2022-03-17 15:47:22 +01:00
Albert S. 66def7a28f append_syscall_to_bpf(): Check for unlikely case of too many sock_filters 2022-03-17 15:47:22 +01:00
Albert S. dbf8e87440 exile.hpp: Mark do_clone inline, not static 2022-03-17 15:47:22 +01:00
Albert S. 98421fab90 Makefile: Build exile.o separately, link it in all tests 2022-03-17 15:47:22 +01:00
Albert S. 70c3fef500 exile.h: Retire static child_read/write_pipe vars 2022-03-17 15:47:22 +01:00
Albert S. 69829374c7 exile.h: Move definitions to new file exile.c
Especially with exile_launch(), we will be included
from more than one translation unit. Thus, ODR becomes
a headache now.

So move definitions to exile.c.
2022-03-17 15:47:22 +01:00
Albert S. 005851c645 exile.h: Add extern "C" guards 2022-03-17 15:47:22 +01:00
Albert S. 95fa11e928 c++: Add explicit exile_launch() std::basic_string variant 2022-03-17 15:47:22 +01:00
Albert S. 97e2025758 c++: Retire exile_launch_trivial(), use std::enable_if 2022-03-17 15:47:22 +01:00
Albert S. 8cfb73568a Makefile: Add 'tests' target, depend on headers too to rebuild on changes of those 2022-03-17 15:47:22 +01:00
Albert S. e7a5ba7f7f test.sh: Also run C++ tests 2022-03-17 15:47:22 +01:00
Albert S. e52eda186b Add test.cpp to test C++ API 2022-03-17 15:47:22 +01:00
Albert S. 90ed5bbae9 Begin C++ API: Add exile.hpp with exile_launch() wrappers 2022-03-17 15:47:22 +01:00
Albert S. 48b6de9036 struct syscall_vow_map: change 'str' to const char* 2022-03-17 15:47:22 +01:00
Albert S. 93acb13929 test: Introduce LOG(), avoid inconsistent printf/fprintf 2022-03-17 15:47:22 +01:00
Albert S. 9247a6636b Introduce exile_vows_from_str() 2022-03-17 15:47:22 +01:00
7 changed files with 2280 additions and 1719 deletions

View File

@ -1,17 +1,27 @@
prefix = /usr/local
bindir = $(prefix)/bin
CFLAGS = -std=c99 -Wall -Wextra -pedantic
CXXFLAGS = -std=c++20 -Wall -Wextra -pedantic
.DEFAULT_GOAL := test
.DEFAULT_GOAL := tests
clean:
rm -f test
rm -f test exile.o testcpp
test: test.c
$(CC) test.c -g $(CFLAGS) -o test
check: test
exile.o: exile.c exile.h
$(CC) -c exile.c -g $(CFLAGS) -o exile.o
test: test.c exile.h exile.o
$(CC) test.c exile.o -g $(CFLAGS) -o test
testcpp: test.cpp exile.h exile.hpp exile.o
$(CXX) test.cpp exile.o -g $(CXXFLAGS) -o testcpp
tests: test testcpp
check: tests
./test.sh
.PHONY: check

1857
exile.c Normal file

File diff suppressed because it is too large Load Diff

1708
exile.h

File diff suppressed because it is too large Load Diff

199
exile.hpp Normal file
View File

@ -0,0 +1,199 @@
#include "exile.h"
#include <functional>
#include <iostream>
#include <string>
#include <tuple>
#include <memory>
#include <sys/wait.h>
#ifndef EXILE_MMAP_SIZE
#define EXILE_MMAP_SIZE 128 * 1024 * 1024 //128MB
#endif
template<typename T, typename U, typename ... Args>
class launch_arg
{
static_assert(std::is_trivially_copyable_v<T>);
static_assert(!std::is_pointer_v<T>);
public:
struct exile_policy *policy;
T *result_shm;
U fn;
std::tuple<Args...> args;
launch_arg(struct exile_policy *policy, T *result_shm, U fn, Args && ... args) : policy(policy),
result_shm(result_shm), fn(fn), args(std::forward<Args>(args)...) {}
};
template<typename T, typename U, typename ... Args>
class launch_arg_serializer
{
static_assert(std::is_copy_constructible_v<T>);
public:
struct exile_policy *policy;
char *serialize_buffer;
size_t n;
U fn;
std::tuple<Args...> args;
const std::function<size_t (const T &, char *, size_t n)> &serializer;
const std::function<T(const char * buf, size_t n)> &deserializer;
launch_arg_serializer(struct exile_policy *policy, char *serialize_buffer, size_t n, const std::function<size_t (const T &, char *, size_t)> &serializer, const std::function<T(const char *, size_t)> &deserializer, U fn, Args && ... args) : policy(policy), serialize_buffer(serialize_buffer), n(n), fn(fn), args(std::forward<Args>(args)...), serializer(serializer), deserializer(deserializer) {}
};
template<typename T, typename U, typename ... Args>
int exile_clone_handle_trivial(void * arg)
{
static_assert(std::is_trivially_copyable_v<T>);
static_assert(!std::is_pointer_v<T>);
launch_arg<T, U, Args...> *launchargs = (launch_arg<T, U, Args...> *) arg;
int ret = exile_enable_policy(launchargs->policy);
if(ret != 0)
{
EXILE_LOG_ERROR("exile_enable_policy() failed: %s\n", strerror(errno));
return 1;
}
T result = std::apply(launchargs->fn, launchargs->args);
std::cout << result;
memcpy(launchargs->result_shm, &result, sizeof(T));
return 0;
}
template<typename T, typename U, typename ... Args>
int exile_clone_handle_serializer(void * arg)
{
static_assert(std::is_copy_constructible_v<T>);
launch_arg_serializer<T, U, Args...> *launchargs = (launch_arg_serializer<T, U, Args...> *) arg;
int ret = exile_enable_policy(launchargs->policy);
if(ret != 0)
{
EXILE_LOG_ERROR("exile_enable_policy() failed: %s\n", strerror(errno));
return 1;
}
T result = std::apply(launchargs->fn, launchargs->args);
/* TODO: exception handling */
/* TODO: ugly :S */
char *target = launchargs->serialize_buffer + sizeof(size_t);
size_t n = launchargs->n - sizeof(size_t);
size_t size = launchargs->serializer(result, target, n);
memcpy(launchargs->serialize_buffer, &size, sizeof(size_t));
return 0;
}
inline int do_clone(int (*clonefn)(void *), void *launcharg)
{
struct rlimit rlimit;
int ret = getrlimit(RLIMIT_STACK, &rlimit);
if(ret != 0)
{
EXILE_LOG_ERROR("Failed to get stack size: %s\n", strerror(errno));
return ret;
}
size_t size = rlimit.rlim_cur;
char *stack = (char *) calloc(1, size);
if(stack == NULL)
{
EXILE_LOG_ERROR("Failed to allocate stack memory for child\n");
return 1;
}
stack += size;
ret = clone(clonefn, stack, 17 /* SIGCHLD */, launcharg);
int status = 0;
waitpid(ret, &status, __WALL);
if(WIFEXITED(status))
{
return WEXITSTATUS(status);
}
/* TODO: exception or what? */
return 23;
}
template<typename T, typename U, typename ... Args>
typename std::enable_if_t<std::is_trivially_copyable_v<T>, T> exile_launch(struct exile_policy *policy, U fn, Args && ... args)
{
size_t mapsize = sizeof(T);
T * sharedbuf = (T *) mmap(NULL, mapsize , PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
if(sharedbuf == NULL)
{
throw std::runtime_error(std::string("mmap failed: ") + strerror(errno));
}
std::shared_ptr<void> deleter(nullptr, [sharedbuf, mapsize](...){ munmap(sharedbuf, mapsize); });
launch_arg<T, U, Args...> launcharg(policy, sharedbuf, fn, std::forward<Args>(args)...);
int (*clonefn)(void *) = &exile_clone_handle_trivial<T, U, Args...>;
/* TODO: exception or what? */
int ret = do_clone(clonefn, &launcharg);
if(ret == 0)
{
return *sharedbuf;
}
throw std::runtime_error(std::string("clone() failed: " + std::to_string(ret)));
return T();
}
template<typename T, typename U, typename ... Args>
typename std::enable_if_t<!std::is_trivially_copyable_v<T> && std::is_copy_constructible_v<T>, T>
exile_launch(struct exile_policy *policy, const std::function<size_t (const T &, char *, size_t)> &serializer, const std::function<T(const char *, size_t)> &deserializer, U fn, Args && ... args)
{
size_t mapsize = EXILE_MMAP_SIZE;
char *sharedbuf = (char *) mmap(NULL, mapsize , PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
if(sharedbuf == NULL)
{
throw std::runtime_error(std::string("mmap failed: ") + strerror(errno));
}
std::shared_ptr<void> deleter(nullptr, [sharedbuf, mapsize](...){ munmap(sharedbuf, mapsize); });
launch_arg_serializer<T, U, Args...> launcharg(policy, sharedbuf, mapsize, serializer, deserializer, fn, std::forward<Args>(args)...);
int (*clonefn)(void *) = &exile_clone_handle_serializer<T, U, Args...>;
/* TODO: exception or what? */
int ret = do_clone(clonefn, &launcharg);
if(ret == 0)
{
size_t size = 0;
memcpy(&size, sharedbuf, sizeof(size));
return deserializer(sharedbuf + sizeof(size_t), size);
}
throw std::runtime_error(std::string("clone() failed: " + std::to_string(ret)));
return T();
}
template<class T>
std::basic_string<typename T::value_type> deserialize_stdstring(const char *buf, size_t n)
{
return std::basic_string<typename T::value_type> { buf, n };
}
template<class T>
size_t serialize_stdstring(const std::basic_string<typename T::value_type> &t, char *buf, size_t n)
{
if(n < t.size())
{
return 0;
}
memcpy(buf, t.data(), t.size());
return t.size();
}
template<typename T, typename U, typename ... Args>
std::basic_string<typename T::value_type> exile_launch(struct exile_policy *policy, U fn, Args && ... args)
{
return exile_launch<T, U, Args...>(policy, &serialize_stdstring<T>, &deserialize_stdstring<T>, fn, std::forward<Args>(args) ...);
}

105
test.c
View File

@ -6,12 +6,14 @@
#include <sys/socket.h>
#include <sys/wait.h>
#define LOG(...) do { fprintf(stdout, "%s(): ", __func__); fprintf(stdout, __VA_ARGS__); } while(0)
int xexile_enable_policy(struct exile_policy *policy)
{
int ret = exile_enable_policy(policy);
if(ret != 0)
{
fprintf(stderr, "exile_enable_policy() failed: %i\n", ret);
LOG("failed: %i\n", ret);
exit(EXIT_FAILURE);
}
return 0;
@ -38,16 +40,16 @@ static int test_expected_kill(int (*f)())
int c = WTERMSIG(status);
if(c == SIGSYS)
{
printf("Got expected signal\n");
LOG("Got expected signal\n");
return 0;
}
printf("Unexpected status code: %i\n", c);
LOG("Unexpected status code: %i\n", c);
return 1;
}
else
{
int c = WEXITSTATUS(status);
printf("Process was not killed, test fails. Status code of exit: %i\n", c);
LOG("Process was not killed, test fails. Status code of exit: %i\n", c);
return 1;
}
return 0;
@ -67,7 +69,7 @@ static int test_successful_exit(int (*f)())
if(WIFSIGNALED(status))
{
int c = WTERMSIG(status);
printf("Received signal, which was not expected. Signal was: %i\n", c);
LOG("Received signal, which was not expected. Signal was: %i\n", c);
return 1;
}
else
@ -75,11 +77,11 @@ static int test_successful_exit(int (*f)())
int c = WEXITSTATUS(status);
if(c != 0)
{
printf("Process failed to exit properly. Status code is: %i\n", c);
LOG("Process failed to exit properly. Status code is: %i\n", c);
}
return c;
}
printf("Process exited sucessfully as expected");
LOG("Process exited sucessfully as expected");
return 0;
}
@ -153,7 +155,7 @@ int test_seccomp_require_last_matchall()
int status = exile_enable_policy(policy);
if(status == 0)
{
printf("Failed. Should not have been enabled!");
LOG("Failed. Should not have been enabled!");
return 1;
}
return 0;
@ -170,7 +172,7 @@ static int do_test_seccomp_errno()
uid_t id = syscall(EXILE_SYS(getuid));
int fd = syscall(EXILE_SYS(close), 0);
printf("close() return code: %i, errno: %s\n", fd, strerror(errno));
LOG("close() return code: %i, errno: %s\n", fd, strerror(errno));
return fd == -1 ? 0 : 1;
}
@ -254,14 +256,14 @@ int test_seccomp_argfilter_mixed()
int s = (int) syscall(EXILE_SYS(stat), "/dev/urandom", &statbuf);
if(s != -1)
{
printf("Failed: stat was expected to fail, but returned %i\n", s);
LOG("Failed: stat was expected to fail, but returned %i\n", s);
return 1;
}
pid_t p = (pid_t) syscall(EXILE_SYS(getpid));
if(p != -1)
{
printf("Failed: getpid was expected to fail, but returned %i\n", p);
LOG("Failed: getpid was expected to fail, but returned %i\n", p);
return 1;
}
@ -269,13 +271,13 @@ int test_seccomp_argfilter_mixed()
int ret = (int) syscall(EXILE_SYS(open),t, O_WRONLY);
if(ret != -1)
{
printf("Failed: open was expected to fail, but returned %i\n", ret);
LOG("Failed: open was expected to fail, but returned %i\n", ret);
return 1;
}
ret = (int) syscall(EXILE_SYS(open), t, O_RDONLY);
if(ret == -1)
{
printf("Failed: open with O_RDONLY was expected to succeed, but returned %i\n", ret);
LOG("Failed: open with O_RDONLY was expected to succeed, but returned %i\n", ret);
return 1;
}
return 0;
@ -291,13 +293,13 @@ int do_test_seccomp_vow_socket()
int s = socket(AF_INET, SOCK_STREAM, 0);
if(s == -1)
{
printf("Failed: socket was expected to succeed, but returned %i\n", s);
LOG("Failed: socket was expected to succeed, but returned %i\n", s);
return 1;
}
s = socket(AF_UNIX, SOCK_DGRAM, 0);
if(s != -1)
{
printf("Failed: socket was expected to fail, but returned %i\n", s);
LOG("Failed: socket was expected to fail, but returned %i\n", s);
return 1;
}
return 0;
@ -312,19 +314,19 @@ int do_test_seccomp_vow_open()
int ret = open("/dev/urandom", O_WRONLY | O_APPEND);
if(ret != -1)
{
printf("Failed: open was expected to fail, but returned %i\n", ret);
LOG("Failed: open was expected to fail, but returned %i\n", ret);
return 1;
}
ret = open("/dev/urandom", O_RDWR);
if(ret != -1)
{
printf("Failed: open O_RDWR was expected to fail, but returned %i\n", ret);
LOG("Failed: open O_RDWR was expected to fail, but returned %i\n", ret);
return 1;
}
ret = open("/dev/urandom", O_RDONLY);
if(ret == -1)
{
printf("Failed: open was expected to succceed, but returned %i\n", ret);
LOG("Failed: open was expected to succceed, but returned %i\n", ret);
return 1;
}
return 0;
@ -335,13 +337,13 @@ int test_seccomp_vow()
int ret = test_successful_exit(&do_test_seccomp_vow_open);
if(ret != 0)
{
printf("Failed: do_test_seccomp_vow_open()\n");
LOG("Failed: do_test_seccomp_vow_open()\n");
return 1;
}
ret = test_successful_exit(&do_test_seccomp_vow_socket);
if(ret != 0)
{
printf("Failed: do_test_seccomp_vow_socket()\n");
LOG("Failed: do_test_seccomp_vow_socket()\n");
return 1;
}
return 0;
@ -353,13 +355,13 @@ int test_seccomp_exile_vow_multiple()
int ret = exile_vow(EXILE_SYSCALL_VOW_STDIO | EXILE_SYSCALL_VOW_UNIX | EXILE_SYSCALL_VOW_SECCOMP_INSTALL | EXILE_SYSCALL_VOW_DENY_ERROR);
if(ret != 0)
{
printf("Failed: exile_vow() call 1 failed\n");
LOG("Failed: exile_vow() call 1 failed\n");
return 1;
}
int s = socket(AF_UNIX, SOCK_STREAM, 0);
if(s == -1)
{
printf("Failed: socket was expected to succeed, but returned %i\n", s);
LOG("Failed: socket was expected to succeed, but returned %i\n", s);
return 1;
}
@ -367,13 +369,13 @@ int test_seccomp_exile_vow_multiple()
ret = exile_vow(EXILE_SYSCALL_VOW_STDIO | EXILE_SYSCALL_VOW_SECCOMP_INSTALL | EXILE_SYSCALL_VOW_DENY_ERROR);
if(ret != 0)
{
printf("Failed: exile_vow() call 2 failed\n");
LOG("Failed: exile_vow() call 2 failed\n");
return 1;
}
s = socket(AF_UNIX, SOCK_STREAM, 0);
if(s != -1)
{
printf("Failed: socket was expected to fail, but returned %i\n", s);
LOG("Failed: socket was expected to fail, but returned %i\n", s);
return 1;
}
@ -381,13 +383,13 @@ int test_seccomp_exile_vow_multiple()
ret = exile_vow(EXILE_SYSCALL_VOW_STDIO | EXILE_SYSCALL_VOW_UNIX | EXILE_SYSCALL_VOW_SECCOMP_INSTALL | EXILE_SYSCALL_VOW_DENY_ERROR);
if(ret != 0)
{
printf("Failed: exile_vow() call 3 failed\n");
LOG("Failed: exile_vow() call 3 failed\n");
return 1;
}
s = socket(AF_UNIX, SOCK_STREAM, 0);
if(s != -1)
{
printf("Failed: socket was still expected to fail, but returned %i\n", s);
LOG("Failed: socket was still expected to fail, but returned %i\n", s);
return 1;
}
@ -400,7 +402,7 @@ int test_landlock()
{
if(!exile_landlock_is_available())
{
printf("landlock not available, so cannot test\n");
LOG("landlock not available, so cannot test\n");
return 1;
}
struct exile_policy *policy = exile_init_policy();
@ -449,14 +451,14 @@ int test_nofs()
int s = socket(AF_INET,SOCK_STREAM,0);
if(s == -1)
{
fprintf(stderr, "Failed to open socket but this was not requested by policy\n");
LOG("Failed to open socket but this was not requested by policy\n");
return 1;
}
/* Expect seccomp to take care of this */
if(open("/test", O_CREAT | O_WRONLY) >= 0)
{
fprintf(stderr, "Failed: We do not expect write access\n");
LOG("Failed: We do not expect write access\n");
return 1;
}
@ -472,14 +474,14 @@ int test_no_new_fds()
if(open("/tmp/test", O_CREAT | O_WRONLY) >= 0)
{
fprintf(stderr, "Failed: Could open new file descriptor\n");
LOG("Failed: Could open new file descriptor\n");
return -1;
}
int s = socket(AF_INET,SOCK_STREAM,0);
if(s >= 0)
{
fprintf(stderr, "Failed: socket got opened but policy denied\n");
LOG("Failed: socket got opened but policy denied\n");
return -1;
}
@ -487,6 +489,7 @@ int test_no_new_fds()
}
extern int mkpath(const char *p, mode_t mode, int baseisfile);
int test_mkpath()
{
system("rm -rf /tmp/.exile.h/");
@ -495,13 +498,13 @@ int test_mkpath()
int ret = mkpath(filepath, 0700, 1);
if(ret != 0)
{
fprintf(stderr, "Failed: mkpath(file) returned: %i\n", ret);
LOG("Failed: mkpath(file) returned: %i\n", ret);
return 1;
}
ret = mkpath(dirpath, 0700, 0);
if(ret != 0)
{
fprintf(stderr, "Failed: mkpath(dirpath) returned: %i\n", ret);
LOG("Failed: mkpath(dirpath) returned: %i\n", ret);
return 1;
}
@ -509,23 +512,23 @@ int test_mkpath()
ret = stat(filepath, &statbuf);
if(ret != 0)
{
fprintf(stderr, "Failed: stat on filepath returned: %i\n", ret);
LOG("Failed: stat on filepath returned: %i\n", ret);
return 1;
}
if(!S_ISREG(statbuf.st_mode))
{
fprintf(stderr, "Failed: mkpath did not create a file: %i\n", ret);
LOG("Failed: mkpath did not create a file: %i\n", ret);
return 1;
}
ret = stat(dirpath, &statbuf);
if(ret != 0)
{
fprintf(stderr, "Failed: stat on dirpath returned: %i\n", ret);
LOG("Failed: stat on dirpath returned: %i\n", ret);
return 1;
}
if(!S_ISDIR(statbuf.st_mode))
{
fprintf(stderr, "Failed: mkpath did not create a directory: %i\n", ret);
LOG("Failed: mkpath did not create a directory: %i\n", ret);
return 1;
}
system("rm -rf /tmp/.exile.h/");
@ -545,12 +548,14 @@ int test_fail_flags()
return 0;
}
static int *read_pipe = NULL;
int do_launch_test(void *arg)
{
int num = *(int *)(arg);
num += 1;
char buffer[512] = { 0 };
read(child_write_pipe[0], buffer, sizeof(buffer)-1);
read(*read_pipe, buffer, sizeof(buffer)-1);
printf("Sandboxed +1: %i\n", num);
printf("Echoing: %s\n", buffer);
fflush(stdout);
@ -566,10 +571,11 @@ int test_launch()
params.func = &do_launch_test;
params.funcarg = &num;
params.policy = policy;
read_pipe = &params.child_write_pipe[0];
int launchfd = exile_launch(&params, &res);
if(launchfd < 0)
{
printf("Failed to launch\n");
LOG("Failed to launch\n");
return 1;
}
@ -577,11 +583,11 @@ int test_launch()
write(res.write_fd, "1234", 4);
int s = read(res.read_fd, buffer, sizeof(buffer)-1);
write(1, buffer, s);
printf("Before wait, got: %i\n", s);
LOG("Before wait, got: %i\n", s);
fflush(stdout);
if(strstr(buffer, "Echoing: 1234") == NULL)
{
printf("Failed: Did not get back what we wrote\n");
LOG("Failed: Did not get back what we wrote\n");
}
int status = 0;
waitpid(res.tid, &status, __WALL);
@ -614,12 +620,24 @@ int test_launch_get()
unsigned int len = strlen(LAUNCH_GET_TEST_STR);
if(n != strlen(LAUNCH_GET_TEST_STR))
{
printf("Lenght does does not match: %lu vs %u\n", n, len);
LOG("Lenght does does not match: %lu vs %u\n", n, len);
return 1;
}
if(strcmp(content, LAUNCH_GET_TEST_STR) != 0)
{
printf("Received content differs\n");
LOG("Received content differs\n");
return 1;
}
return 0;
}
int test_vows_from_str()
{
uint64_t expected = EXILE_SYSCALL_VOW_CHOWN | EXILE_SYSCALL_VOW_WPATH | EXILE_SYSCALL_VOW_INET | EXILE_SYSCALL_VOW_DENY_ERROR;
uint64_t actual = exile_vows_from_str("chown wpath inet error");
if(expected != actual)
{
LOG("Masks don't match: %lu vs %lu\n", expected, actual);
return 1;
}
return 0;
@ -651,6 +669,7 @@ struct dispatcher dispatchers[] = {
{ "failflags", &test_fail_flags},
{ "launch", &test_launch},
{ "launch-get", &test_launch_get},
{ "vow_from_str", &test_vows_from_str},
};
int main(int argc, char *argv[])

92
test.cpp Normal file
View File

@ -0,0 +1,92 @@
#include "exile.hpp"
#include "assert.h"
#include <map>
std::string sandboxed_reverse(std::string str)
{
std::reverse(str.begin(), str.end());
return str;
}
size_t stdstrlen(const std::string &str)
{
return str.size();
}
int incrementer(int arg)
{
return ++arg;
}
int test_exile_launch_trivial()
{
int u = 22;
int result = exile_launch<int>(exile_init_policy(), &incrementer, u);
assert(result == 23);
return 0;
}
int test_exile_launch_stdstring()
{
std::string str = "abc123";
std::string reversed = exile_launch<std::string>(exile_init_policy(), &sandboxed_reverse, str);
assert(reversed == "321cba");
return 0;
}
struct not_trivially_copyable
{
public:
std::string somecontent;
};
int test_exile_launch_serializer()
{
static_assert(! std::is_trivially_copyable_v<not_trivially_copyable>);
auto serializer = [](const not_trivially_copyable &obj, char *buf, size_t n){
serialize_stdstring<std::string>(obj.somecontent, buf, n);
return obj.somecontent.size();
};
auto deserializer = [](const char *buffer, size_t n) {
not_trivially_copyable obj;
obj.somecontent = deserialize_stdstring<std::string>(buffer, n);
return obj;
};
not_trivially_copyable result = exile_launch<not_trivially_copyable>(exile_init_policy(), serializer, deserializer, []() {not_trivially_copyable obj; obj.somecontent = "Just something"; return obj;});
assert(result.somecontent == "Just something");
return 0;
}
int main(int argc, char *argv[])
{
if(argc < 2)
{
std::cerr << "Missing test" << std::endl;
return 1;
}
std::map<std::string, int (*)()> map = {
{ "launch-trivial-cpp", &test_exile_launch_trivial} ,
{ "launch-stdstring-cpp", &test_exile_launch_stdstring },
{ "launch-serializer-cpp", &test_exile_launch_serializer },
};
std::string test = argv[1];
if(test == "--dumptests")
{
for(auto &entry : map)
{
std::cout << entry.first << std::endl;
}
return 0;
}
int (*fn)() = map[test];
if(fn != nullptr)
{
return fn();
}
std::cerr << "Unknown test" << std::endl;
return 1;
}

18
test.sh
View File

@ -44,14 +44,15 @@ function runtest_skipped()
function runtest()
{
testname="$1"
test_log_file="$2"
testbin="$1"
testname="$2"
test_log_file="$3"
echo "Running: $testname. Date: $(date)" > "${test_log_file}"
echo -n "Running $1... "
echo -n "Running $testname... "
#exit $? to suppress shell message like "./test.sh: line 18: pid Bad system call"
(./test $1 || exit $?) &>> "${test_log_file}"
(./$testbin "$testname" || exit $?) &>> "${test_log_file}"
ret=$?
SUCCESS="no"
if [ $ret -eq 0 ] ; then
@ -64,7 +65,7 @@ function runtest()
runtest_fail
fi
echo "Finished: ${testname}. Date: $(date). Success: $SUCCESS" >> "${test_log_file}"
echo "Finished: ${testname} (${testbin}). Date: $(date). Success: $SUCCESS" >> "${test_log_file}"
}
GIT_ID=$( git log --pretty="format:%h" -n1 )
@ -79,7 +80,12 @@ LOG_OUTPUT_DIR_PATH="${LOG_OUTPUT_DIR}/exile_test_${GIT_ID}_${TIMESTAMP}"
for test in $( ./test --dumptests ) ; do
testname=$( echo $test )
runtest "$testname" "${LOG_OUTPUT_DIR_PATH}/log.${testname}"
runtest test "$testname" "${LOG_OUTPUT_DIR_PATH}/log.${testname}"
done
for test in $( ./testcpp --dumptests ) ; do
testname=$( echo $test )
runtest testcpp "$testname" "${LOG_OUTPUT_DIR_PATH}/log.${testname}"
done
echo
echo "Tests finished. Logs in $(realpath ${LOG_OUTPUT_DIR_PATH})"