#include "exile.h" #include #include #include #include #include #include #ifndef EXILE_MMAP_SIZE #define EXILE_MMAP_SIZE 128 * 1024 * 1024 //128MB #endif template class launch_arg { static_assert(std::is_trivially_copyable_v); static_assert(!std::is_pointer_v); public: struct exile_policy *policy; T *result_shm; U fn; std::tuple 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)...) {} }; template class launch_arg_serializer { static_assert(std::is_copy_constructible_v); public: struct exile_policy *policy; char *serialize_buffer; size_t n; U fn; std::tuple args; const std::function &serializer; const std::function &deserializer; launch_arg_serializer(struct exile_policy *policy, char *serialize_buffer, size_t n, const std::function &serializer, const std::function &deserializer, U fn, Args && ... args) : policy(policy), serialize_buffer(serialize_buffer), n(n), fn(fn), args(std::forward(args)...), serializer(serializer), deserializer(deserializer) {} }; template int exile_clone_handle_trivial(void * arg) { static_assert(std::is_trivially_copyable_v); static_assert(!std::is_pointer_v); launch_arg *launchargs = (launch_arg *) 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 int exile_clone_handle_serializer(void * arg) { static_assert(std::is_copy_constructible_v); launch_arg_serializer *launchargs = (launch_arg_serializer *) 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); char *stackbegin = stack; 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); free(stackbegin); if(WIFEXITED(status)) { return WEXITSTATUS(status); } /* TODO: exception or what? */ return 23; } template typename std::enable_if_t && !std::is_pointer_v, 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 deleter(nullptr, [sharedbuf, mapsize](...){ munmap(sharedbuf, mapsize); }); launch_arg launcharg(policy, sharedbuf, fn, std::forward(args)...); int (*clonefn)(void *) = &exile_clone_handle_trivial; /* 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 std::enable_if_t || (!std::is_trivially_copyable_v && std::is_copy_constructible_v), T> exile_launch(struct exile_policy *policy, const std::function &serializer, const std::function &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 deleter(nullptr, [sharedbuf, mapsize](...){ munmap(sharedbuf, mapsize); }); launch_arg_serializer launcharg(policy, sharedbuf, mapsize, serializer, deserializer, fn, std::forward(args)...); int (*clonefn)(void *) = &exile_clone_handle_serializer; /* 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 std::basic_string deserialize_stdstring(const char *buf, size_t n) { return std::basic_string { buf, n }; } template size_t serialize_stdstring(const std::basic_string &t, char *buf, size_t n) { if(n < t.size()) { return 0; } memcpy(buf, t.data(), t.size()); return t.size(); } template std::basic_string exile_launch(struct exile_policy *policy, U fn, Args && ... args) { return exile_launch(policy, &serialize_stdstring, &deserialize_stdstring, fn, std::forward(args) ...); }