From cc96736a93e741834dfd16de12c1179173bc685c Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Wed, 17 Jul 2024 16:29:55 +0200 Subject: [PATCH 001/151] Cleanup of CAPIOV1 unused code --- src/common/capio/constants.hpp | 1 + src/common/capio/dirent.hpp | 21 - src/common/capio/requests.hpp | 56 +- src/posix/handlers/access.hpp | 58 +- src/posix/handlers/chdir.hpp | 17 +- src/posix/handlers/close.hpp | 5 +- src/posix/handlers/dup.hpp | 7 +- src/posix/handlers/exit_group.hpp | 3 +- src/posix/handlers/fchmod.hpp | 10 +- src/posix/handlers/fchown.hpp | 12 +- src/posix/handlers/fcntl.hpp | 45 +- src/posix/handlers/fgetxattr.hpp | 8 +- src/posix/handlers/getcwd.hpp | 22 +- src/posix/handlers/getdents.hpp | 34 +- src/posix/handlers/ioctl.hpp | 4 +- src/posix/handlers/lseek.hpp | 69 +-- src/posix/handlers/mkdir.hpp | 35 +- src/posix/handlers/open.hpp | 37 +- src/posix/handlers/read.hpp | 7 +- src/posix/handlers/rename.hpp | 27 +- src/posix/handlers/stat.hpp | 52 +- src/posix/handlers/statfs.hpp | 5 +- src/posix/handlers/statx.hpp | 81 +-- src/posix/handlers/unlink.hpp | 18 +- src/posix/handlers/write.hpp | 43 +- src/posix/libcapio_posix.cpp | 1 - src/posix/utils/cache.hpp | 186 ------- src/posix/utils/clone.hpp | 2 - src/posix/utils/data.hpp | 41 -- src/posix/utils/requests.hpp | 181 ++----- src/server/capio_server.cpp | 137 +---- src/server/cl-engine/cl_engine.hpp | 150 ++++++ .../cl-engine/src/capio_file_locations.hpp | 187 +++++++ src/server/cl-engine/src/json_parser.hpp | 244 +++++++++ src/server/handlers.hpp | 25 - src/server/handlers/access.hpp | 14 - src/server/handlers/clone.hpp | 27 - src/server/handlers/close.hpp | 79 --- src/server/handlers/common.hpp | 37 -- src/server/handlers/dup.hpp | 15 - src/server/handlers/exig.hpp | 49 -- src/server/handlers/getdents.hpp | 111 ---- src/server/handlers/handshake.hpp | 30 -- src/server/handlers/mkdir.hpp | 11 - src/server/handlers/open.hpp | 91 ---- src/server/handlers/read.hpp | 187 ------- src/server/handlers/rename.hpp | 40 -- src/server/handlers/rmdir.hpp | 20 - src/server/handlers/seek.hpp | 65 --- src/server/handlers/stat.hpp | 95 ---- src/server/handlers/unlink.hpp | 28 - src/server/handlers/write.hpp | 40 -- src/server/remote/README.md | 22 - src/server/remote/backend.hpp | 92 ---- src/server/remote/backend/include.hpp | 8 - src/server/remote/backend/mpi.hpp | 166 ------ src/server/remote/handlers.hpp | 7 - src/server/remote/handlers/read.hpp | 304 ----------- src/server/remote/handlers/stat.hpp | 85 --- src/server/remote/listener.hpp | 73 --- src/server/remote/requests.hpp | 113 ---- src/server/utils/capio_file.hpp | 495 ------------------ src/server/utils/common.hpp | 48 -- src/server/utils/filesystem.hpp | 1 - src/server/utils/json.hpp | 231 -------- src/server/utils/location.hpp | 301 ----------- src/server/utils/metadata.hpp | 276 ---------- src/server/utils/requests.hpp | 95 ---- src/server/utils/signals.hpp | 15 +- src/server/utils/types.hpp | 4 - 70 files changed, 767 insertions(+), 4339 deletions(-) delete mode 100644 src/common/capio/dirent.hpp delete mode 100644 src/posix/utils/cache.hpp delete mode 100644 src/posix/utils/data.hpp create mode 100644 src/server/cl-engine/cl_engine.hpp create mode 100644 src/server/cl-engine/src/capio_file_locations.hpp create mode 100644 src/server/cl-engine/src/json_parser.hpp delete mode 100644 src/server/handlers.hpp delete mode 100644 src/server/handlers/access.hpp delete mode 100644 src/server/handlers/clone.hpp delete mode 100644 src/server/handlers/close.hpp delete mode 100644 src/server/handlers/common.hpp delete mode 100644 src/server/handlers/dup.hpp delete mode 100644 src/server/handlers/exig.hpp delete mode 100644 src/server/handlers/getdents.hpp delete mode 100644 src/server/handlers/handshake.hpp delete mode 100644 src/server/handlers/mkdir.hpp delete mode 100644 src/server/handlers/open.hpp delete mode 100644 src/server/handlers/read.hpp delete mode 100644 src/server/handlers/rename.hpp delete mode 100644 src/server/handlers/rmdir.hpp delete mode 100644 src/server/handlers/seek.hpp delete mode 100644 src/server/handlers/stat.hpp delete mode 100644 src/server/handlers/unlink.hpp delete mode 100644 src/server/handlers/write.hpp delete mode 100644 src/server/remote/README.md delete mode 100644 src/server/remote/backend.hpp delete mode 100644 src/server/remote/backend/include.hpp delete mode 100644 src/server/remote/backend/mpi.hpp delete mode 100644 src/server/remote/handlers.hpp delete mode 100644 src/server/remote/handlers/read.hpp delete mode 100644 src/server/remote/handlers/stat.hpp delete mode 100644 src/server/remote/listener.hpp delete mode 100644 src/server/remote/requests.hpp delete mode 100644 src/server/utils/capio_file.hpp delete mode 100644 src/server/utils/json.hpp delete mode 100644 src/server/utils/location.hpp delete mode 100644 src/server/utils/metadata.hpp delete mode 100644 src/server/utils/requests.hpp diff --git a/src/common/capio/constants.hpp b/src/common/capio/constants.hpp index 81bf47d64..1fbd8aab5 100644 --- a/src/common/capio/constants.hpp +++ b/src/common/capio/constants.hpp @@ -99,6 +99,7 @@ constexpr char CAPIO_LOG_SERVER_BANNER[] = constexpr char CAPIO_LOG_SERVER_CLI_LEVEL_INFO[] = "[ \033[1;32m SERVER \033[0m ] "; constexpr char CAPIO_LOG_SERVER_CLI_LEVEL_WARNING[] = "[ \033[1;33m SERVER \033[0m ] "; constexpr char CAPIO_LOG_SERVER_CLI_LEVEL_ERROR[] = "[ \033[1;31m SERVER \033[0m ] "; +constexpr char CAPIO_LOG_SERVER_CLI_LEVEL_JSON[] = "[ \033[1;34m SERVER \033[0m ] "; constexpr char CAPIO_LOG_SERVER_CLI_LOGGING_ENABLED_WARNING[] = "[ \033[1;33m SERVER \033[0m ] " "|==================================================================|\n" diff --git a/src/common/capio/dirent.hpp b/src/common/capio/dirent.hpp deleted file mode 100644 index 474818b0c..000000000 --- a/src/common/capio/dirent.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef CAPIO_SERVER_UTILS_DATA_STRUCTURE_HPP -#define CAPIO_SERVER_UTILS_DATA_STRUCTURE_HPP - -#include - -#include -#include -#include -#include - -#include "constants.hpp" - -struct linux_dirent64 { - ino64_t d_ino; /* 64-bit inode number */ - off64_t d_off; /* 64-bit offset to next structure */ - unsigned short d_reclen; /* Size of this dirent */ - unsigned char d_type; /* File type */ - char d_name[NAME_MAX]; /* Filename (null-terminated) */ -}; - -#endif // CAPIO_SERVER_UTILS_DATA_STRUCTURE_HPP diff --git a/src/common/capio/requests.hpp b/src/common/capio/requests.hpp index 533a68178..8020f492a 100644 --- a/src/common/capio/requests.hpp +++ b/src/common/capio/requests.hpp @@ -1,42 +1,26 @@ #ifndef CAPIO_COMMON_REQUESTS_HPP #define CAPIO_COMMON_REQUESTS_HPP -constexpr const int CAPIO_REQUEST_ACCESS = 0; -constexpr const int CAPIO_REQUEST_CLONE = 1; -constexpr const int CAPIO_REQUEST_CLOSE = 2; -constexpr const int CAPIO_REQUEST_CREATE = 3; -constexpr const int CAPIO_REQUEST_CREATE_EXCLUSIVE = 4; -constexpr const int CAPIO_REQUEST_DUP = 5; -constexpr const int CAPIO_REQUEST_EXIT_GROUP = 6; -constexpr const int CAPIO_REQUEST_FSTAT = 7; -constexpr const int CAPIO_REQUEST_GETDENTS = 8; -constexpr const int CAPIO_REQUEST_GETDENTS64 = 9; -constexpr const int CAPIO_REQUEST_HANDSHAKE_NAMED = 10; -constexpr const int CAPIO_REQUEST_HANDSHAKE_ANONYMOUS = 11; -constexpr const int CAPIO_REQUEST_MKDIR = 12; -constexpr const int CAPIO_REQUEST_OPEN = 13; -constexpr const int CAPIO_REQUEST_READ = 14; -constexpr const int CAPIO_REQUEST_RENAME = 15; -constexpr const int CAPIO_REQUEST_SEEK = 16; -constexpr const int CAPIO_REQUEST_SEEK_DATA = 17; -constexpr const int CAPIO_REQUEST_SEEK_END = 18; -constexpr const int CAPIO_REQUEST_SEEK_HOLE = 19; -constexpr const int CAPIO_REQUEST_STAT = 20; -constexpr const int CAPIO_REQUEST_UNLINK = 21; -constexpr const int CAPIO_REQUEST_WRITE = 22; -constexpr const int CAPIO_REQUEST_RMDIR = 23; +constexpr const int CAPIO_REQUEST_CONSENT = 0; +constexpr const int CAPIO_REQUEST_ACCESS = 1; +constexpr const int CAPIO_REQUEST_CLONE = 2; +constexpr const int CAPIO_REQUEST_CLOSE = 3; +constexpr const int CAPIO_REQUEST_CREATE = 4; +constexpr const int CAPIO_REQUEST_EXIT_GROUP = 5; +constexpr const int CAPIO_REQUEST_GETDENTS = 6; +constexpr const int CAPIO_REQUEST_GETDENTS64 = 7; +constexpr const int CAPIO_REQUEST_HANDSHAKE_NAMED = 8; +constexpr const int CAPIO_REQUEST_HANDSHAKE_ANONYMOUS = 9; +constexpr const int CAPIO_REQUEST_MKDIR = 10; +constexpr const int CAPIO_REQUEST_OPEN = 11; +constexpr const int CAPIO_REQUEST_READ = 12; +constexpr const int CAPIO_REQUEST_RENAME = 13; +constexpr const int CAPIO_REQUEST_SEEK = 14; +constexpr const int CAPIO_REQUEST_STAT = 15; +constexpr const int CAPIO_REQUEST_UNLINK = 16; +constexpr const int CAPIO_REQUEST_WRITE = 17; +constexpr const int CAPIO_REQUEST_RMDIR = 18; -constexpr const int CAPIO_NR_REQUESTS = 24; - -/*REQUESTS FOR SERVER TO SERVER COMMUNICATION*/ - -constexpr const int CAPIO_SERVER_REQUEST_READ = 0; -constexpr const int CAPIO_SERVER_REQUEST_READ_REPLY = 1; -constexpr const int CAPIO_SERVER_REQUEST_READ_BATCH = 2; -constexpr const int CAPIO_SERVER_REQUEST_READ_BATCH_REPLY = 3; -constexpr const int CAPIO_SERVER_REQUEST_STAT = 4; -constexpr const int CAPIO_SERVER_REQUEST_STAT_REPLY = 5; - -constexpr const int CAPIO_SERVER_NR_REQUEST = 6; +constexpr const int CAPIO_NR_REQUESTS = 19; #endif // CAPIO_COMMON_REQUESTS_HPP diff --git a/src/posix/handlers/access.hpp b/src/posix/handlers/access.hpp index 0f56b2f7a..cf5d65850 100644 --- a/src/posix/handlers/access.hpp +++ b/src/posix/handlers/access.hpp @@ -6,10 +6,33 @@ #include "utils/common.hpp" #include "utils/filesystem.hpp" -inline off64_t capio_faccessat(int dirfd, const std::string_view &pathname, mode_t mode, int flags, - long tid) { - START_LOG(tid, "call(dirfd=%d, pathname=%s, mode=%o, flags=%X)", dirfd, pathname.data(), mode, - flags); +int access_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { + const std::string_view pathname(reinterpret_cast(arg0)); + auto mode = static_cast(arg1); + long tid = syscall_no_intercept(SYS_gettid); + START_LOG(tid, "call()"); + if (is_forbidden_path(pathname)) { + LOG("Path %s is forbidden: skip", pathname.data()); + return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; + } + + std::filesystem::path path(pathname); + if (path.is_relative()) { + path = capio_posix_realpath(pathname); + } + + consent_to_proceed_request(path, tid); + return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; +} + +int faccessat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, + long *result) { + auto dirfd = static_cast(arg0); + const std::string_view pathname(reinterpret_cast(arg1)); + auto mode = static_cast(arg2); + auto flags = static_cast(arg3); + long tid = syscall_no_intercept(SYS_gettid); + START_LOG(tid, "call()"); if (is_forbidden_path(pathname)) { LOG("Path %s is forbidden: skip", pathname.data()); @@ -34,34 +57,11 @@ inline off64_t capio_faccessat(int dirfd, const std::string_view &pathname, mode return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } path = (dir_path / path).lexically_normal(); - return is_capio_path(path) ? access_request(path, tid) : -2; } } - if (is_capio_path(path)) { - return access_request(path, tid); - } else { - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; - } -} - -int access_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - const std::string_view pathname(reinterpret_cast(arg0)); - auto mode = static_cast(arg1); - long tid = syscall_no_intercept(SYS_gettid); - - return posix_return_value(capio_faccessat(AT_FDCWD, pathname, mode, 0, tid), result); -} - -int faccessat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, - long *result) { - auto dirfd = static_cast(arg0); - const std::string_view pathname(reinterpret_cast(arg1)); - auto mode = static_cast(arg2); - auto flags = static_cast(arg3); - long tid = syscall_no_intercept(SYS_gettid); - - return posix_return_value(capio_faccessat(dirfd, pathname, mode, flags, tid), result); + consent_to_proceed_request(path, tid); + return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } #endif // SYS_access || SYS_faccessat || SYS_faccessat2 diff --git a/src/posix/handlers/chdir.hpp b/src/posix/handlers/chdir.hpp index 179beb196..9191e043a 100644 --- a/src/posix/handlers/chdir.hpp +++ b/src/posix/handlers/chdir.hpp @@ -1,16 +1,11 @@ #ifndef CAPIO_POSIX_HANDLERS_CHDIR_HPP #define CAPIO_POSIX_HANDLERS_CHDIR_HPP +#include "utils/requests.hpp" #if defined(SYS_chdir) #include "utils/filesystem.hpp" -/* - * chdir could be done to a CAPIO dir that is not present in the filesystem. - * For this reason if chdir is done to a CAPIO directory we don't give control - * to the kernel. - */ - int chdir_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { const std::string_view pathname(reinterpret_cast(arg0)); long tid = syscall_no_intercept(SYS_gettid); @@ -25,17 +20,9 @@ int chdir_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long ar std::filesystem::path path(pathname); if (path.is_relative()) { path = capio_posix_realpath(path); - if (path.empty()) { - *result = -errno; - return CAPIO_POSIX_SYSCALL_SUCCESS; - } } - if (is_capio_path(path)) { - set_current_dir(path); - errno = 0; - return CAPIO_POSIX_SYSCALL_SUCCESS; - } + consent_to_proceed_request(path, tid); // if not a capio path, then control is given to kernel return CAPIO_POSIX_SYSCALL_SKIP; diff --git a/src/posix/handlers/close.hpp b/src/posix/handlers/close.hpp index 6889002a6..c32a661b0 100644 --- a/src/posix/handlers/close.hpp +++ b/src/posix/handlers/close.hpp @@ -11,11 +11,10 @@ int close_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long ar START_LOG(tid, "call(fd=%ld)", fd); if (exists_capio_fd(fd)) { - get_write_cache(tid).flush(); - close_request(fd, tid); + close_request(get_capio_fd_path(fd), tid); delete_capio_fd(fd); - *result = 0; } + return CAPIO_POSIX_SYSCALL_SKIP; } diff --git a/src/posix/handlers/dup.hpp b/src/posix/handlers/dup.hpp index fba955675..ff494f52c 100644 --- a/src/posix/handlers/dup.hpp +++ b/src/posix/handlers/dup.hpp @@ -13,12 +13,11 @@ int dup_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5 START_LOG(tid, "call(fd=%d)", fd); if (exists_capio_fd(fd)) { - int res = open("/dev/null", O_WRONLY); + auto res = static_cast(syscall_no_intercept(SYS_dup, fd)); if (res == -1) { *result = -errno; return CAPIO_POSIX_SYSCALL_SUCCESS; } - dup_request(fd, res, tid); dup_capio_fd(tid, fd, res, false); *result = res; @@ -32,8 +31,6 @@ int dup2_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg int fd = static_cast(arg0); int fd2 = static_cast(arg1); - // int res = capio_dup2(fd, fd2, tid); - START_LOG(tid, "call(fd=%d, fd2=%d)", fd, fd2); if (exists_capio_fd(fd)) { @@ -43,7 +40,6 @@ int dup2_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg return CAPIO_POSIX_SYSCALL_SUCCESS; } if (fd != res) { - dup_request(fd, res, tid); dup_capio_fd(tid, fd, res, false); } *result = res; @@ -74,7 +70,6 @@ int dup3_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg return CAPIO_POSIX_SYSCALL_SUCCESS; } bool is_cloexec = (flags & O_CLOEXEC) == O_CLOEXEC; - dup_request(fd, res, tid); dup_capio_fd(tid, fd, res, is_cloexec); *result = res; diff --git a/src/posix/handlers/exit_group.hpp b/src/posix/handlers/exit_group.hpp index ff609f8c5..b6a720970 100644 --- a/src/posix/handlers/exit_group.hpp +++ b/src/posix/handlers/exit_group.hpp @@ -20,8 +20,7 @@ int exit_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg if (is_capio_tid(tid)) { LOG("Thread %d is a CAPIO thread: clean up", tid); - get_read_cache(tid).flush(); - get_write_cache(tid).flush(); + exit_group_request(tid); remove_capio_tid(tid); } diff --git a/src/posix/handlers/fchmod.hpp b/src/posix/handlers/fchmod.hpp index f8f27f8fd..c338461c2 100644 --- a/src/posix/handlers/fchmod.hpp +++ b/src/posix/handlers/fchmod.hpp @@ -5,6 +5,7 @@ int fchmod_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { int fd = static_cast(arg0); + long tid = syscall_no_intercept(SYS_gettid); START_LOG(syscall_no_intercept(SYS_gettid), "call(fd=%d)", fd); if (!exists_capio_fd(fd)) { @@ -12,12 +13,9 @@ int fchmod_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long a return CAPIO_POSIX_SYSCALL_SKIP; } - // Upon success fchmod shall return 0 - // Since capio does not handle permission, we will be - // Upon the assumption that all the operations occurs with success - *result = 0; - LOG("File is present in capio. Ignoring fchmod operation"); - return CAPIO_POSIX_SYSCALL_SUCCESS; + consent_to_proceed_request(get_capio_fd_path(fd), tid); + + return CAPIO_POSIX_SYSCALL_SKIP; } #endif // SYS_chmod diff --git a/src/posix/handlers/fchown.hpp b/src/posix/handlers/fchown.hpp index e347ae8b5..78b034658 100644 --- a/src/posix/handlers/fchown.hpp +++ b/src/posix/handlers/fchown.hpp @@ -1,10 +1,12 @@ #ifndef CAPIO_POSIX_HANDLERS_FCHOWN_HPP #define CAPIO_POSIX_HANDLERS_FCHOWN_HPP +#include "utils/requests.hpp" #if defined(SYS_chown) int fchown_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { int fd = static_cast(arg0); + long tid = syscall_no_intercept(SYS_gettid); START_LOG(syscall_no_intercept(SYS_gettid), "call(fd=%d)", fd); if (!exists_capio_fd(fd)) { @@ -12,13 +14,9 @@ int fchown_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long a return CAPIO_POSIX_SYSCALL_SKIP; } - // Upon success fchown shall return 0 - // Since capio does not handle permission, we will be - // Upon the assumption that all the operations occurs with success - *result = 0; - LOG("File is present in capio. Ignoring fchmod operation"); - return CAPIO_POSIX_SYSCALL_SUCCESS; - return CAPIO_POSIX_SYSCALL_SUCCESS; + consent_to_proceed_request(get_capio_fd_path(fd), tid); + + return CAPIO_POSIX_SYSCALL_SKIP; } #endif // SYS_chown diff --git a/src/posix/handlers/fcntl.hpp b/src/posix/handlers/fcntl.hpp index 1d8f021ce..aa77c5455 100644 --- a/src/posix/handlers/fcntl.hpp +++ b/src/posix/handlers/fcntl.hpp @@ -11,53 +11,10 @@ int fcntl_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long ar auto arg = static_cast(arg2); long tid = syscall_no_intercept(SYS_gettid); - // int res = capio_fcntl(fd, cmd, arg, tid); - START_LOG(tid, "call(fd=%d, cmd=%d, arg=%d)", fd, cmd, arg); if (exists_capio_fd(fd)) { - switch (cmd) { - case F_GETFD: { - *result = get_capio_fd_cloexec(fd); - return CAPIO_POSIX_SYSCALL_SUCCESS; - } - - case F_SETFD: { - set_capio_fd_cloexec(fd, arg); - return CAPIO_POSIX_SYSCALL_SUCCESS; - } - - case F_GETFL: { - *result = get_capio_fd_flags(fd); - return CAPIO_POSIX_SYSCALL_SUCCESS; - } - - case F_SETFL: { - set_capio_fd_flags(fd, arg); - return CAPIO_POSIX_SYSCALL_SUCCESS; - } - - case F_DUPFD_CLOEXEC: { - int dev_fd = open("/dev/null", O_RDONLY); - - if (dev_fd == -1) { - ERR_EXIT("open /dev/null"); - } - - int res = fcntl(dev_fd, F_DUPFD_CLOEXEC, arg); - if (res == -1) { - *result = -errno; - } - close(dev_fd); - dup_capio_fd(tid, fd, res, true); - dup_request(fd, res, tid); - - return CAPIO_POSIX_SYSCALL_SUCCESS; - } - - default: - ERR_EXIT("fcntl with cmd %d is not yet supported", cmd); - } + consent_to_proceed_request(get_capio_fd_path(fd), tid); } return CAPIO_POSIX_SYSCALL_SKIP; } diff --git a/src/posix/handlers/fgetxattr.hpp b/src/posix/handlers/fgetxattr.hpp index 1e4ba4acd..adefb117a 100644 --- a/src/posix/handlers/fgetxattr.hpp +++ b/src/posix/handlers/fgetxattr.hpp @@ -13,13 +13,7 @@ int fgetxattr_handler(long arg0, long arg1, long arg2, long arg3, long arg4, lon START_LOG(tid, "call(name=%s, value=0x%08x, size=%ld)", name.c_str(), value, size); if (exists_capio_fd(fd)) { - if (std::equal(name.begin(), name.end(), "system.posix_acl_access")) { - errno = ENODATA; - *result = -errno; - return CAPIO_POSIX_SYSCALL_SUCCESS; - } else { - ERR_EXIT("fgetxattr with name %s is not yet supported in CAPIO", name.c_str()); - } + consent_to_proceed_request(get_capio_fd_path(fd), tid); } return CAPIO_POSIX_SYSCALL_SKIP; } diff --git a/src/posix/handlers/getcwd.hpp b/src/posix/handlers/getcwd.hpp index fb1b977e6..33af40c6c 100644 --- a/src/posix/handlers/getcwd.hpp +++ b/src/posix/handlers/getcwd.hpp @@ -10,27 +10,7 @@ int getcwd_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long a START_LOG(tid, "call(buf=0x%08x, size=%ld)", buf, size); - const std::filesystem::path &cwd = get_current_dir(); - - /* - * Returning control to the kernel when current directory is - * not a CAPIO path helps to solve timing issues with network - * file systems such as NFS. - */ - if (!is_capio_path(cwd)) { - return CAPIO_POSIX_SYSCALL_SKIP; - } - - const size_t length = cwd.native().length(); - if ((length + 1) * sizeof(char) > size) { - *result = -ERANGE; - } else { - LOG("CWD: %s", cwd.c_str()); - cwd.native().copy(buf, size); - buf[length] = '\0'; - *result = static_cast(length) + 1; - } - return CAPIO_POSIX_SYSCALL_SUCCESS; + return CAPIO_POSIX_SYSCALL_SKIP; } #endif // SYS_getcwd diff --git a/src/posix/handlers/getdents.hpp b/src/posix/handlers/getdents.hpp index 5c4fec3f6..231d2b27f 100644 --- a/src/posix/handlers/getdents.hpp +++ b/src/posix/handlers/getdents.hpp @@ -3,9 +3,7 @@ #if defined(SYS_getdents) || defined(SYS_getdents64) -#include "capio/dirent.hpp" -#include "utils/data.hpp" // TODO: too similar to capio_read, refactoring needed inline int getdents_handler_impl(long arg0, long arg1, long arg2, long *result, bool is64bit) { @@ -18,37 +16,7 @@ inline int getdents_handler_impl(long arg0, long arg1, long arg2, long *result, is64bit ? "true" : "false"); if (exists_capio_fd(fd)) { - LOG("fd=%d, is a capio file descriptor", fd); - - if (count >= SSIZE_MAX) { - ERR_EXIT("src does not support read bigger than SSIZE_MAX yet"); - } - get_write_cache(tid).flush(); - *result = get_read_cache(tid).read(fd, buffer, count, true, is64bit); - - DBG(tid, [](char *buf, off64_t count) { - START_LOG(syscall_no_intercept(SYS_gettid), "call (count=%ld)", count); - struct linux_dirent64 *d; - LOG("%25s %12s %13s %15s %s", "INODE", "TYPE", "RECORD_LENGTH", "OFFSET", "NAME"); - for (off64_t bpos = 0, i = 0; bpos < count && i < 10; i++) { - d = (struct linux_dirent64 *) (buf + bpos); - LOG("%25lu %12s %13ld %15ld %s\n", d->d_ino, - (d->d_type == 8) ? "regular" - : (d->d_type == 4) ? "directory" - : (d->d_type == 1) ? "FIFO" - : (d->d_type == 12) ? "socket" - : (d->d_type == 10) ? "symlink" - : (d->d_type == 6) ? "block dev" - : (d->d_type == 2) ? "char dev" - : "???", - d->d_reclen, d->d_off, d->d_name); - bpos += d->d_reclen; - } - }(reinterpret_cast(buffer), *result)); - - return CAPIO_POSIX_SYSCALL_SUCCESS; - } else { - LOG("fd=%d, is not a capio file descriptor", fd); + consent_to_proceed_request(get_capio_fd_path(fd), tid); } return CAPIO_POSIX_SYSCALL_SKIP; } diff --git a/src/posix/handlers/ioctl.hpp b/src/posix/handlers/ioctl.hpp index 9bd6b923e..98afe9226 100644 --- a/src/posix/handlers/ioctl.hpp +++ b/src/posix/handlers/ioctl.hpp @@ -10,9 +10,7 @@ int ioctl_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long ar START_LOG(tid, "call(fd=%d, request=%ld)", fd, request, tid); if (exists_capio_fd(fd)) { - errno = ENOTTY; - *result = -errno; - return CAPIO_POSIX_SYSCALL_SUCCESS; + consent_to_proceed_request(get_capio_fd_path(fd), tid); } return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } diff --git a/src/posix/handlers/lseek.hpp b/src/posix/handlers/lseek.hpp index 9cc275449..0faf3fb82 100644 --- a/src/posix/handlers/lseek.hpp +++ b/src/posix/handlers/lseek.hpp @@ -5,73 +5,20 @@ #include "utils/common.hpp" -// TODO: EOVERFLOW is not addressed -inline off64_t capio_lseek(int fd, off64_t offset, int whence, long tid) { - START_LOG(tid, "call(fd=%d, offset=%ld, whence=%d)", fd, offset, whence); - - if (exists_capio_fd(fd)) { - get_read_cache(tid).flush(); - get_write_cache(tid).flush(); - off64_t file_offset = get_capio_fd_offset(fd); - if (whence == SEEK_SET) { - LOG("whence %d is SEEK_SET", whence); - if (offset >= 0) { - set_capio_fd_offset(fd, offset); - seek_request(fd, offset, tid); - LOG("the new offset of file %d is %ld", fd, offset); - return offset; - } else { - errno = EINVAL; - return CAPIO_POSIX_SYSCALL_ERRNO; - } - } else if (whence == SEEK_CUR) { - LOG("whence %d is SEEK_CUR", whence); - off64_t new_offset = file_offset + offset; - if (new_offset >= 0) { - set_capio_fd_offset(fd, new_offset); - seek_request(fd, new_offset, tid); - LOG("the new offset of file %d is %ld", fd, new_offset); - return new_offset; - } else { - errno = EINVAL; - return CAPIO_POSIX_SYSCALL_ERRNO; - } - } else if (whence == SEEK_END) { - LOG("whence %d is SEEK_END", whence); - seek_end_request(fd, tid); - off64_t new_offset = file_offset + offset; - set_capio_fd_offset(fd, new_offset); - LOG("the new offset of file %d is %ld", fd, new_offset); - return new_offset; - } else if (whence == SEEK_DATA) { - LOG("whence %d is SEEK_DATA", whence); - off64_t new_offset = seek_data_request(fd, file_offset, tid); - set_capio_fd_offset(fd, new_offset); - LOG("the new offset of file %d is %ld", fd, new_offset); - return new_offset; - } else if (whence == SEEK_HOLE) { - LOG("whence %d is SEEK_HOLE", whence); - off64_t new_offset = seek_hole_request(fd, file_offset, tid); - set_capio_fd_offset(fd, new_offset); - LOG("the new offset of file %d is %ld", fd, new_offset); - return new_offset; - } else { - LOG("whence %d is invalid", whence); - errno = EINVAL; - return CAPIO_POSIX_SYSCALL_ERRNO; - } - } else { - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; - } -} - int lseek_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { int fd = static_cast(arg0); auto offset = static_cast(arg1); int whence = static_cast(arg2); long tid = syscall_no_intercept(SYS_gettid); - return posix_return_value(capio_lseek(fd, offset, whence, tid), result); + START_LOG(tid, "call(fd=%d, offset=%ld, whence=%d)", fd, offset, whence); + if (exists_capio_fd(fd)) { + (get_capio_fd_path(fd), tid); + } + + seek_request(get_capio_fd_path(fd), offset, whence, tid); + + return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } #endif // SYS_lseek diff --git a/src/posix/handlers/mkdir.hpp b/src/posix/handlers/mkdir.hpp index b2331b3fb..445ebb447 100644 --- a/src/posix/handlers/mkdir.hpp +++ b/src/posix/handlers/mkdir.hpp @@ -34,21 +34,9 @@ inline off64_t capio_mkdirat(int dirfd, const std::string_view &pathname, mode_t } if (is_capio_path(path)) { - if (exists_capio_path(path)) { - errno = EEXIST; - return CAPIO_POSIX_SYSCALL_ERRNO; - } - off64_t res = mkdir_request(path, tid); - if (res == 1) { - return CAPIO_POSIX_SYSCALL_ERRNO; - } else { - LOG("Adding %s to capio_files_path", path.c_str()); - add_capio_path(path); - return res; - } - } else { - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; + mkdir_request(path, tid); } + return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } inline off64_t capio_rmdir(const std::string_view &pathname, long tid) { @@ -69,24 +57,9 @@ inline off64_t capio_rmdir(const std::string_view &pathname, long tid) { } if (is_capio_path(path)) { - if (!exists_capio_path(path)) { - LOG("capio_files_path.find == end. errno = " - "ENOENT"); - errno = ENOENT; - return CAPIO_POSIX_SYSCALL_ERRNO; - } - off64_t res = rmdir_request(path, tid); - if (res == 2) { - LOG("res == 2. errno = ENOENT"); - errno = ENOENT; - return CAPIO_POSIX_SYSCALL_ERRNO; - } else { - delete_capio_path(path); - return res; - } - } else { - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; + rmdir_request(path, tid); } + return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } int mkdir_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { diff --git a/src/posix/handlers/open.hpp b/src/posix/handlers/open.hpp index 5d3073eec..4483c39cc 100644 --- a/src/posix/handlers/open.hpp +++ b/src/posix/handlers/open.hpp @@ -36,47 +36,30 @@ inline int capio_openat(int dirfd, const std::string_view &pathname, int flags, } if (is_capio_path(path)) { - int fd = static_cast(syscall_no_intercept(SYS_open, "/dev/null", O_RDONLY)); - if (fd == -1) { - ERR_EXIT("capio_open, /dev/null opening"); - } + + auto fd = static_cast(syscall_no_intercept(SYS_open, path.c_str(), flags)); + bool create = (flags & O_CREAT) == O_CREAT; bool excl = (flags & O_EXCL) == O_EXCL; - if (excl) { + + if (excl | create) { LOG("excl"); - off64_t return_code = create_exclusive_request(fd, path, tid); - if (return_code == 1) { - errno = EEXIST; - return CAPIO_POSIX_SYSCALL_ERRNO; - } - } else if (create) { - LOG("create"); - off64_t return_code = create_request(fd, path, tid); - if (return_code == 1) { - errno = ENOENT; - return CAPIO_POSIX_SYSCALL_ERRNO; - } + create_request(fd, path, tid); } else { LOG("!excl && !create"); - off64_t return_code = open_request(fd, path, tid); - if (return_code == 1) { - errno = ENOENT; - return CAPIO_POSIX_SYSCALL_ERRNO; - } + open_request(fd, path, tid); } + int actual_flags = flags & ~O_CLOEXEC; if ((flags & O_DIRECTORY) == O_DIRECTORY) { actual_flags = actual_flags | O_LARGEFILE; } add_capio_fd(tid, path, fd, 0, CAPIO_DEFAULT_FILE_INITIAL_SIZE, actual_flags, (flags & O_CLOEXEC) == O_CLOEXEC); - if ((flags & O_APPEND) == O_APPEND) { - lseek(fd, 0, SEEK_END); - } + return fd; - } else { - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } + return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } int creat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { diff --git a/src/posix/handlers/read.hpp b/src/posix/handlers/read.hpp index ad1066bdd..101b6720e 100644 --- a/src/posix/handlers/read.hpp +++ b/src/posix/handlers/read.hpp @@ -3,7 +3,6 @@ #if defined(SYS_read) || defined(SYS_readv) -#include "utils/data.hpp" inline off64_t capio_read(int fd, void *buffer, off64_t count, long tid) { START_LOG(tid, "call(fd=%d, buf=0x%08x, count=%ld)", fd, buffer, count); @@ -13,11 +12,9 @@ inline off64_t capio_read(int fd, void *buffer, off64_t count, long tid) { ERR_EXIT("CAPIO does not support read bigger than SSIZE_MAX yet"); } - get_write_cache(tid).flush(); - return get_read_cache(tid).read(fd, buffer, count, false, false); - } else { - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; + read_request(get_capio_fd_path(fd), count, tid); } + return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } inline ssize_t capio_readv(int fd, const struct iovec *iov, int iovcnt, long tid) { diff --git a/src/posix/handlers/rename.hpp b/src/posix/handlers/rename.hpp index 8bdaa4941..309be86ec 100644 --- a/src/posix/handlers/rename.hpp +++ b/src/posix/handlers/rename.hpp @@ -14,26 +14,21 @@ int rename_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long a auto oldpath_abs = capio_absolute(oldpath); auto newpath_abs = capio_absolute(newpath); - if (is_prefix(oldpath_abs, newpath_abs)) { // TODO: The check is more complex - errno = EINVAL; - *result = -errno; - return CAPIO_POSIX_SYSCALL_SUCCESS; - } + /* if (is_prefix(oldpath_abs, newpath_abs)) { // TODO: The check is more complex + errno = EINVAL; + *result = -errno; + return CAPIO_POSIX_SYSCALL_SUCCESS; + }*/ if (is_capio_path(oldpath_abs)) { rename_capio_path(oldpath_abs, newpath_abs); - auto res = rename_request(tid, oldpath_abs, newpath_abs); - *result = (res < 0 ? -errno : res); - return CAPIO_POSIX_SYSCALL_SUCCESS; - } else { - if (is_capio_path(newpath_abs)) { - std::filesystem::copy(oldpath_abs, newpath_abs); - *result = -errno; - return CAPIO_POSIX_SYSCALL_SUCCESS; - } else { - return CAPIO_POSIX_SYSCALL_SKIP; - } + rename_request(tid, oldpath_abs, newpath_abs); + + } else if (is_capio_path(newpath_abs)) { + rename_request(tid, oldpath_abs, newpath_abs); } + + return CAPIO_POSIX_SYSCALL_SUCCESS; } #endif // SYS_rename diff --git a/src/posix/handlers/stat.hpp b/src/posix/handlers/stat.hpp index 4e6f598e8..faa97580d 100644 --- a/src/posix/handlers/stat.hpp +++ b/src/posix/handlers/stat.hpp @@ -15,49 +15,13 @@ inline blkcnt_t get_nblocks(off64_t file_size) { return (file_size % 4096 == 0) ? (file_size / 512) : (file_size / 512 + 8); } -inline void fill_statbuf(struct stat *statbuf, off_t file_size, bool is_dir, ino_t inode) { - START_LOG(syscall_no_intercept(SYS_gettid), - "call(statbuf=0x%08x, file_size=%ld, is_dir=%s, inode=%ul)", statbuf, file_size, - is_dir ? "true" : "false", inode); - - struct timespec time { - 1, 1 - }; - if (is_dir == 1) { - statbuf->st_mode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; - file_size = 4096; - } else { - statbuf->st_mode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - } - statbuf->st_dev = 100; - statbuf->st_ino = inode; - statbuf->st_nlink = 1; - statbuf->st_uid = syscall_no_intercept(SYS_getuid); - statbuf->st_gid = syscall_no_intercept(SYS_getgid); - statbuf->st_rdev = 0; - statbuf->st_size = file_size; - statbuf->st_blksize = 4096; - statbuf->st_blocks = (file_size < 4096) ? 8 : get_nblocks(file_size); - statbuf->st_atim = time; - statbuf->st_mtim = time; - statbuf->st_ctim = time; -} - inline int capio_fstat(int fd, struct stat *statbuf, long tid) { START_LOG(tid, "call(fd=%d, statbuf=0x%08x)", fd, statbuf); if (exists_capio_fd(fd)) { - get_write_cache(tid).flush(); - auto [file_size, is_dir] = fstat_request(fd, tid); - if (file_size == -1) { - errno = ENOENT; - return CAPIO_POSIX_SYSCALL_ERRNO; - } - fill_statbuf(statbuf, file_size, is_dir, std::hash{}(get_capio_fd_path(fd))); - return CAPIO_POSIX_SYSCALL_SUCCESS; - } else { - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; + consent_to_proceed_request(get_capio_fd_path(fd), tid); } + return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } inline int capio_lstat(const std::string_view &pathname, struct stat *statbuf, long tid) { @@ -70,17 +34,9 @@ inline int capio_lstat(const std::string_view &pathname, struct stat *statbuf, l const std::filesystem::path absolute_path(pathname); if (is_capio_path(absolute_path)) { - get_write_cache(tid).flush(); - auto [file_size, is_dir] = stat_request(absolute_path, tid); - if (file_size == -1) { - errno = ENOENT; - return CAPIO_POSIX_SYSCALL_ERRNO; - } - fill_statbuf(statbuf, file_size, is_dir, std::hash{}(absolute_path)); - return CAPIO_POSIX_SYSCALL_SUCCESS; - } else { - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; + consent_to_proceed_request(pathname, tid); } + return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } inline int capio_lstat_wrapper(const std::string_view &pathname, struct stat *statbuf, long tid) { diff --git a/src/posix/handlers/statfs.hpp b/src/posix/handlers/statfs.hpp index c82d593c3..42c630f00 100644 --- a/src/posix/handlers/statfs.hpp +++ b/src/posix/handlers/statfs.hpp @@ -12,10 +12,7 @@ int fstatfs_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long START_LOG(tid, "call(fd=%d, buf=0x%08x)", fd, buf); if (exists_capio_fd(fd)) { - const std::filesystem::path path(get_capio_fd_path(fd)); - - *result = static_cast(syscall_no_intercept(SYS_statfs, get_capio_dir().c_str(), buf)); - return CAPIO_POSIX_SYSCALL_SUCCESS; + consent_to_proceed_request(get_capio_fd_path(fd), tid); } return CAPIO_POSIX_SYSCALL_SKIP; } diff --git a/src/posix/handlers/statx.hpp b/src/posix/handlers/statx.hpp index 4be9d6106..18484ebf7 100644 --- a/src/posix/handlers/statx.hpp +++ b/src/posix/handlers/statx.hpp @@ -5,35 +5,6 @@ #include "utils/common.hpp" -inline void fill_statxbuf(struct statx *statxbuf, off_t file_size, bool is_dir, ino_t inode, - int mask) { - START_LOG(syscall_no_intercept(SYS_gettid), "call(filesize=%ld, is_dir=%s, inode=%d, mask=%d)", - file_size, is_dir ? "true" : "false", mask); - - statx_timestamp time{1, 1}; - if (is_dir == 1) { - LOG("Filling statx struct for file entry"); - statxbuf->stx_mode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; - file_size = 4096; - } else { - LOG("Filling statx struct for directory entry"); - statxbuf->stx_mode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - } - statxbuf->stx_mask = STATX_BASIC_STATS | STATX_BTIME; - statxbuf->stx_attributes_mask = 0; - statxbuf->stx_blksize = 4096; - statxbuf->stx_nlink = 1; - statxbuf->stx_uid = syscall_no_intercept(SYS_getuid); - statxbuf->stx_gid = syscall_no_intercept(SYS_getgid); - statxbuf->stx_ino = inode; - statxbuf->stx_size = file_size; - statxbuf->stx_blocks = (file_size < 4096) ? 8 : get_nblocks(file_size); - statxbuf->stx_atime = time; - statxbuf->stx_btime = time; - statxbuf->stx_ctime = time; - statxbuf->stx_mtime = time; -} - inline int capio_statx(int dirfd, const std::string_view &pathname, int flags, int mask, struct statx *statxbuf, long tid) { START_LOG(tid, "call(dirfd=%d, pathname=%s, flags=%d, mask=%d, statxbuf=0x%08x)", dirfd, @@ -45,57 +16,11 @@ inline int capio_statx(int dirfd, const std::string_view &pathname, int flags, i } std::filesystem::path path(pathname); - if (path.empty() && (flags & AT_EMPTY_PATH) == AT_EMPTY_PATH) { - LOG("pathname is empty and AT_EMPTY_PATH is set"); - if (dirfd == AT_FDCWD) { // operate on currdir - LOG("dirfd is AT_FDCWD"); - path = get_current_dir(); - } else { // operate on dirfd. in this case dirfd can refer to any type of file - if (exists_capio_fd(dirfd)) { - path = get_capio_fd_path(dirfd); - } else { - LOG("returning -2 due to !exists_capio_fd"); - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; - } - } - } else { - if (path.is_relative()) { - if (dirfd == AT_FDCWD) { - LOG("dirfd is AT_FDCWD"); - path = capio_posix_realpath(path); - if (path.empty()) { - LOG("returning -1 due to pathname empty"); - errno = ENOENT; - return CAPIO_POSIX_SYSCALL_ERRNO; - } - } else { - if (!is_directory(dirfd)) { - LOG("returning -2 due to !is_directory"); - errno = ENOTDIR; - return CAPIO_POSIX_SYSCALL_ERRNO; - } - const std::filesystem::path dir_path = get_dir_path(dirfd); - if (dir_path.empty()) { - LOG("returning -2 due to dir path empty"); - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; - } - path = (dir_path / path).lexically_normal(); - } - } - if (!is_capio_path(path)) { - LOG("returning -2 due to not being a capio path"); - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; - } - } - get_write_cache(tid).flush(); - auto [file_size, is_dir] = stat_request(path, tid); - if (file_size == -1) { - errno = ENOENT; - return CAPIO_POSIX_SYSCALL_ERRNO; + if (is_capio_path(path)) { + consent_to_proceed_request(path, tid); } - fill_statxbuf(statxbuf, file_size, is_dir, std::hash{}(path), mask); - return CAPIO_POSIX_SYSCALL_SUCCESS; + return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } int statx_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { diff --git a/src/posix/handlers/unlink.hpp b/src/posix/handlers/unlink.hpp index 71aa9390e..d64aa2f90 100644 --- a/src/posix/handlers/unlink.hpp +++ b/src/posix/handlers/unlink.hpp @@ -9,21 +9,11 @@ off64_t capio_unlink_abs(const std::filesystem::path &abs_path, long tid, bool i START_LOG(tid, "call(abs_path=%s, is_dir=%s)", abs_path.c_str(), is_dir ? "true" : "false"); if (is_capio_path(abs_path)) { - if (is_capio_dir(abs_path)) { - ERR_EXIT("ERROR: unlink to the capio_dir %s", abs_path.c_str()); - } else { - off64_t res = is_dir ? rmdir_request(abs_path, tid) : unlink_request(abs_path, tid); - if (res == -1) { - errno = ENOENT; - } else { - LOG("Removing %s from capio_files_path", abs_path.c_str()); - delete_capio_path(abs_path); - } - return res; - } - } else { - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; + is_dir ? rmdir_request(abs_path, tid) : unlink_request(abs_path, tid); + LOG("Removing %s from capio_files_path", abs_path.c_str()); + delete_capio_path(abs_path); } + return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } inline off64_t capio_unlinkat(int dirfd, const std::string_view &pathname, int flags, long tid) { diff --git a/src/posix/handlers/write.hpp b/src/posix/handlers/write.hpp index 115b3eada..b817d6313 100644 --- a/src/posix/handlers/write.hpp +++ b/src/posix/handlers/write.hpp @@ -10,46 +10,9 @@ inline off64_t capio_write(int fd, const void *buffer, off64_t count, long tid) START_LOG(tid, "call(fd=%d, buf=0x%08x, count=%ld)", fd, buffer, count); if (exists_capio_fd(fd)) { - if (count > SSIZE_MAX) { - ERR_EXIT("Capio does not support writes bigger than " - "SSIZE_MAX yet"); - } - - get_read_cache(tid).flush(); - get_write_cache(tid).write(fd, buffer, count); - - return count; - } else { - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; - } -} - -inline off64_t capio_writev(int fd, const struct iovec *iov, int iovcnt, long tid) { - START_LOG(tid, "call(fd=%d, iov.iov_base=0x%08x, iov.iov_len=%ld, iovcnt=%d)", fd, - iov->iov_base, iov->iov_len, iovcnt); - - if (exists_capio_fd(fd)) { - LOG("fd %d exists and is a capio fd", fd); - off64_t tot_bytes = 0; - off64_t res = 0; - int i = 0; - LOG("iov setup completed. starting write request loop"); - while (i < iovcnt && res >= 0) { - size_t iov_len = iov[i].iov_len; - if (iov_len != 0) { - res = capio_write(fd, iov[i].iov_base, iov[i].iov_len, tid); - if (res == -1) { - return CAPIO_POSIX_SYSCALL_ERRNO; - } - tot_bytes += res; - } - ++i; - } - return tot_bytes; - } else { - LOG("fd %d is not a capio fd. returning -2", fd); - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; + write_request(get_capio_fd_path(fd), count, tid); } + return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } int write_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { @@ -67,7 +30,7 @@ int writev_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long a auto iovcnt = static_cast(arg2); long tid = syscall_no_intercept(SYS_gettid); - return posix_return_value(capio_writev(fd, iov, iovcnt, tid), result); + return posix_return_value(capio_write(fd, iov, iovcnt, tid), result); } #endif // SYS_write || SYS_writev diff --git a/src/posix/libcapio_posix.cpp b/src/posix/libcapio_posix.cpp index 7acda3fdf..251db927c 100644 --- a/src/posix/libcapio_posix.cpp +++ b/src/posix/libcapio_posix.cpp @@ -365,7 +365,6 @@ static int hook(long syscall_number, long arg0, long arg1, long arg2, long arg3, static __attribute__((constructor)) void init() { init_client(); - init_data_plane(); init_filesystem(); init_threading_support(); diff --git a/src/posix/utils/cache.hpp b/src/posix/utils/cache.hpp deleted file mode 100644 index 784245708..000000000 --- a/src/posix/utils/cache.hpp +++ /dev/null @@ -1,186 +0,0 @@ -#ifndef CAPIO_SERVER_UTILS_CACHE -#define CAPIO_SERVER_UTILS_CACHE - -#include "capio/dirent.hpp" -#include "capio/queue.hpp" - -#include "requests.hpp" - -class ReadCache { - private: - char *_cache; - long _tid; - int _last_fd; - off64_t _max_line_size, _actual_size, _cache_offset; - SPSCQueue _queue; - - inline void _read(void *buffer, off64_t count) { - START_LOG(capio_syscall(SYS_gettid), "call(count=%ld)", count); - - if (count > 0) { - memcpy(buffer, _cache + _cache_offset, count); - LOG("Read %ld. adding it to _cache_offset of value %ld", count, _cache_offset); - _cache_offset += count; - } - } - - public: - ReadCache(long tid, off64_t lines, off64_t line_size, const std::string &workflow_name) - : _cache(nullptr), _tid(tid), _last_fd(-1), _max_line_size(line_size), _actual_size(0), - _cache_offset(0), - _queue(SHM_SPSC_PREFIX_READ + std::to_string(tid), lines, line_size, workflow_name) {} - - inline void flush() { - START_LOG(capio_syscall(SYS_gettid), "call()"); - - if (_cache_offset != _actual_size) { - _actual_size = _cache_offset = 0; - seek_request(_last_fd, get_capio_fd_offset(_last_fd), _tid); - } - } - - inline off64_t read(int fd, void *buffer, off64_t count, bool is_getdents, bool is64bit) { - START_LOG(capio_syscall(SYS_gettid), "call(fd=%d, count=%ld, is_getdents=%s, is64bit=%s)", - fd, count, is_getdents ? "true" : "false", is64bit ? "true" : "false"); - - if (_last_fd != fd) { - LOG("changed fd from %d to %d: flushing", _last_fd, fd); - flush(); - _last_fd = fd; - } - - off64_t remaining_bytes = _actual_size - _cache_offset; - off64_t file_offset = get_capio_fd_offset(fd); - off64_t bytes_read; - - if (is_getdents) { - auto dirent_size = static_cast(sizeof(linux_dirent64)); - count = (count / dirent_size) * dirent_size; - } - - auto read_size = count - remaining_bytes; - LOG("Read() will need to read %ld bytes", read_size); - - if (count <= remaining_bytes) { - LOG("count %ld <= remaining_bytes %ld", count, remaining_bytes); - _read(buffer, count); - bytes_read = count; - } else { - LOG("count %ld > remaining_bytes %ld", count, remaining_bytes); - _read(buffer, remaining_bytes); - buffer = reinterpret_cast(buffer) + remaining_bytes; - - if (read_size > _max_line_size) { - LOG("count - remaining_bytes %ld > _max_line_size %ld", read_size, _max_line_size); - LOG("Reading exactly requested size"); - off64_t end_of_read = is_getdents ? getdents_request(fd, read_size, is64bit, _tid) - : read_request(fd, read_size, _tid); - bytes_read = end_of_read - file_offset; - _queue.read(reinterpret_cast(buffer), bytes_read); - } else { - LOG("count - remaining_bytes %ld <= _max_line_size %ld", read_size, _max_line_size); - LOG("Reading more to use pre fetching and caching"); - off64_t end_of_read = is_getdents - ? getdents_request(fd, _max_line_size, is64bit, _tid) - : read_request(fd, _max_line_size, _tid); - LOG("request return value is %ld", end_of_read); - _actual_size = end_of_read - file_offset - remaining_bytes; - LOG("ReaderCache actual size, after requested read is: %ld bytes", _actual_size); - _cache_offset = 0; - if (_actual_size > 0) { - LOG("Fetching data from shm _queue"); - _cache = _queue.fetch(); - } - if (read_size < _actual_size) { - LOG("count - remaining_bytes %ld < _actual_size %ld", read_size, _actual_size); - _read(buffer, read_size); - bytes_read = count; - } else { - LOG("count - remaining_bytes %ld >= _actual_size %ld", read_size, _actual_size); - _read(buffer, _actual_size); - bytes_read = remaining_bytes + _actual_size; - } - } - } - LOG("%ld bytes have been read. setting fd offset to %ld", bytes_read, - file_offset + bytes_read); - set_capio_fd_offset(fd, file_offset + bytes_read); - return bytes_read; - } -}; - -class WriteCache { - private: - char *_cache; - long _tid; - int _fd; - off64_t _max_line_size, _actual_size; - SPSCQueue _queue; - - inline void _write(off64_t count, const void *buffer) { - START_LOG(capio_syscall(SYS_gettid), "call(count=%ld)", count); - - if (count > 0) { - if (_cache == nullptr) { - _cache = _queue.reserve(); - } - memcpy(_cache + _actual_size, buffer, count); - _actual_size += count; - if (_actual_size == _max_line_size) { - flush(); - } - } - } - - public: - WriteCache(long tid, off64_t lines, off64_t line_size, const std::string &workflow_name) - : _cache(nullptr), _tid(tid), _fd(-1), _max_line_size(line_size), _actual_size(0), - _queue(SHM_SPSC_PREFIX_WRITE + std::to_string(tid), lines, line_size, workflow_name) {} - - inline void flush() { - START_LOG(capio_syscall(SYS_gettid), "call()"); - - if (_actual_size != 0) { - write_request(_fd, _actual_size, _tid); - _cache = nullptr; - _actual_size = 0; - } - } - - inline void write(int fd, const void *buffer, off64_t count) { - START_LOG(capio_syscall(SYS_gettid), "call(fd=%d, buffer=0x%08x, count=%ld)", fd, buffer, - count); - - if (_fd != fd) { - LOG("changed fd from %d to %d: flushing", _fd, fd); - flush(); - _fd = fd; - } - - if (count <= _max_line_size - _actual_size) { - LOG("count %ld <= _max_line_size - _actual_size %ld", count, - _max_line_size - _actual_size); - _write(count, buffer); - } else { - LOG("count %ld > _max_line_size - _actual_size %ld", count, - _max_line_size - _actual_size); - flush(); - if (count - _actual_size > _max_line_size) { - LOG("count - _actual_size %ld > _max_line_size %ld", count - _actual_size, - _max_line_size); - write_request(_fd, count, _tid); - _queue.write(reinterpret_cast(buffer), count); - } else { - LOG("count - _actual_size %ld <= _max_line_size %ld", count - _actual_size, - _max_line_size); - _write(count, buffer); - } - } - - set_capio_fd_offset(fd, get_capio_fd_offset(fd) + count); - } -}; - -typedef std::unordered_map> CPThreadDataCache_t; - -#endif // CAPIO_SERVER_UTILS_CACHE diff --git a/src/posix/utils/clone.hpp b/src/posix/utils/clone.hpp index c0d986ada..8185eb876 100644 --- a/src/posix/utils/clone.hpp +++ b/src/posix/utils/clone.hpp @@ -6,7 +6,6 @@ #include "capio/syscall.hpp" -#include "data.hpp" #include "requests.hpp" std::mutex clone_mutex; @@ -36,7 +35,6 @@ void init_process(long tid) { syscall_no_intercept_flag = true; register_listener(tid); - register_data_listener(tid); const char *capio_app_name = get_capio_app_name(); long pid = syscall_no_intercept(SYS_getpid); diff --git a/src/posix/utils/data.hpp b/src/posix/utils/data.hpp deleted file mode 100644 index 8089b2815..000000000 --- a/src/posix/utils/data.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef CAPIO_POSIX_UTILS_DATA_HPP -#define CAPIO_POSIX_UTILS_DATA_HPP - -#include "cache.hpp" - -CPThreadDataCache_t *threads_data_cache; - -/** - * Get read cache for thread @param tid - * @param tid - * @return the thread read cache - */ -inline ReadCache &get_read_cache(long tid) { return *threads_data_cache->at(tid).second; } - -/** - * Get write cache for thread @param tid - * @param tid - * @return the thread write cache - */ -inline WriteCache &get_write_cache(long tid) { return *threads_data_cache->at(tid).first; } - -/** - * Initialize data buffers - * @return - */ -inline void init_data_plane() { threads_data_cache = new CPThreadDataCache_t; } - -/** - * Add a new response buffer for thread @param tid - * @param tid - * @return - */ -inline void register_data_listener(long tid) { - threads_data_cache->insert( - {static_cast(tid), - {new WriteCache(tid, get_cache_lines(), get_cache_line_size(), get_capio_workflow_name()), - new ReadCache(tid, get_cache_lines(), get_cache_line_size(), - get_capio_workflow_name())}}); -} - -#endif // CAPIO_POSIX_UTILS_DATA_HPP diff --git a/src/posix/utils/requests.hpp b/src/posix/utils/requests.hpp index 6b2ed3ed3..6f6178785 100644 --- a/src/posix/utils/requests.hpp +++ b/src/posix/utils/requests.hpp @@ -7,8 +7,8 @@ #include "filesystem.hpp" #include "types.hpp" -CircularBuffer *buf_requests; -CPBufResponse_t *bufs_response; +inline CircularBuffer *buf_requests; +inline CPBufResponse_t *bufs_response; /** * Initialize request and response buffers @@ -27,22 +27,33 @@ inline void init_client() { * @return */ inline void register_listener(long tid) { - // TODO: replace numbers with constexpr auto *p_buf_response = new CircularBuffer(SHM_COMM_CHAN_NAME_RESP + std::to_string(tid), CAPIO_REQ_BUFF_CNT, sizeof(off_t)); bufs_response->insert(std::make_pair(tid, p_buf_response)); } -inline off64_t access_request(const std::filesystem::path &path, const long tid) { +// Block until server allows for proceeding to a generic request +inline void consent_to_proceed_request(const std::filesystem::path &path, const long tid) { START_LOG(capio_syscall(SYS_gettid), "call(path=%s, tid=%ld)", path.c_str(), tid); char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %s", CAPIO_REQUEST_ACCESS, tid, path.c_str()); + sprintf(req, "%04d %ld %s", CAPIO_REQUEST_CONSENT, tid, path.c_str()); + buf_requests->write(req, CAPIO_REQ_MAX_SIZE); + off64_t res; + bufs_response->at(tid)->read(&res); +} + +inline void seek_request(const std::filesystem::path &path, const long offset, const int whence, + const long tid) { + START_LOG(capio_syscall(SYS_gettid), "call(path=%s, offset=%ld, tid=%ld)", path.c_str(), offset, + tid); + char req[CAPIO_REQ_MAX_SIZE]; + sprintf(req, "%04d %ld %s %ld %d", CAPIO_REQUEST_SEEK, tid, path.c_str(), offset, whence); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); off64_t res; bufs_response->at(tid)->read(&res); - return res; } +// non blocking inline void clone_request(const long parent_tid, const long child_tid) { START_LOG(capio_syscall(SYS_gettid), "call(parent_tid=%ld, child_tid=%ld)", parent_tid, child_tid); @@ -51,15 +62,17 @@ inline void clone_request(const long parent_tid, const long child_tid) { buf_requests->write(req, CAPIO_REQ_MAX_SIZE); } -inline void close_request(const int fd, const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(fd=%ld, tid=%ld)", fd, tid); +// non blocking +inline void close_request(const std::filesystem::path &path, const long tid) { + START_LOG(capio_syscall(SYS_gettid), "call(path=%s, tid=%ld)", path.c_str(), tid); char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %d", CAPIO_REQUEST_CLOSE, tid, fd); + sprintf(req, "%04d %ld %s", CAPIO_REQUEST_CLOSE, tid, path.c_str()); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); } -inline off64_t rename_request(const long tid, const std::filesystem::path &old_path, - const std::filesystem::path &newpath) { +// block until server registers rename +inline void rename_request(const long tid, const std::filesystem::path &old_path, + const std::filesystem::path &newpath) { START_LOG(capio_syscall(SYS_gettid), "call(tid=%ld, old_path=%s, new_path=%s)", tid, old_path.c_str(), newpath.c_str()); char req[CAPIO_REQ_MAX_SIZE]; @@ -67,37 +80,17 @@ inline off64_t rename_request(const long tid, const std::filesystem::path &old_p buf_requests->write(req, CAPIO_REQ_MAX_SIZE); off64_t res; bufs_response->at(tid)->read(&res); - return res; } -inline off64_t create_request(const int fd, const std::filesystem::path &path, const long tid) { +// non blocking +inline void create_request(const int fd, const std::filesystem::path &path, const long tid) { START_LOG(capio_syscall(SYS_gettid), "call(fd=%ld, path=%s, tid=%ld)", fd, path.c_str(), tid); char req[CAPIO_REQ_MAX_SIZE]; sprintf(req, "%04d %ld %d %s", CAPIO_REQUEST_CREATE, tid, fd, path.c_str()); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res; - bufs_response->at(tid)->read(&res); - return res; -} - -inline off64_t create_exclusive_request(const int fd, const std::filesystem::path &path, - const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(fd=%ld, path=%s, tid=%ld)", fd, path.c_str(), tid); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %d %s", CAPIO_REQUEST_CREATE_EXCLUSIVE, tid, fd, path.c_str()); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res; - bufs_response->at(tid)->read(&res); - return res; -} - -inline void dup_request(const int old_fd, const int new_fd, const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(old_fd=%ld, new_fd=%ld, tid)", old_fd, new_fd, tid); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %d %d", CAPIO_REQUEST_DUP, tid, old_fd, new_fd); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); } +// non blocking inline void exit_group_request(const long tid) { START_LOG(capio_syscall(SYS_gettid), "call(tid=%ld)", tid); char req[CAPIO_REQ_MAX_SIZE]; @@ -105,18 +98,7 @@ inline void exit_group_request(const long tid) { buf_requests->write(req, CAPIO_REQ_MAX_SIZE); } -inline off64_t getdents_request(const int fd, const off64_t count, bool is64bit, const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(fd=%ld, count=%ld, is_64_bit=%s, tid=%ld)", fd, - count, is64bit ? "true" : "false", tid); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %d %ld", is64bit ? CAPIO_REQUEST_GETDENTS64 : CAPIO_REQUEST_GETDENTS, - tid, fd, count); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res; - bufs_response->at(tid)->read(&res); - return res; -} - +// non blocking inline void handshake_anonymous_request(const long tid, const long pid) { START_LOG(capio_syscall(SYS_gettid), "call(tid=%ld, pid=%ld)", tid, pid); char req[CAPIO_REQ_MAX_SIZE]; @@ -124,6 +106,7 @@ inline void handshake_anonymous_request(const long tid, const long pid) { buf_requests->write(req, CAPIO_REQ_MAX_SIZE); } +// non blocking inline void handshake_named_request(const long tid, const long pid, const std::string &app_name) { START_LOG(capio_syscall(SYS_gettid), "call(tid=%ld, pid=%ld, app_name=%s)", tid, pid, app_name.c_str()); @@ -132,43 +115,30 @@ inline void handshake_named_request(const long tid, const long pid, const std::s buf_requests->write(req, CAPIO_REQ_MAX_SIZE); } -inline CPStatResponse_t fstat_request(const int fd, const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(fd=%ld, tid=%ld)", fd, tid); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %d", CAPIO_REQUEST_FSTAT, tid, fd); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - // FIXME: these two reads don't work in multithreading - off64_t file_size; - bufs_response->at(tid)->read(&file_size); - off64_t is_dir; - bufs_response->at(tid)->read(&is_dir); - return {file_size, is_dir}; -} - -inline off64_t mkdir_request(const std::filesystem::path &path, const long tid) { +// non blocking +inline void mkdir_request(const std::filesystem::path &path, const long tid) { START_LOG(capio_syscall(SYS_gettid), "call(path=%s, tid=%ld)", path.c_str(), tid); char req[CAPIO_REQ_MAX_SIZE]; sprintf(req, "%04d %ld %s", CAPIO_REQUEST_MKDIR, tid, path.c_str()); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res; - bufs_response->at(tid)->read(&res); - return res; } -inline off64_t open_request(const int fd, const std::filesystem::path &path, const long tid) { +// block until open is possible +inline void open_request(const int fd, const std::filesystem::path &path, const long tid) { START_LOG(capio_syscall(SYS_gettid), "call(fd=%ld, path=%s, tid=%ld)", fd, path.c_str(), tid); char req[CAPIO_REQ_MAX_SIZE]; sprintf(req, "%04d %ld %d %s", CAPIO_REQUEST_OPEN, tid, fd, path.c_str()); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); off64_t res; bufs_response->at(tid)->read(&res); - return res; } -inline off64_t read_request(const int fd, const off64_t count, const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(fd=%ld, count=%ld, tid=%ld)", fd, count, tid); +// return amount of readable bytes +inline off64_t read_request(const std::filesystem::path &path, const off64_t count, + const long tid) { + START_LOG(capio_syscall(SYS_gettid), "call(path=%s, count=%ld, tid=%ld)", path, count, tid); char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %d %ld", CAPIO_REQUEST_READ, tid, fd, count); + sprintf(req, "%04d %s %ld %ld", CAPIO_REQUEST_READ, path.c_str(), tid, count); LOG("Sending read request %s", req); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); off64_t res; @@ -177,85 +147,28 @@ inline off64_t read_request(const int fd, const off64_t count, const long tid) { return res; } -inline off64_t seek_data_request(const int fd, const off64_t offset, const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(fd=%ld, offset=%ld, tid=%ld)", fd, offset, tid); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %d %zu", CAPIO_REQUEST_SEEK_DATA, tid, fd, offset); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res; - bufs_response->at(tid)->read(&res); - return res; -} - -inline off64_t seek_end_request(const int fd, const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(fd=%ld, tid=%ld)", fd, tid); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %d", CAPIO_REQUEST_SEEK_END, tid, fd); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res, is_dir; - bufs_response->at(tid)->read(&res); - bufs_response->at(tid)->read(&is_dir); - return res; -} - -inline off64_t seek_hole_request(const int fd, const off64_t offset, const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(fd=%ld, offset=%ld, tid=%ld)", fd, offset, tid); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %d %zu", CAPIO_REQUEST_SEEK_HOLE, tid, fd, offset); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res; - bufs_response->at(tid)->read(&res); - return res; -} - -inline off64_t seek_request(const int fd, const off64_t offset, const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(fd=%ld, offset=%ld, tid=%ld)", fd, offset, tid); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %d %zu", CAPIO_REQUEST_SEEK, tid, fd, offset); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res; - bufs_response->at(tid)->read(&res); - return res; -} - -inline CPStatResponse_t stat_request(const std::filesystem::path &path, const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(path=%s, tid=%ld)", path.c_str(), tid); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %s", CAPIO_REQUEST_STAT, tid, path.c_str()); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t file_size; - bufs_response->at(tid)->read(&file_size); - off64_t is_dir; - LOG("Received file size = %d", file_size); - bufs_response->at(tid)->read(&is_dir); - LOG("Received is_dir = %d", is_dir); - return {file_size, is_dir}; -} - -inline off64_t unlink_request(const std::filesystem::path &path, const long tid) { +// non blocking +inline void unlink_request(const std::filesystem::path &path, const long tid) { START_LOG(capio_syscall(SYS_gettid), "call(path=%s, tid=%ld)", path.c_str(), tid); char req[CAPIO_REQ_MAX_SIZE]; sprintf(req, "%04d %ld %s", CAPIO_REQUEST_UNLINK, tid, path.c_str()); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res; - bufs_response->at(tid)->read(&res); - return res; } -inline off64_t rmdir_request(const std::filesystem::path &dir_path, long tid) { +// non blocking +inline void rmdir_request(const std::filesystem::path &dir_path, long tid) { START_LOG(capio_syscall(SYS_gettid), "call(dir_path=%s, tid=%ld)", dir_path.c_str(), tid); char req[CAPIO_REQ_MAX_SIZE]; sprintf(req, "%04d %s %ld", CAPIO_REQUEST_RMDIR, dir_path.c_str(), tid); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res; - bufs_response->at(tid)->read(&res); - return res; } -inline void write_request(const int fd, const off64_t count, const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(fd=%ld, count=%ld, tid=%ld)", fd, count, tid); +// non blocking as write is not in the pre port of capio semantics +inline void write_request(const std::filesystem::path &path, const off64_t count, const long tid) { + START_LOG(capio_syscall(SYS_gettid), "call(path=%s, count=%ld, tid=%ld)", path.c_str(), count, + tid); char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %d %ld", CAPIO_REQUEST_WRITE, tid, fd, count); + sprintf(req, "%04d %ld %s %ld", CAPIO_REQUEST_WRITE, tid, path.c_str(), count); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); } diff --git a/src/server/capio_server.cpp b/src/server/capio_server.cpp index 35b625000..9bf495e2e 100644 --- a/src/server/capio_server.cpp +++ b/src/server/capio_server.cpp @@ -30,109 +30,13 @@ CSDataBufferMap_t data_buffers; #include "capio/env.hpp" #include "capio/logger.hpp" #include "capio/semaphore.hpp" -#include "utils/capio_file.hpp" #include "utils/common.hpp" #include "utils/env.hpp" -#include "utils/json.hpp" -#include "utils/metadata.hpp" -#include "utils/requests.hpp" - -using namespace simdjson; - -int n_servers; -// name of the node -char *node_name; - -/* - * For multithreading: - * tid -> pid*/ -CSPidsMap_T pids; - -// tid -> application name -CSAppsMap_t apps; - -// application name -> set of files already sent -CSFilesSentMap_t files_sent; - -/* - * pid -> pathname -> bool - * Different threads with the same pid are treated as a single writer - */ -CSWritersMap_t writers; - -CSClientsRemotePendingNFilesMap_t clients_remote_pending_nfiles; - -std::mutex nfiles_mutex; - -#include "handlers.hpp" -#include "utils/location.hpp" #include "utils/signals.hpp" -#include "remote/listener.hpp" - -static constexpr std::array build_request_handlers_table() { - std::array _request_handlers{0}; - - _request_handlers[CAPIO_REQUEST_ACCESS] = access_handler; - _request_handlers[CAPIO_REQUEST_CLONE] = clone_handler; - _request_handlers[CAPIO_REQUEST_CLOSE] = close_handler; - _request_handlers[CAPIO_REQUEST_CREATE] = create_handler; - _request_handlers[CAPIO_REQUEST_CREATE_EXCLUSIVE] = create_exclusive_handler; - _request_handlers[CAPIO_REQUEST_DUP] = dup_handler; - _request_handlers[CAPIO_REQUEST_EXIT_GROUP] = exit_group_handler; - _request_handlers[CAPIO_REQUEST_FSTAT] = fstat_handler; - _request_handlers[CAPIO_REQUEST_GETDENTS] = getdents_handler; - _request_handlers[CAPIO_REQUEST_GETDENTS64] = getdents_handler; - _request_handlers[CAPIO_REQUEST_HANDSHAKE_NAMED] = handshake_named_handler; - _request_handlers[CAPIO_REQUEST_HANDSHAKE_ANONYMOUS] = handshake_anonymous_handler; - _request_handlers[CAPIO_REQUEST_MKDIR] = mkdir_handler; - _request_handlers[CAPIO_REQUEST_OPEN] = open_handler; - _request_handlers[CAPIO_REQUEST_READ] = read_handler; - _request_handlers[CAPIO_REQUEST_RENAME] = rename_handler; - _request_handlers[CAPIO_REQUEST_RMDIR] = rmdir_handler; - _request_handlers[CAPIO_REQUEST_SEEK] = lseek_handler; - _request_handlers[CAPIO_REQUEST_SEEK_DATA] = seek_data_handler; - _request_handlers[CAPIO_REQUEST_SEEK_END] = seek_end_handler; - _request_handlers[CAPIO_REQUEST_SEEK_HOLE] = seek_hole_handler; - _request_handlers[CAPIO_REQUEST_STAT] = stat_handler; - _request_handlers[CAPIO_REQUEST_UNLINK] = unlink_handler; - _request_handlers[CAPIO_REQUEST_WRITE] = write_handler; - - return _request_handlers; -} - -[[noreturn]] void capio_server(Semaphore &internal_server_sem) { - static const std::array request_handlers = - build_request_handlers_table(); - - START_LOG(gettid(), "call()"); - - MPI_Comm_size(MPI_COMM_WORLD, &n_servers); - setup_signal_handlers(); - backend->handshake_servers(); - - create_dir(getpid(), get_capio_dir()); - - init_server(); - - internal_server_sem.unlock(); +#include "cl-engine/cl_engine.hpp" - auto str = std::unique_ptr(new char[CAPIO_REQ_MAX_SIZE]); - while (true) { - LOG(CAPIO_LOG_SERVER_REQUEST_START); - int code = read_next_request(str.get()); - if (code < 0 || code > CAPIO_NR_REQUESTS) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << "Received invalid code: " << code - << std::endl; - - ERR_EXIT("Error: received invalid request code"); - } - request_handlers[code](str.get()); - LOG(CAPIO_LOG_SERVER_REQUEST_END); - } -} - -int parseCLI(int argc, char **argv) { +std::string parseCLI(int argc, char **argv) { Logger *log; args::ArgumentParser parser(CAPIO_SERVER_ARG_PARSER_PRE, CAPIO_SERVER_ARG_PARSER_EPILOGUE); @@ -150,8 +54,6 @@ int parseCLI(int argc, char **argv) { CAPIO_SERVER_ARG_PARSER_CONFIG_OPT_HELP, {'c', "config"}); args::Flag noConfigFile(arguments, "no-config", CAPIO_SERVER_ARG_PARSER_CONFIG_NO_CONF_FILE_HELP, {"no-config"}); - args::ValueFlag backend_flag( - arguments, "backend", CAPIO_SERVER_ARG_PARSER_CONFIG_BACKEND_HELP, {'b', "backend"}); args::Flag continueOnErrorFlag(arguments, "continue-on-error", CAPIO_SERVER_ARG_PARSER_CONFIG_NCONTINUE_ON_ERROR_HELP, @@ -217,11 +119,10 @@ int parseCLI(int argc, char **argv) { #endif if (config) { - std::string token = args::get(config); - const std::filesystem::path &capio_dir = get_capio_dir(); + std::string token = args::get(config); std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << "parsing config file: " << token << std::endl; - parse_conf_file(token, capio_dir); + // TODO: pass config file path } else if (noConfigFile) { workflow_name = std::string_view(get_capio_workflow_name()); std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "skipping config file parsing." @@ -257,39 +158,29 @@ int parseCLI(int argc, char **argv) { } #endif - // Backend selection phase - std::string backend_name_str; - if (backend_flag) { - backend_name_str = args::get(backend_flag); - } - backend = select_backend(backend_name_str, argc, argv); - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << "server initialization completed!" << std::endl << std::flush; - return 0; + + if (config) { + return std::string(args::get(config).data()); + } + return ""; } int main(int argc, char **argv) { - Semaphore internal_server_sem(0); - std::cout << CAPIO_LOG_SERVER_BANNER; - parseCLI(argc, argv); + const std::string config_path = parseCLI(argc, argv); START_LOG(gettid(), "call()"); - open_files_location(); - shm_canary = new CapioShmCanary(workflow_name); - std::thread server_thread(capio_server, std::ref(internal_server_sem)); - LOG("capio_server thread started"); - std::thread remote_listener_thread(capio_remote_listener, std::ref(internal_server_sem)); - LOG("capio_remote_listener thread started."); - server_thread.join(); - remote_listener_thread.join(); + setup_signal_handlers(); + + cl_engine = new ClEngine(config_path); + cl_engine->start(); - delete backend; return 0; } \ No newline at end of file diff --git a/src/server/cl-engine/cl_engine.hpp b/src/server/cl-engine/cl_engine.hpp new file mode 100644 index 000000000..dd04e3a36 --- /dev/null +++ b/src/server/cl-engine/cl_engine.hpp @@ -0,0 +1,150 @@ +#ifndef CAPIO_CL_ENGINE_MAIN_HPP +#define CAPIO_CL_ENGINE_MAIN_HPP +#include "capio/requests.hpp" +#include "src/capio_file_locations.hpp" +#include "src/json_parser.hpp" + +class ClEngine { + private: + //// Variables + CapioFileLocations *locations; + std::array request_handlers; + + CSBufRequest_t *buf_requests; + CSBufResponse_t *bufs_response; + + //// Class methods + + static constexpr std::array build_request_handlers_table() { + std::array _request_handlers{0}; + + _request_handlers[CAPIO_REQUEST_ACCESS] = nullptr; + _request_handlers[CAPIO_REQUEST_CLONE] = nullptr; + _request_handlers[CAPIO_REQUEST_CLOSE] = nullptr; + _request_handlers[CAPIO_REQUEST_CREATE] = nullptr; + _request_handlers[CAPIO_REQUEST_EXIT_GROUP] = nullptr; + _request_handlers[CAPIO_REQUEST_GETDENTS] = nullptr; + _request_handlers[CAPIO_REQUEST_GETDENTS64] = nullptr; + _request_handlers[CAPIO_REQUEST_HANDSHAKE_NAMED] = nullptr; + _request_handlers[CAPIO_REQUEST_HANDSHAKE_ANONYMOUS] = nullptr; + _request_handlers[CAPIO_REQUEST_MKDIR] = nullptr; + _request_handlers[CAPIO_REQUEST_OPEN] = nullptr; + _request_handlers[CAPIO_REQUEST_READ] = nullptr; + _request_handlers[CAPIO_REQUEST_RENAME] = nullptr; + _request_handlers[CAPIO_REQUEST_RMDIR] = nullptr; + _request_handlers[CAPIO_REQUEST_SEEK] = nullptr; + _request_handlers[CAPIO_REQUEST_SEEK] = nullptr; + _request_handlers[CAPIO_REQUEST_STAT] = nullptr; + _request_handlers[CAPIO_REQUEST_UNLINK] = nullptr; + _request_handlers[CAPIO_REQUEST_WRITE] = nullptr; + + return _request_handlers; + } + + /** + * Read next incoming request into @param str and returns the request code + * @param str + * @return request code + */ + inline auto read_next_request(char *str) { + char req[CAPIO_REQ_MAX_SIZE]; + buf_requests->read(req); + START_LOG(gettid(), "call(req=%s)", req); + int code = -1; + auto [ptr, ec] = std::from_chars(req, req + 4, code); + if (ec == std::errc()) { + strcpy(str, ptr + 1); + } else { + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << "Received invalid code: " << code + << std::endl; + ERR_EXIT("Invalid request %d%s", code, ptr); + } + return code; + } + + public: + explicit ClEngine(const std::filesystem::path &json_path) { + START_LOG(gettid(), "call(path=%s)", json_path.c_str()); + + locations = JsonParser::parse(json_path); + request_handlers = build_request_handlers_table(); + + buf_requests = new CSBufRequest_t(SHM_COMM_CHAN_NAME, CAPIO_REQ_BUFF_CNT, + CAPIO_REQ_MAX_SIZE, workflow_name); + bufs_response = new CSBufResponse_t(); + locations->print(); + + std::cout << CAPIO_SERVER_CLI_LOG_SERVER + << " CL-Engine initialization completed. ready to listen for incoming requests" << std::endl; + } + + ~ClEngine() { + delete buf_requests; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_requests cleanup completed" + << std::endl; + + delete bufs_response; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_response cleanup completed" + << std::endl; + } + + /** + * Add a new response buffer for thread @param tid + * @param tid + * @return + */ + inline void register_new_client(long tid) const { + // TODO: replace numbers with constexpr + auto *p_buf_response = + new CircularBuffer(SHM_COMM_CHAN_NAME_RESP + std::to_string(tid), + CAPIO_REQ_BUFF_CNT, sizeof(off_t), workflow_name); + bufs_response->insert(std::make_pair(tid, p_buf_response)); + } + + /** + * Delete the response buffer associated with thread @param tid + * @param tid + * @return + */ + inline void remove_client(int tid) { + auto it_resp = bufs_response->find(tid); + if (it_resp != bufs_response->end()) { + delete it_resp->second; + bufs_response->erase(it_resp); + } + } + + /** + * Write offset to response buffer of process @param tid + * @param tid + * @param offset + * @return + */ + inline void reply_to_client(int tid, off64_t offset) { + START_LOG(gettid(), "call(tid=%d, offset=%ld)", tid, offset); + + return bufs_response->at(tid)->write(&offset); + } + + [[noreturn]] void start() { + START_LOG(gettid(), "call()"); + + auto str = std::unique_ptr(new char[CAPIO_REQ_MAX_SIZE]); + while (true) { + LOG(CAPIO_LOG_SERVER_REQUEST_START); + int code = read_next_request(str.get()); + if (code < 0 || code > CAPIO_NR_REQUESTS) { + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << "Received invalid code: " << code + << std::endl; + + ERR_EXIT("Error: received invalid request code"); + } + request_handlers[code](str.get()); + LOG(CAPIO_LOG_SERVER_REQUEST_END); + } + } +}; + +inline ClEngine *cl_engine; + +#endif // CAPIO_CL_ENGINE_MAIN_HPP diff --git a/src/server/cl-engine/src/capio_file_locations.hpp b/src/server/cl-engine/src/capio_file_locations.hpp new file mode 100644 index 000000000..6dcff4230 --- /dev/null +++ b/src/server/cl-engine/src/capio_file_locations.hpp @@ -0,0 +1,187 @@ +#ifndef CAPIO_ENGINE_HPP +#define CAPIO_ENGINE_HPP + +class CapioFileLocations { + private: + std::unordered_map, // Vector for producers + std::vector, // Vector for consumers + std::string, // commit rule + std::string, // fire_rule + bool, // permanent + bool, // exclude + bool, // is_file (if true yes otherwise it is a directory) + int, // commit on file number + long> // directory file count + > + _locations; + + static inline std::string truncate_last_n(const std::string &str, int n) { + return str.length() > n ? "[..] " + str.substr(str.length() - n) : str; + } + + public: + void print() const { + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + << "Composition of expected CAPIO FS: " << std::endl + << std::endl + << "|============================================================================" + "===============================================|" + << std::endl + << "|" << std::setw(124) << "|" << std::endl + << "| Parsed configuration file for workflow: \e[1;36m" << workflow_name + << std::setw(83 - workflow_name.length()) << "\e[0m |" << std::endl + << "|" << std::setw(124) << "|" << std::endl + << "|============================================================================" + "===============================================|" + << std::endl + << "|======|===================|===================|====================|========" + "============|============|===========|=========|" + << std::endl + << "| Kind | Filename | Producer step | Consumer step | " + "Commit Rule | Fire Rule | Permanent | Exclude |" + << std::endl + << "|======|===================|===================|====================|========" + "============|============|===========|=========|" + << std::endl; + for (auto itm : _locations) { + std::string name_trunc = truncate_last_n(itm.first, 12); + auto kind = std::get<5>(itm.second) ? "F" : "D"; + + std::cout << "| " << kind << " " + << "| " << name_trunc << std::setfill(' ') + << std::setw(20 - name_trunc.length()) << "| "; + + auto producers = std::get<0>(itm.second); + auto consumers = std::get<1>(itm.second); + auto rowCount = + producers.size() > consumers.size() ? producers.size() : consumers.size(); + + for (int i = 0; i <= rowCount; i++) { + std::string prod, cons; + if (i > 0) { + std::cout << "| | | "; + } + + if (i < producers.size()) { + auto prod1 = truncate_last_n(producers.at(i), 12); + std::cout << prod1 << std::setfill(' ') << std::setw(20 - prod1.length()) + << " | "; + } else { + std::cout << std::setfill(' ') << std::setw(20) << " | "; + } + + if (i < consumers.size()) { + auto cons1 = truncate_last_n(consumers.at(i), 12); + std::cout << " " << cons1 << std::setfill(' ') << std::setw(20 - cons1.length()) + << " | "; + } else { + std::cout << std::setfill(' ') << std::setw(21) << " | "; + } + + if (i == 0) { + std::string commit_rule = std::get<2>(itm.second), + fire_rule = std::get<3>(itm.second); + bool exclude = std::get<4>(itm.second), permanent = std::get<5>(itm.second); + + std::cout << " " << commit_rule << std::setfill(' ') + << std::setw(20 - commit_rule.length()) << " | " << fire_rule + << std::setfill(' ') << std::setw(13 - fire_rule.length()) << " | " + << " " << (permanent ? "YES" : "NO ") << " | " + << (exclude ? "YES" : "NO ") << " |" << std::endl; + } else { + std::cout << std::setfill(' ') << std::setw(20) << "|" << std::setfill(' ') + << std::setw(13) << "|" << std::setfill(' ') << std::setw(12) << "|" + << std::setfill(' ') << std::setw(10) << "|" << std::endl; + } + } + std::cout << "*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + "~~~~~~~" + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*" + << std::endl; + } + std::cout << std::endl; + }; + + inline void add(std::string &path, std::vector &producers, + std::vector &consumers, const std::string &commit_rule, + const std::string &fire_rule, bool permanent, bool exclude) { + _locations.emplace(path, std::make_tuple(producers, consumers, commit_rule, fire_rule, + permanent, exclude, true, -1, -1)); + } + + inline void newFile(const std::string &path) { + if (_locations.find(path) == _locations.end()) { + _locations.emplace( + path, std::make_tuple(std::vector(), std::vector(), + CAPIO_FILE_COMMITTED_ON_TERMINATION, CAPIO_FILE_MODE_UPDATE, + false, false, true, -1, -1)); + } + } + + inline void addProducer(const std::string &path, std::string &producer) { + this->newFile(path); + producer.erase(remove_if(producer.begin(), producer.end(), isspace), producer.end()); + std::get<0>(_locations.at(path)).emplace_back(producer); + } + + inline void addConsumer(const std::string &path, std::string &consumer) { + this->newFile(path); + consumer.erase(remove_if(consumer.begin(), consumer.end(), isspace), consumer.end()); + std::get<1>(_locations.at(path)).emplace_back(consumer); + } + + inline void setCommitRule(const std::string &path, const std::string &commit_rule) { + this->newFile(path); + std::get<2>(_locations.at(path)) = commit_rule; + } + + inline void setFireRule(const std::string &path, const std::string &fire_rule) { + this->newFile(path); + std::get<3>(_locations.at(path)) = fire_rule; + } + + inline void setPermanent(const std::string &path, bool value) { + this->newFile(path); + std::get<5>(_locations.at(path)) = value; + } + + inline void setExclude(const std::string &path, bool value) { + this->newFile(path); + std::get<4>(_locations.at(path)) = value; + } + + inline void setDirectory(const std::string &path) { + this->newFile(path); + std::get<5>(_locations.at(path)) = false; + } + + inline bool isDirectory(const std::string &path) { return !std::get<5>(_locations.at(path)); } + + inline void setFile(const std::string &path) { + this->newFile(path); + std::get<5>(_locations.at(path)) = true; + } + + inline bool isFile(const std::string &path) { return std::get<5>(_locations.at(path)); } + + inline void setCommitedNumber(const std::string &path, int num) { + this->newFile(path); + std::get<6>(_locations.at(path)) = num; + } + + inline void setDirectoryFileCount(const std::string &path, long num) { + this->newFile(path); + std::get<7>(_locations.at(path)) = num; + } + + inline void remove(const std::string &path) { _locations.erase(path); } + + // TODO: return vector + inline auto producers(const std::string &path) { return std::get<0>(_locations.at(path)); } + + // TODO: return vector + inline auto consumers(const std::string &path) { return std::get<1>(_locations.at(path)); } +}; + +#endif // CAPIO_ENGINE_HPP diff --git a/src/server/cl-engine/src/json_parser.hpp b/src/server/cl-engine/src/json_parser.hpp new file mode 100644 index 000000000..e690ebe13 --- /dev/null +++ b/src/server/cl-engine/src/json_parser.hpp @@ -0,0 +1,244 @@ +#ifndef JSON_PARSER_HPP +#define JSON_PARSER_HPP +#include + +class JsonParser { + public: + static CapioFileLocations *parse(const std::filesystem::path &source) { + auto locations = new CapioFileLocations(); + auto capio_dir = get_capio_dir(); + + START_LOG(gettid(), "call(config_file='%s', capio_dir='%s')", source.c_str(), + capio_dir.c_str()); + + locations->newFile(get_capio_dir()); + if (source.empty()) { + return locations; + } + + simdjson::ondemand::parser parser; + simdjson::padded_string json; + simdjson::ondemand::document entries; + simdjson::ondemand::array input_stream, output_stream, streaming, permanent_files; + simdjson::error_code error; + + try { + json = simdjson::padded_string::load(source.c_str()); + } catch (const simdjson::simdjson_error &e) { + std::cerr << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + << "Exception thrown while opening config file: " << e.what() << std::endl; + LOG("Exception thrown while opening config file: %s", e.what()); + ERR_EXIT("Exception thrown while opening config file: %s", e.what()); + } + + entries = parser.iterate(json); + std::string_view wf_name; + error = entries["name"].get_string().get(wf_name); + if (error) { + ERR_EXIT("Error: workflow name is mandatory"); + } + workflow_name = std::string(wf_name); + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + << "Parsing configuration for workflow: " << workflow_name << std::endl; + LOG("Parsing configuration for workflow: %s", std::string(workflow_name).c_str()); + + auto io_graph = entries["IO_Graph"]; + + for (auto app : io_graph) { + std::string_view app_name; + error = app["name"].get_string().get(app_name); + if (error) { + ERR_EXIT("Error: app name is mandatory"); + } + + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Parsing config for app " << app_name + << std::endl; + LOG("Parsing config for app %s", std::string(app_name).c_str()); + + if (app["input_stream"].get_array().get(input_stream)) { + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + << "No input_stream section found for app " << app_name << std::endl; + ERR_EXIT("No input_stream section found for app %s", std::string(app_name).c_str()); + } else { + for (auto itm : input_stream) { + std::filesystem::path file(itm.get_string().take_value()); + std::string appname(app_name); + locations->newFile(file); + locations->addConsumer(file, appname); + } + + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + << "Completed input_stream parsing for app: " << app_name << std::endl; + LOG("Completed input_stream parsing for app: %s", std::string(app_name).c_str()); + } + + if (app["output_stream"].get_array().get(output_stream)) { + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + << "No output_stream section found for app " << app_name << std::endl; + ERR_EXIT("No output_stream section found for app %s", + std::string(app_name).c_str()); + } else { + for (auto itm : output_stream) { + std::filesystem::path file(itm.get_string().take_value()); + std::string appname(app_name); + locations->newFile(file); + locations->addConsumer(file, appname); + } + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + << "Completed output_stream parsing for app: " << app_name << std::endl; + LOG("Completed output_stream parsing for app: %s", std::string(app_name).c_str()); + } + + // PARSING STREAMING FILES + if (app["streaming"].get_array().get(streaming)) { + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING + << "No streaming section found for app: " << app_name << std::endl; + LOG("No streaming section found for app: %s", std::string(app_name).c_str()); + } else { + LOG("Began parsing streaming section for app %s", std::string(app_name).c_str()); + for (auto file : streaming) { + std::string_view committed, mode, commit_rule; + std::vector streaming_names; + long int n_close = -1; + long n_files, batch_size; + + simdjson::ondemand::array name = file["name"].get_array(); + if (name.is_empty()) { + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + << "error: name for application is mandatory" << std::endl; + ERR_EXIT("error: name for application is mandatory"); + } + for (auto item : name) { + std::string_view elem = item.get_string().value(); + streaming_names.emplace_back(elem); + LOG("Found name: %s", std::string(elem).c_str()); + } + + // PARSING COMMITTED + error = file["committed"].get_string().get(committed); + if (error) { + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + << "commit rule is mandatory in streaming section" << std::endl; + ERR_EXIT("error commit rule is mandatory in streaming section"); + } else { + auto pos = committed.find(':'); + if (pos != std::string::npos) { + commit_rule = committed.substr(0, pos); + if (commit_rule != CAPIO_FILE_COMMITTED_ON_CLOSE) { + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << "commit rule " + << commit_rule << std::endl; + ERR_EXIT("error commit rule: %s", std::string(commit_rule).c_str()); + } + + std::string n_close_str(committed.substr(pos + 1, committed.length())); + + if (!is_int(n_close_str)) { + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + << "commit rule on_close invalid number" << std::endl; + ERR_EXIT("error commit rule on_close invalid number: !is_int()"); + } + n_close = std::stol(n_close_str); + } else { + commit_rule = committed; + } + } + LOG("Committed: %s", std::string(committed).c_str()); + // END PARSING COMMITTED + + error = file["mode"].get_string().get(mode); + if (error) { + mode = CAPIO_FILE_MODE_UPDATE; + } + LOG("Mode: %s", std::string(mode).c_str()); + + error = file["n_files"].get_int64().get(n_files); + if (error) { + n_files = -1; + } + LOG("n_files: %d", n_files); + + error = file["batch_size"].get_int64().get(batch_size); + if (error) { + batch_size = 0; + } + LOG("batch_size: %d", batch_size); + for (auto path : streaming_names) { + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + << "Updating metadata for path: " << path << std::endl; + if (path.is_relative()) { + path = (capio_dir / path).lexically_normal(); + } + LOG("path: %s", path.c_str()); + + std::size_t pos = path.native().find('*'); + + // TODO: check for globs + std::string commit(commit_rule), firerule(mode); + if (n_files != -1) { + locations->setDirectory(path); + locations->setDirectoryFileCount(path, n_files); + } + locations->setCommitRule(path, commit); + locations->setFireRule(path, firerule); + locations->setCommitedNumber(path, n_close); + } + } + + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + << "completed parsing of streaming section for app: " << app_name + << std::endl; + LOG("completed parsing of streaming section for app: %s", + std::string(app_name).c_str()); + } // END PARSING STREAMING FILES + + } // END OF APP MAIN LOOPS + LOG("Completed parsing of io_graph app main loops"); + + long int batch_size = 0; + if (entries["permanent"].get_array().get(permanent_files)) { // PARSING PERMANENT FILES + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING + << "No permanent section found for workflow: " << workflow_name << std::endl; + LOG("No permanent section found for workflow: %s", std::string(workflow_name).c_str()); + } else { + for (auto file : permanent_files) { + std::string_view name; + error = file.get_string().get(name); + if (error) { + ERR_EXIT("error name for permanent section is mandatory"); + } + LOG("Permanent name: %s", std::string(name).c_str()); + + std::filesystem::path path(name); + + if (path.is_relative()) { + path = (capio_dir / path).lexically_normal(); + } + // NOTE: here there was a copy of the previous structured block. + // pretty much sure it is a bug, but it might be wanted... + + std::size_t pos = path.native().find('*'); + // TODO: check for globs + + locations->setPermanent(name.data(), true); + } + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Completed parsing of permanent files" + << std::endl; + LOG("Completed parsing of permanent files"); + } // END PARSING PERMANENT FILES + + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Completed parsing of io_graph" + << std::endl; + LOG("Completed parsing of io_graph"); + + auto home_node_policies = entries["home_node_policy"].error(); + if (!home_node_policies) { + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING + << "Warning: capio does not support home node policies yet! skipping section " + << std::endl; + } + + return locations; + } +}; + +#endif // JSON_PARSER_HPP diff --git a/src/server/handlers.hpp b/src/server/handlers.hpp deleted file mode 100644 index d8a7be5fc..000000000 --- a/src/server/handlers.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_HPP -#define CAPIO_SERVER_HANDLERS_HPP - -// TODO: remove these headers from here and ensure all handlers are -// self-contained - -#include "handlers/access.hpp" -#include "handlers/clone.hpp" -#include "handlers/close.hpp" -#include "handlers/common.hpp" -#include "handlers/dup.hpp" -#include "handlers/exig.hpp" -#include "handlers/getdents.hpp" -#include "handlers/handshake.hpp" -#include "handlers/mkdir.hpp" -#include "handlers/open.hpp" -#include "handlers/read.hpp" -#include "handlers/rename.hpp" -#include "handlers/rmdir.hpp" -#include "handlers/seek.hpp" -#include "handlers/stat.hpp" -#include "handlers/unlink.hpp" -#include "handlers/write.hpp" - -#endif // CAPIO_SERVER_HANDLERS_HPP diff --git a/src/server/handlers/access.hpp b/src/server/handlers/access.hpp deleted file mode 100644 index 7810453e8..000000000 --- a/src/server/handlers/access.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_ACCESS_HPP -#define CAPIO_SERVER_HANDLERS_ACCESS_HPP - -#include "utils/location.hpp" - -void access_handler(const char *const str) { - START_LOG(gettid(), "call(str=%s)", str); - long tid; - char path[PATH_MAX]; - sscanf(str, "%ld %s", &tid, path); - write_response(tid, get_file_location_opt(path) ? 0 : -1); -} - -#endif // CAPIO_SERVER_HANDLERS_ACCESS_HPP diff --git a/src/server/handlers/clone.hpp b/src/server/handlers/clone.hpp deleted file mode 100644 index bbdc550d3..000000000 --- a/src/server/handlers/clone.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_CLONE_HPP -#define CAPIO_SERVER_HANDLERS_CLONE_HPP - -#include "common.hpp" - -// TODO: caching info -inline void handle_clone(pid_t parent_tid, pid_t child_tid) { - START_LOG(gettid(), "call(parent_tid=%d, child_tid=%d)", parent_tid, child_tid); - init_process(child_tid); - clone_capio_file(parent_tid, child_tid); - int ppid = pids[parent_tid]; - int new_pid = pids[child_tid]; - if (ppid != new_pid) { - writers[child_tid] = writers[parent_tid]; - for (auto &p : writers[child_tid]) { - p.second = false; - } - } -} - -void clone_handler(const char *const str) { - pid_t parent_tid, child_tid; - sscanf(str, "%d %d", &parent_tid, &child_tid); - handle_clone(parent_tid, child_tid); -} - -#endif // CAPIO_SERVER_HANDLERS_CLONE_HPP diff --git a/src/server/handlers/close.hpp b/src/server/handlers/close.hpp deleted file mode 100644 index 9d36d382f..000000000 --- a/src/server/handlers/close.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_CLOSE_HPP -#define CAPIO_SERVER_HANDLERS_CLOSE_HPP - -#include "read.hpp" - -#include "utils/filesystem.hpp" - -inline void handle_pending_remote_nfiles(const std::filesystem::path &path) { - START_LOG(gettid(), "call(%s)", path.c_str()); - - std::lock_guard lg(nfiles_mutex); - - for (auto &p : clients_remote_pending_nfiles) { - - auto &[app, app_pending_nfiles] = p; - LOG("Handling pending files for app: %s", app.c_str()); - - for (const auto &[prefix, batch_size, dest, files_path, sem] : app_pending_nfiles) { - LOG("Expanded iterator: prefix=%s, batch_size=%ld, dest=%s [others missing....]", - prefix.c_str(), batch_size, dest.c_str()); - auto &files = files_sent[app]; - LOG("Obtained files for app %s", app.c_str()); - auto file_location_opt = get_file_location_opt(path); - LOG("Handling files for prefix: %s. batch size is: %d", prefix.c_str(), batch_size); - if (files.find(path) == files.end() && file_location_opt && - std::get<0>(file_location_opt->get()) == std::string(node_name) && - path.native().compare(0, prefix.native().length(), prefix) == 0) { - files_path->push_back(path); - files.insert(path); - LOG("Inserted file %s in batch", path.c_str()); - if (files_path->size() == batch_size) { - LOG("Waking up thread to handle batch, as batch is full and can be served"); - sem->unlock(); - } - } - } - } -} - -inline void handle_close(int tid, int fd) { - START_LOG(gettid(), "call(tid=%d, fd=%d)", tid, fd); - - const std::filesystem::path &path = get_capio_file_path(tid, fd); - if (path.empty()) { // avoid to try to close a file that does not exists - // (example: try to close() on a dir - LOG("Path is empty. might be a directory. returning"); - return; - } - - CapioFile &c_file = get_capio_file(path); - c_file.close(); - LOG("File with path %s was closed", path.c_str()); - - if (c_file.get_committed() == CAPIO_FILE_COMMITTED_ON_CLOSE && c_file.is_closed()) { - LOG("Capio File %s is closed and commit rule is on_close. setting it to complete and " - "starting batch handling", - path.c_str()); - c_file.set_complete(); - handle_pending_remote_nfiles(path); - c_file.commit(); - } - - if (c_file.is_deletable()) { - LOG("file %s is deletable from CAPIO_SERVER", path.c_str()); - delete_capio_file(path); - delete_from_files_location(path); - } else { - LOG("Deleting capio file %s from tid=%d", path.c_str(), tid); - delete_capio_file_from_tid(tid, fd); - } -} - -void close_handler(const char *str) { - int tid, fd; - sscanf(str, "%d %d", &tid, &fd); - handle_close(tid, fd); -} - -#endif // CAPIO_SERVER_HANDLERS_CLOSE_HPP diff --git a/src/server/handlers/common.hpp b/src/server/handlers/common.hpp deleted file mode 100644 index 6264fbdea..000000000 --- a/src/server/handlers/common.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_COMMON_HPP -#define CAPIO_SERVER_HANDLERS_COMMON_HPP - -inline void init_process(int tid) { - START_LOG(gettid(), "call(%d)", tid); - - if (data_buffers.find(tid) == data_buffers.end()) { - register_listener(tid); - - data_buffers.insert( - {tid, - {new SPSCQueue(SHM_SPSC_PREFIX_WRITE + std::to_string(tid), get_cache_lines(), - get_cache_line_size(), workflow_name), - new SPSCQueue(SHM_SPSC_PREFIX_READ + std::to_string(tid), get_cache_lines(), - get_cache_line_size(), workflow_name)}}); - } -} - -/* - * Unlink resources in shared memory of the thread with thread id = tid - * To be called only when the client thread terminates - */ - -void free_resources(int tid) { - START_LOG(gettid(), "call(%d)", tid); - std::string sem_write_shm_name; - remove_listener(tid); - - auto it = data_buffers.find(tid); - if (it != data_buffers.end()) { - delete it->second.first; - delete it->second.second; - data_buffers.erase(it); - } -} - -#endif // CAPIO_SERVER_HANDLERS_COMMON_HPP diff --git a/src/server/handlers/dup.hpp b/src/server/handlers/dup.hpp deleted file mode 100644 index c40923ba4..000000000 --- a/src/server/handlers/dup.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_DUP_HPP -#define CAPIO_SERVER_HANDLERS_DUP_HPP - -#include "utils/metadata.hpp" - -void dup_handler(const char *const str) { - int tid, old_fd, new_fd; - sscanf(str, "%d %d %d", &tid, &old_fd, &new_fd); - START_LOG(gettid(), "call(tid=%d, old_fd=%d, new_fd=%d)", tid, old_fd, new_fd); - if (old_fd != new_fd) { - dup_capio_file(tid, old_fd, new_fd); - } -} - -#endif // CAPIO_SERVER_HANDLERS_DUP_HPP diff --git a/src/server/handlers/exig.hpp b/src/server/handlers/exig.hpp deleted file mode 100644 index 4f76a76ab..000000000 --- a/src/server/handlers/exig.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_EXITG_HPP -#define CAPIO_SERVER_HANDLERS_EXITG_HPP - -inline void handle_exit_group(int tid) { - START_LOG(gettid(), "call(tid=%d)", tid); - - LOG("retrieving pid for process with tid = %d", tid); - int pid = pids[tid]; - LOG("retrieving files from writers for process with pid = %d", pid); - auto files = writers[pid]; - for (auto &pair : files) { - std::string path = pair.first; - LOG("Path %s found. handling? %s", path.c_str(), pair.second ? "yes" : "no"); - if (pair.second) { - LOG("Handling file %s", path.c_str()); - auto it_conf = metadata_conf.find(path); - if (it_conf == metadata_conf.end() || - std::get<0>(it_conf->second) == CAPIO_FILE_COMMITTED_ON_TERMINATION || - std::get<0>(it_conf->second).empty()) { - CapioFile &c_file = get_capio_file(path.c_str()); - if (c_file.is_dir()) { - LOG("file %s is dir", path.c_str()); - long int n_committed = c_file.n_files_expected; - if (n_committed <= c_file.n_files) { - LOG("Setting file %s to complete", path.c_str()); - c_file.set_complete(); - } - } else { - LOG("Setting file %s to complete", path.c_str()); - c_file.set_complete(); - c_file.commit(); - } - } - } - } - - for (auto &fd : get_capio_fds_for_tid(tid)) { - handle_close(tid, fd); - } - free_resources(tid); -} - -void exit_group_handler(const char *const str) { - int tid; - sscanf(str, "%d", &tid); - handle_exit_group(tid); -} - -#endif // CAPIO_SERVER_HANDLERS_EXITG_HPP diff --git a/src/server/handlers/getdents.hpp b/src/server/handlers/getdents.hpp deleted file mode 100644 index 5e2c26970..000000000 --- a/src/server/handlers/getdents.hpp +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef CAPIO_GETDENTS_HPP -#define CAPIO_GETDENTS_HPP - -#include - -#include "remote/backend.hpp" -#include "remote/requests.hpp" - -#include "utils/location.hpp" -#include "utils/metadata.hpp" -#include "utils/producer.hpp" - -inline void request_remote_getdents(int tid, int fd, off64_t count) { - START_LOG(gettid(), "call(tid=%d, fd=%d, count=%ld)", tid, fd, count); - - const std::filesystem::path &path = get_capio_file_path(tid, fd); - CapioFile &c_file = get_capio_file(path); - off64_t offset = get_capio_file_offset(tid, fd); - off64_t end_of_read = offset + count; - off64_t end_of_sector = c_file.get_sector_end(offset); - - if (c_file.is_complete() && - (end_of_read <= end_of_sector || - (end_of_sector == -1 ? 0 : end_of_sector) == c_file.real_file_size)) { - LOG("Handling local read"); - send_dirent_to_client(tid, fd, c_file, offset, count); - } else if (end_of_read <= end_of_sector) { - LOG("?"); - c_file.create_buffer_if_needed(path, false); - send_data_to_client(tid, fd, c_file.get_buffer(), offset, count); - } else { - LOG("Delegating to backend remote read"); - handle_remote_read_request(tid, fd, count, true); - } -} - -inline void handle_getdents(int tid, int fd, long int count) { - START_LOG(gettid(), "call(tid=%d, fd=%d, count=%ld)", tid, fd, count); - - const std::filesystem::path &path = get_capio_file_path(tid, fd); - const std::filesystem::path &capio_dir = get_capio_dir(); - bool is_prod = is_producer(tid, path); - auto file_location_opt = get_file_location_opt(path); - - if (!file_location_opt && !is_prod) { - std::thread t([tid, fd, count] { - START_LOG(gettid(), "call(tid=%d, fd=%d, count=%ld)", tid, fd, count); - - const std::filesystem::path &path_to_check = get_capio_file_path(tid, fd); - loop_load_file_location(path_to_check); - - if (strcmp(std::get<0>(get_file_location(path_to_check)), node_name) == 0) { - handle_getdents(tid, fd, count); - } else { - const CapioFile &c_file = get_capio_file(path_to_check); - auto remote_app = apps.find(tid); - if (!c_file.is_complete() && remote_app != apps.end()) { - long int pos = match_globs(path_to_check); - if (pos != -1) { - const std::string &remote_app_name = remote_app->second; - std::string prefix = std::get<0>(metadata_conf_globs[pos]); - off64_t batch_size = std::get<5>(metadata_conf_globs[pos]); - if (batch_size > 0) { - handle_remote_read_batch_request(tid, fd, count, remote_app_name, - prefix, batch_size, true); - return; - } - } - } - request_remote_getdents(tid, fd, count); - } - }); - t.detach(); - } else if (is_prod || strcmp(std::get<0>(file_location_opt->get()), node_name) == 0 || - capio_dir == path) { - CapioFile &c_file = get_capio_file(path); - off64_t offset = get_capio_file_offset(tid, fd); - send_dirent_to_client(tid, fd, c_file, offset, count); - } else { - LOG("File is remote"); - CapioFile &c_file = get_capio_file(path); - auto it = apps.find(tid); - if (!c_file.is_complete() && it != apps.end()) { - LOG("File not complete"); - const std::string &app_name = it->second; - long int pos = match_globs(path); - if (pos != -1) { - LOG("Glob matched"); - std::string prefix = std::get<0>(metadata_conf_globs[pos]); - off64_t batch_size = std::get<5>(metadata_conf_globs[pos]); - if (batch_size > 0) { - LOG("Handling batch file"); - handle_remote_read_batch_request(tid, fd, count, app_name, prefix, batch_size, - true); - return; - } - } - } - LOG("Delegating to backend remote read"); - request_remote_getdents(tid, fd, count); - } -} - -void getdents_handler(const char *const str) { - int tid, fd; - off64_t count; - sscanf(str, "%d %d %ld", &tid, &fd, &count); - handle_getdents(tid, fd, count); -} - -#endif // CAPIO_GETDENTS_HPP diff --git a/src/server/handlers/handshake.hpp b/src/server/handlers/handshake.hpp deleted file mode 100644 index eb1f463e4..000000000 --- a/src/server/handlers/handshake.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_HANDSHAKE_HPP -#define CAPIO_SERVER_HANDLERS_HANDSHAKE_HPP - -inline void handle_handshake_anonymous(int tid, int pid) { - START_LOG(gettid(), "call(tid=%d, pid=%d)", tid, pid); - pids[tid] = pid; - init_process(tid); -} - -inline void handle_handshake_named(int tid, int pid, const char *app_name) { - START_LOG(gettid(), "call(tid=%d, pid=%d, app_name=%s)", tid, pid, app_name); - apps[tid] = app_name; - pids[tid] = pid; - init_process(tid); -} - -void handshake_anonymous_handler(const char *const str) { - int tid, pid; - sscanf(str, "%d %d", &tid, &pid); - handle_handshake_anonymous(tid, pid); -} - -void handshake_named_handler(const char *const str) { - int tid, pid; - char app_name[1024]; - sscanf(str, "%d %d %s", &tid, &pid, app_name); - handle_handshake_named(tid, pid, app_name); -} - -#endif // CAPIO_SERVER_HANDLERS_HANDSHAKE_HPP diff --git a/src/server/handlers/mkdir.hpp b/src/server/handlers/mkdir.hpp deleted file mode 100644 index b0f211e5f..000000000 --- a/src/server/handlers/mkdir.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_MKDIR_HPP -#define CAPIO_SERVER_HANDLERS_MKDIR_HPP - -void mkdir_handler(const char *const str) { - pid_t tid; - char pathname[PATH_MAX]; - sscanf(str, "%d %s", &tid, pathname); - write_response(tid, create_dir(tid, pathname)); -} - -#endif // CAPIO_SERVER_HANDLERS_MKDIR_HPP diff --git a/src/server/handlers/open.hpp b/src/server/handlers/open.hpp deleted file mode 100644 index 3760c732f..000000000 --- a/src/server/handlers/open.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_OPEN_HPP -#define CAPIO_SERVER_HANDLERS_OPEN_HPP - -#include "utils/filesystem.hpp" -#include "utils/location.hpp" -#include "utils/metadata.hpp" - -inline void update_file_metadata(const std::filesystem::path &path, int tid, int fd, bool is_creat, - off64_t offset) { - START_LOG(gettid(), "call(path=%s, client_tid=%d fd=%d, is_creat=%s, offset=%ld)", path.c_str(), - tid, fd, is_creat ? "true" : "false", offset); - - // TODO: check the size that the user wrote in the configuration file - //*caching_info[tid].second += 2; - auto c_file_opt = get_capio_file_opt(path); - CapioFile &c_file = - (c_file_opt) ? c_file_opt->get() : create_capio_file(path, false, get_file_initial_size()); - add_capio_file_to_tid(tid, fd, path, offset); - int pid = pids[tid]; - auto it_files = writers.find(pid); - if (it_files != writers.end()) { - if (it_files->second.find(path) == it_files->second.end()) { - LOG("setting writers[%ld][%s]=false 1", pid, path.c_str()); - writers[pid][path] = false; - } - } else { - LOG("setting writers[%ld][%s]=true", pid, path.c_str()); - writers[pid][path] = true; - } - if (c_file.first_write && is_creat) { - c_file.first_write = false; - write_file_location(path); - update_dir(tid, path); - } -} - -inline void handle_create(int tid, int fd, const std::filesystem::path &path) { - START_LOG(gettid(), "call(tid=%d, fd=%d, path_cstr=%s)", tid, fd, path.c_str()); - - bool is_creat = !(get_file_location_opt(path) || load_file_location(path)); - update_file_metadata(path, tid, fd, is_creat, 0); - write_response(tid, 0); -} - -inline void handle_create_exclusive(int tid, int fd, const std::filesystem::path &path) { - START_LOG(gettid(), "call(tid=%d, fd=%d, path_cstr=%s)", tid, fd, path.c_str()); - - if (get_capio_file_opt(path)) { - write_response(tid, 1); - } else { - write_response(tid, 0); - update_file_metadata(path, tid, fd, true, 0); - } -} - -inline void handle_open(int tid, int fd, const std::filesystem::path &path) { - START_LOG(gettid(), "call(tid=%d, fd=%d, path_cstr=%s)", tid, fd, path.c_str()); - - // it is important that check_files_location is the last because is the - // slowest (short circuit evaluation) - if (get_file_location_opt(path) || metadata_conf.find(path) != metadata_conf.end() || - match_globs(path) != -1 || load_file_location(path)) { - update_file_metadata(path, tid, fd, false, 0); - } else { - write_response(tid, 1); - } - write_response(tid, 0); -} - -void create_handler(const char *const str) { - int tid, fd; - char path[PATH_MAX]; - sscanf(str, "%d %d %s", &tid, &fd, path); - handle_create(tid, fd, path); -} - -void create_exclusive_handler(const char *const str) { - int tid, fd; - char path[PATH_MAX]; - sscanf(str, "%d %d %s", &tid, &fd, path); - handle_create_exclusive(tid, fd, path); -} - -void open_handler(const char *const str) { - int tid, fd; - char path[PATH_MAX]; - sscanf(str, "%d %d %s", &tid, &fd, path); - handle_open(tid, fd, path); -} - -#endif // CAPIO_SERVER_HANDLERS_OPEN_HPP diff --git a/src/server/handlers/read.hpp b/src/server/handlers/read.hpp deleted file mode 100644 index d4f577156..000000000 --- a/src/server/handlers/read.hpp +++ /dev/null @@ -1,187 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_READ_HPP -#define CAPIO_SERVER_HANDLERS_READ_HPP - -#include -#include - -#include "remote/backend.hpp" -#include "remote/requests.hpp" - -#include "utils/location.hpp" -#include "utils/metadata.hpp" -#include "utils/producer.hpp" - -std::mutex local_read_mutex; - -inline void handle_pending_read(int tid, int fd, long int process_offset, long int count) { - START_LOG(gettid(), "call(tid=%d, fd=%d, process_offset=%ld, count=%ld)", tid, fd, - process_offset, count); - - const std::filesystem::path &path = get_capio_file_path(tid, fd); - CapioFile &c_file = get_capio_file(path); - off64_t end_of_sector = c_file.get_sector_end(process_offset); - off64_t end_of_read = process_offset + count; - - off64_t bytes_read; - if (end_of_sector > end_of_read) { - bytes_read = count; - } else { - bytes_read = end_of_sector - process_offset; - } - - c_file.create_buffer_if_needed(path, false); - send_data_to_client(tid, fd, c_file.get_buffer(), process_offset, bytes_read); - - // TODO: check if the file was moved to the disk -} - -inline void handle_local_read(int tid, int fd, off64_t count, bool is_prod) { - START_LOG(gettid(), "call(tid=%d, fd=%d, count=%ld, is_prod=%s)", tid, fd, count, - is_prod ? "true" : "false"); - - const std::lock_guard lg(local_read_mutex); - const std::filesystem::path &path = get_capio_file_path(tid, fd); - CapioFile &c_file = get_capio_file(path); - off64_t process_offset = get_capio_file_offset(tid, fd); - int pid = pids[tid]; - bool writer = writers[pid][path]; - off64_t end_of_sector = c_file.get_sector_end(process_offset); - off64_t end_of_read = process_offset + count; - std::string_view mode = c_file.get_mode(); - if (mode == CAPIO_FILE_MODE_UPDATE && !c_file.is_complete() && !writer && !is_prod) { - // wait for file to be completed and then do what is done inside handle pending read - LOG("Starting async thread to wait for file availability"); - std::thread t([&c_file, tid, fd, count, process_offset] { - c_file.wait_for_completion(); - handle_pending_read(tid, fd, process_offset, count); - }); - t.detach(); - } else if (end_of_read > end_of_sector) { - if (!is_prod && !writer && !c_file.is_complete()) { - LOG("Mode is NO_UPDATE. awaiting for data on separate thread before sending it to " - "client"); - // here if mode is NO_UPDATE, wait for data and then send it - std::thread t([&c_file, tid, fd, count, process_offset] { - c_file.wait_for_data(process_offset + count); - handle_pending_read(tid, fd, process_offset, count); - }); - t.detach(); - - } else { - LOG("Data is available."); - if (end_of_sector == -1) { - LOG("End of sector is -1. returning process_offset without serving data"); - write_response(tid, process_offset); - return; - } - c_file.create_buffer_if_needed(path, false); - send_data_to_client(tid, fd, c_file.get_buffer(), process_offset, - end_of_sector - process_offset); - } - } else { - c_file.create_buffer_if_needed(path, false); - send_data_to_client(tid, fd, c_file.get_buffer(), process_offset, count); - } -} - -inline void request_remote_read(int tid, int fd, off64_t count) { - START_LOG(gettid(), "call(tid=%d, fd=%d, count=%ld)", tid, fd, count); - - const std::filesystem::path &path = get_capio_file_path(tid, fd); - CapioFile &c_file = get_capio_file(path); - off64_t offset = get_capio_file_offset(tid, fd); - off64_t end_of_read = offset + count; - off64_t end_of_sector = c_file.get_sector_end(offset); - - if (c_file.is_complete() && - (end_of_read <= end_of_sector || - (end_of_sector == -1 ? 0 : end_of_sector) == c_file.real_file_size)) { - LOG("Handling local read"); - handle_local_read(tid, fd, count, true); - } else if (end_of_read <= end_of_sector) { - LOG("Data is present locally and can be served to client"); - c_file.create_buffer_if_needed(path, false); - send_data_to_client(tid, fd, c_file.get_buffer(), offset, count); - } else { - LOG("Delegating to backend remote read"); - handle_remote_read_request(tid, fd, count, false); - } -} - -void wait_for_file(const std::filesystem::path &path, int tid, int fd, off64_t count) { - START_LOG(gettid(), "call(path=%s, tid=%d, fd=%d, count=%ld)", path.c_str(), tid, fd, count); - - loop_load_file_location(path); - - // check if the file is local or remote - if (strcmp(std::get<0>(get_file_location(path)), node_name) == 0) { - handle_local_read(tid, fd, count, false); - } else { - const CapioFile &c_file = get_capio_file(path); - auto remote_app = apps.find(tid); - if (!c_file.is_complete() && remote_app != apps.end()) { - long int pos = match_globs(path); - if (pos != -1) { - const std::string &remote_app_name = remote_app->second; - std::string prefix = std::get<0>(metadata_conf_globs[pos]); - off64_t batch_size = std::get<5>(metadata_conf_globs[pos]); - if (batch_size > 0) { - handle_remote_read_batch_request(tid, fd, count, remote_app_name, prefix, - batch_size, false); - return; - } - } - } - request_remote_read(tid, fd, count); - } -} - -inline void handle_read(int tid, int fd, off64_t count) { - START_LOG(gettid(), "call(tid=%d, fd=%d, count=%ld)", tid, fd, count); - - const std::filesystem::path &path = get_capio_file_path(tid, fd); - const std::filesystem::path &capio_dir = get_capio_dir(); - bool is_prod = is_producer(tid, path); - auto file_location_opt = get_file_location_opt(path); - if (!file_location_opt && !is_prod) { - LOG("Starting thread to wait for file creation"); - // launch a thread that checks when the file is created - std::thread t(wait_for_file, path, tid, fd, count); - t.detach(); - } else if (is_prod || strcmp(std::get<0>(file_location_opt->get()), node_name) == 0 || - capio_dir == path) { - LOG("File is local. handling local read"); - handle_local_read(tid, fd, count, is_prod); - } else { - LOG("File is remote"); - CapioFile &c_file = get_capio_file(path); - auto it = apps.find(tid); - if (!c_file.is_complete() && it != apps.end()) { - LOG("File not complete"); - const std::string &app_name = it->second; - long int pos = match_globs(path); - if (pos != -1) { - LOG("Glob matched"); - std::string prefix = std::get<0>(metadata_conf_globs[pos]); - off64_t batch_size = std::get<5>(metadata_conf_globs[pos]); - if (batch_size > 0) { - LOG("Handling batch file"); - handle_remote_read_batch_request(tid, fd, count, app_name, prefix, batch_size, - false); - return; - } - } - } - LOG("Delegating to backend remote read"); - request_remote_read(tid, fd, count); - } -} - -void read_handler(const char *const str) { - int tid, fd; - off64_t count; - sscanf(str, "%d %d %ld", &tid, &fd, &count); - handle_read(tid, fd, count); -} - -#endif // CAPIO_SERVER_HANDLERS_READ_HPP \ No newline at end of file diff --git a/src/server/handlers/rename.hpp b/src/server/handlers/rename.hpp deleted file mode 100644 index 5c57b9e87..000000000 --- a/src/server/handlers/rename.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_RENAME_HPP -#define CAPIO_SERVER_HANDLERS_RENAME_HPP - -#include "utils/location.hpp" - -void handle_rename(int tid, const std::filesystem::path &oldpath, - const std::filesystem::path &newpath) { - START_LOG(gettid(), "call(tid=%d, oldpath=%s, newpath=%s)", tid, oldpath.c_str(), - newpath.c_str()); - - // FIXME: this doesn't work if a node renames a file handled by another node - if (auto c_file_opt = get_capio_file_opt(oldpath)) { - rename_capio_file(oldpath, newpath); - for (auto &pair : writers) { - auto node = pair.second.extract(oldpath); - if (!node.empty()) { - node.key() = newpath; - pair.second.insert(std::move(node)); - } - } - delete_from_files_location(oldpath); - if (!get_file_location_opt(newpath)) { - write_file_location(newpath); - } - rename_file_location(oldpath, newpath); - write_response(tid, 0); - } else { - write_response(tid, 1); - } -} - -void rename_handler(const char *const str) { - char oldpath[PATH_MAX]; - char newpath[PATH_MAX]; - int tid; - sscanf(str, "%s %s %d", oldpath, newpath, &tid); - handle_rename(tid, oldpath, newpath); -} - -#endif // CAPIO_SERVER_HANDLERS_RENAME_HPP diff --git a/src/server/handlers/rmdir.hpp b/src/server/handlers/rmdir.hpp deleted file mode 100644 index 50c61d560..000000000 --- a/src/server/handlers/rmdir.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_RMDIR_HPP -#define CAPIO_SERVER_HANDLERS_RMDIR_HPP - -#include "utils/location.hpp" - -inline void handle_rmdir(int tid, const std::filesystem::path &dir_to_remove) { - START_LOG(gettid(), "call(tid=%d, dir_to_remove=%s)", tid, dir_to_remove.c_str()); - - int res = delete_from_files_location(dir_to_remove); - write_response(tid, res); -} - -void rmdir_handler(const char *const str) { - char dir_to_remove[PATH_MAX]; - int tid; - sscanf(str, "%s %d", dir_to_remove, &tid); - handle_rmdir(tid, dir_to_remove); -} - -#endif // CAPIO_SERVER_HANDLERS_RMDIR_HPP diff --git a/src/server/handlers/seek.hpp b/src/server/handlers/seek.hpp deleted file mode 100644 index 8aad11bfd..000000000 --- a/src/server/handlers/seek.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_SEEK_HPP -#define CAPIO_SERVER_HANDLERS_SEEK_HPP - -#include "stat.hpp" - -inline void handle_lseek(int tid, int fd, off64_t offset) { - START_LOG(gettid(), "call(tid=%d, fd=%d, offset=%ld)", tid, fd, offset); - - set_capio_file_offset(tid, fd, offset); - write_response(tid, offset); -} - -void handle_seek_data(int tid, int fd, off64_t offset) { - START_LOG(gettid(), "call(tid=%d, fd=%d, offset=%ld)", tid, fd, offset); - - CapioFile &c_file = get_capio_file(get_capio_file_path(tid, fd)); - offset = c_file.seek_data(offset); - set_capio_file_offset(tid, fd, offset); - write_response(tid, offset); -} - -inline void handle_seek_end(int tid, int fd) { - START_LOG(gettid(), "call(tid=%d, fd=%d)", tid, fd); - - // seek_end here behaves as stat because we want the file size - reply_stat(tid, get_capio_file_path(tid, fd)); -} - -inline void handle_seek_hole(int tid, int fd, off64_t offset) { - START_LOG(gettid(), "call(tid=%d, fd=%d, offset=%ld)", tid, fd, offset); - - CapioFile &c_file = get_capio_file(get_capio_file_path(tid, fd)); - offset = c_file.seek_hole(offset); - set_capio_file_offset(tid, fd, offset); - write_response(tid, offset); -} - -void lseek_handler(const char *const str) { - int tid, fd; - off64_t offset; - sscanf(str, "%d %d %ld", &tid, &fd, &offset); - handle_lseek(tid, fd, offset); -} - -void seek_data_handler(const char *const str) { - int tid, fd; - off64_t offset; - sscanf(str, "%d %d %ld", &tid, &fd, &offset); - handle_seek_data(tid, fd, offset); -} - -void seek_end_handler(const char *const str) { - int tid, fd; - sscanf(str, "%d %d", &tid, &fd); - handle_seek_end(tid, fd); -} - -void seek_hole_handler(const char *const str) { - int tid, fd; - off64_t offset; - sscanf(str, "%d %d %ld", &tid, &fd, &offset); - handle_seek_hole(tid, fd, offset); -} - -#endif // CAPIO_SERVER_HANDLERS_SEEK_HPP diff --git a/src/server/handlers/stat.hpp b/src/server/handlers/stat.hpp deleted file mode 100644 index 22acb76f1..000000000 --- a/src/server/handlers/stat.hpp +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_STAT_HPP -#define CAPIO_SERVER_HANDLERS_STAT_HPP - -#include -#include - -#include "remote/backend.hpp" - -#include "remote/requests.hpp" - -#include "utils/location.hpp" -#include "utils/producer.hpp" -#include "utils/types.hpp" - -void wait_for_file_completion(int tid, const std::filesystem::path &path) { - START_LOG(gettid(), "call(tid=%d, path=%s)", tid, path.c_str()); - - loop_load_file_location(path); - // check if the file is local or remote - CapioFile &c_file = get_capio_file(path); - - // if file is streamable - if (c_file.is_complete() || c_file.get_mode() == CAPIO_FILE_MODE_NO_UPDATE || - strcmp(std::get<0>(get_file_location(path)), node_name) == 0) { - - write_response(tid, c_file.get_file_size()); - write_response(tid, static_cast(c_file.is_dir() ? 1 : 0)); - - } else { - handle_remote_stat_request(tid, path); - } -} - -inline void reply_stat(int tid, const std::filesystem::path &path) { - START_LOG(gettid(), "call(tid=%d, path=%s)", tid, path.c_str()); - - auto file_location_opt = get_file_location_opt(path); - LOG("File %s is local? %s", path.c_str(), file_location_opt ? "True" : "False"); - - if (!file_location_opt) { - if (!load_file_location(path)) { - LOG("path %s is not present in any node", path.c_str()); - // if it is in configuration file then wait otherwise fail - if ((metadata_conf.find(path) != metadata_conf.end() || match_globs(path) != -1) && - !is_producer(tid, path)) { - LOG("File found but not ready yet. Starting a thread to wait for file %s", - path.c_str()); - std::thread t(wait_for_file_completion, tid, std::filesystem::path(path)); - t.detach(); - } else { - LOG("Metadata do not contains file or globs did not contain file or app is " - "producer."); - write_response(tid, -1); // return size - write_response(tid, -1); // return is_dir - } - return; - } - } - auto c_file_opt = get_capio_file_opt(path); - CapioFile &c_file = - (c_file_opt) ? c_file_opt->get() : create_capio_file(path, false, get_file_initial_size()); - LOG("Obtained capio file. ready to reply to client"); - const std::filesystem::path &capio_dir = get_capio_dir(); - LOG("Obtained capio_dir"); - if (!file_location_opt) { - LOG("File is now present from remote node. retrieving file again."); - file_location_opt = get_file_location_opt(path); - } - if (c_file.is_complete() || strcmp(std::get<0>(file_location_opt->get()), node_name) == 0 || - c_file.get_mode() == CAPIO_FILE_MODE_NO_UPDATE || capio_dir == path) { - LOG("Sending response to client"); - write_response(tid, c_file.get_file_size()); - write_response(tid, static_cast(c_file.is_dir() ? 1 : 0)); - } else { - LOG("Delegating backend to reply to remote stats"); - // send a request for file. then start a thread to wait for the request completion - c_file.create_buffer_if_needed(path, false); - handle_remote_stat_request(tid, path); - } -} - -void fstat_handler(const char *const str) { - int tid, fd; - sscanf(str, "%d %d", &tid, &fd); - reply_stat(tid, get_capio_file_path(tid, fd)); -} - -void stat_handler(const char *const str) { - char path[2048]; - int tid; - sscanf(str, "%d %s", &tid, path); - reply_stat(tid, path); -} - -#endif // CAPIO_SERVER_HANDLERS_STAT_HPP diff --git a/src/server/handlers/unlink.hpp b/src/server/handlers/unlink.hpp deleted file mode 100644 index 7aa8219cc..000000000 --- a/src/server/handlers/unlink.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_UNLINK_HPP -#define CAPIO_SERVER_HANDLERS_UNLINK_HPP - -inline void handle_unlink(int tid, const std::filesystem::path &path) { - START_LOG(gettid(), "call(tid=%d, path=%s)", tid, path.c_str()); - - auto c_file_opt = get_capio_file_opt(path); - if (c_file_opt) { // TODO: it works only in the local case - CapioFile &c_file = c_file_opt->get(); - c_file.unlink(); - if (c_file.is_deletable()) { - delete_capio_file(path); - delete_from_files_location(path); - } - write_response(tid, 0); - } else { - write_response(tid, -1); - } -} - -void unlink_handler(const char *const str) { - char path[PATH_MAX]; - int tid; - sscanf(str, "%d %s", &tid, path); - handle_unlink(tid, path); -} - -#endif // CAPIO_SERVER_HANDLERS_UNLINK_HPP diff --git a/src/server/handlers/write.hpp b/src/server/handlers/write.hpp deleted file mode 100644 index f7eaaaf3a..000000000 --- a/src/server/handlers/write.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef CAPIO_SERVER_HANDLERS_WRITE_HPP -#define CAPIO_SERVER_HANDLERS_WRITE_HPP - -#include "utils/location.hpp" -#include "utils/metadata.hpp" - -void write_handler(const char *const str) { - std::string request; - int tid, fd; - off64_t count; - sscanf(str, "%d %d %ld", &tid, &fd, &count); - - START_LOG(gettid(), "call(tid=%d, fd=%d, count=%ld)", tid, fd, count); - // check if another process is waiting for this data - off64_t offset = get_capio_file_offset(tid, fd); - off64_t end_of_write = offset + count; - const std::filesystem::path &path = get_capio_file_path(tid, fd); - CapioFile &c_file = get_capio_file(path); - off64_t file_shm_size = c_file.get_buf_size(); - auto *data_buf = data_buffers[tid].first; - - c_file.create_buffer_if_needed(path, true); - if (end_of_write > file_shm_size) { - c_file.expand_buffer(end_of_write); - } - c_file.read_from_queue(*data_buf, offset, count); - - int pid = pids[tid]; - writers[pid][path] = true; - c_file.insert_sector(offset, end_of_write); - if (c_file.first_write) { - c_file.first_write = false; - write_file_location(path); - // TODO: it works only if there is one prod per file - update_dir(tid, path); - } - set_capio_file_offset(tid, fd, end_of_write); -} - -#endif // CAPIO_SERVER_HANDLERS_WRITE_HPP diff --git a/src/server/remote/README.md b/src/server/remote/README.md deleted file mode 100644 index 0c596125d..000000000 --- a/src/server/remote/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# How to add a new backend - -To implement a new backend for CAPIO, first create a new backend file inside the -directory `CAPIO_ROOT_DIR/src/server/remote/backend/`. This file must be included inside the -file `CAPIO_ROOT_DIR/src/server/remote/backend/include.hpp`. - -## Class requirement for the new backend. - -The newly created backend must extend the class `Backend` (defined in `CAPIO_ROOT_DIR/src/server/remote/backend.hpp`) -and implements its methods. More methods can be implemented, but CAPIO will only use the ones that are defined -into the interface. - -## Register a new backend inside capio - -Finally, to be able to use the newly created backend, the function `select_backend()`, defined inside the file -`CAPIO_ROOT_DIR/src/server/remote/`, has to be modified. In particular, a new conditional branch should be added before -the final return statement, comparing the new backend name with the one chosen by the user, returning an instance of the -new backend if the names matches. - -The last thing to do, is to let the user know that your backend actually exists. To do this, you should change the -constant `CAPIO_SERVER_ARG_PARSER_CONFIG_BACKEND_HELP`, defined in the -file `CAPIO_ROOT_DIR/src/common/capio/constants.hpp`, to include the name of your backend. diff --git a/src/server/remote/backend.hpp b/src/server/remote/backend.hpp deleted file mode 100644 index 5d1844711..000000000 --- a/src/server/remote/backend.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef CAPIO_SERVER_REMOTE_BACKEND_HPP -#define CAPIO_SERVER_REMOTE_BACKEND_HPP -#include "capio/logger.hpp" -#include - -class RemoteRequest { - private: - char *_buf_recv; - int _code; - const std::string _source; - - public: - RemoteRequest(char *buf_recv, const std::string &source) : _source(source) { - START_LOG(gettid(), "call(buf_recv=%s, source=%s)", buf_recv, source.c_str()); - int code; - auto [ptr, ec] = std::from_chars(buf_recv, buf_recv + 4, code); - if (ec == std::errc()) { - this->_code = code; - this->_buf_recv = new char[CAPIO_SERVER_REQUEST_MAX_SIZE]; - strcpy(this->_buf_recv, ptr + 1); - LOG("Received request %d from %s : %s", this->_code, this->_source.c_str(), - this->_buf_recv); - } else { - this->_code = -1; - } - }; - - RemoteRequest(const RemoteRequest &) = delete; - RemoteRequest &operator=(const RemoteRequest &) = delete; - - ~RemoteRequest() { delete[] _buf_recv; } - - [[nodiscard]] auto get_source() const { return this->_source; } - [[nodiscard]] auto get_content() const { return this->_buf_recv; } - [[nodiscard]] auto get_code() const { return this->_code; } -}; - -/** - * This class is the interface prototype - * for capio backend communication services. - * To implement a new backend, please implement the following - * functions in a dedicated backend. - */ -class Backend { - public: - virtual ~Backend() = default; - - /** - * Returns the node names of the CAPIO servers - * @return A set containing the node names of all CAPIO servers - */ - virtual const std::set get_nodes() = 0; - - /** - * Handshake the server applications - */ - virtual void handshake_servers() = 0; - - /** - * Read the next message from the incoming queue - * @return A RemoteRequest class object containing the request contents - */ - virtual RemoteRequest read_next_request() = 0; - - /** - * Send file - * @param shm buffer of data to be sent - * @param nbytes length of @param shm - * @param dest target to send files to - */ - virtual void send_file(char *shm, long int nbytes, const std::string &target) = 0; - - /** - * receive a file from another process - * @param shm Buffer that will be filled with incoming data - * @param source The source target to receive from - * @param bytes_expected Size of expected incoming buffer - */ - virtual void recv_file(char *shm, const std::string &source, long int bytes_expected) = 0; - - /** - * - * @param message - * @param message_len - * @param target - */ - virtual void send_request(const char *message, int message_len, const std::string &target) = 0; -}; - -Backend *backend; - -#endif // CAPIO_SERVER_REMOTE_BACKEND_HPP diff --git a/src/server/remote/backend/include.hpp b/src/server/remote/backend/include.hpp deleted file mode 100644 index 6291a9500..000000000 --- a/src/server/remote/backend/include.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef CAPIO_SERVER_REMOTE_BACKEND_INCLUDE_HPP -#define CAPIO_SERVER_REMOTE_BACKEND_INCLUDE_HPP -/* - * Include here all backend classes - */ - -#include "mpi.hpp" -#endif // CAPIO_SERVER_REMOTE_BACKEND_INCLUDE_HPP diff --git a/src/server/remote/backend/mpi.hpp b/src/server/remote/backend/mpi.hpp deleted file mode 100644 index 477d1445b..000000000 --- a/src/server/remote/backend/mpi.hpp +++ /dev/null @@ -1,166 +0,0 @@ -#ifndef CAPIO_SERVER_REMOTE_BACKEND_MPI_HPP -#define CAPIO_SERVER_REMOTE_BACKEND_MPI_HPP - -#include - -#include "remote/backend.hpp" - -class MPIBackend : public Backend { - - protected: - MPI_Request req{}; - int rank = -1; - - /* - * This structure holds inside the information to convert from hostname to MPI rank*/ - std::set nodes; - std::unordered_map rank_nodes_equivalence; - static constexpr long MPI_MAX_ELEM_COUNT = 1024L * 1024 * 1024; - - public: - MPIBackend(int argc, char **argv) { - int node_name_len, provided; - START_LOG(gettid(), "call()"); - LOG("Created a MPI backend"); - MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); - LOG("Mpi has multithreading support? %s (%d)", - provided == MPI_THREAD_MULTIPLE ? "yes" : "no", provided); - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - LOG("node_rank=%d", &rank); - if (provided != MPI_THREAD_MULTIPLE) { - LOG("Error: The threading support level is not MPI_THREAD_MULTIPLE (is %d)", provided); - MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE); - } - - node_name = new char[MPI_MAX_PROCESSOR_NAME]; - MPI_Get_processor_name(node_name, &node_name_len); - LOG("Node name = %s, length=%d", node_name, node_name_len); - nodes.emplace(node_name); - rank_nodes_equivalence[std::to_string(rank)] = node_name; - rank_nodes_equivalence[node_name] = std::to_string(rank); - } - - ~MPIBackend() override { - START_LOG(gettid(), "Call()"); - MPI_Finalize(); - } - - inline const std::set get_nodes() override { return nodes; } - - inline void handshake_servers() override { - START_LOG(gettid(), "call()"); - - auto buf = std::unique_ptr(new char[MPI_MAX_PROCESSOR_NAME]); - for (int i = 0; i < n_servers; i += 1) { - if (i != rank) { - // TODO: possible deadlock - MPI_Send(node_name, strlen(node_name), MPI_CHAR, i, 0, MPI_COMM_WORLD); - std::fill(buf.get(), buf.get() + MPI_MAX_PROCESSOR_NAME, 0); - MPI_Recv(buf.get(), MPI_MAX_PROCESSOR_NAME, MPI_CHAR, i, 0, MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - nodes.emplace(buf.get()); - rank_nodes_equivalence.emplace(buf.get(), std::to_string(i)); - rank_nodes_equivalence.emplace(std::to_string(i), buf.get()); - } - } - } - - RemoteRequest read_next_request() override { - START_LOG(gettid(), "call()"); - MPI_Status status; - char *buff = new char[CAPIO_SERVER_REQUEST_MAX_SIZE]; - LOG("initiating a lightweight MPI receive"); - MPI_Request request; - int received = 0; - - // receive from server - MPI_Irecv(buff, CAPIO_SERVER_REQUEST_MAX_SIZE, MPI_CHAR, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, - &request); - struct timespec sleepTime {}; - struct timespec returnTime {}; - sleepTime.tv_sec = 0; - sleepTime.tv_nsec = 200000; - - while (!received) { - MPI_Test(&request, &received, &status); - nanosleep(&sleepTime, &returnTime); - } - int bytes_received; - MPI_Get_count(&status, MPI_CHAR, &bytes_received); - - LOG("receive completed!"); - return {buff, rank_nodes_equivalence[std::to_string(status.MPI_SOURCE)]}; - } - - void send_file(char *shm, long int nbytes, const std::string &target) override { - START_LOG(gettid(), "call(%.50s, %ld, %s)", shm, nbytes, target.c_str()); - int elem_to_snd = 0; - int dest = std::stoi(rank_nodes_equivalence[target]); - for (long int k = 0; k < nbytes; k += elem_to_snd) { - // Compute the maximum amount to send for this chunk - elem_to_snd = static_cast(std::min(nbytes - k, MPI_MAX_ELEM_COUNT)); - - LOG("Sending %d bytes to %d with offset from beginning odf k=%ld", elem_to_snd, dest, - k); - MPI_Isend(shm + k, elem_to_snd, MPI_BYTE, dest, 0, MPI_COMM_WORLD, &req); - LOG("Sent chunk of %d bytes", elem_to_snd); - } - } - - void send_request(const char *message, int message_len, const std::string &target) override { - START_LOG(gettid(), "call(message=%s, message_len=%d, target=%s)", message, message_len, - target.c_str()); - const std::string &mpi_target = rank_nodes_equivalence[target]; - LOG("MPI_rank for target %s is %s", target.c_str(), mpi_target.c_str()); - - MPI_Send(message, message_len + 1, MPI_CHAR, std::stoi(mpi_target), 0, MPI_COMM_WORLD); - } - - inline void recv_file(char *shm, const std::string &source, long int bytes_expected) override { - START_LOG(gettid(), "call(shm=%ld, source=%s, bytes_expected=%ld)", shm, source.c_str(), - bytes_expected); - MPI_Status status; - int bytes_received = 0, count = 0, source_rank = std::stoi(rank_nodes_equivalence[source]); - LOG("Buffer is valid? %s", - shm != nullptr ? "yes" - : "NO! a nullptr was given to recv_file. this will make mpi crash!"); - for (long int k = 0; k < bytes_expected; k += bytes_received) { - - count = static_cast(std::min(bytes_expected - k, MPI_MAX_ELEM_COUNT)); - - LOG("Expected %ld bytes from %s with offset from beginning odf k=%ld", count, - source.c_str(), k); - MPI_Recv(shm + k, count, MPI_BYTE, source_rank, 0, MPI_COMM_WORLD, &status); - LOG("Received chunk"); - MPI_Get_count(&status, MPI_BYTE, &bytes_received); - LOG("Chunk size is %ld bytes", bytes_received); - } - } -}; - -class MPISYNCBackend : public MPIBackend { - public: - MPISYNCBackend(int argc, char *argv[]) : MPIBackend(argc, argv) { - START_LOG(gettid(), "call()"); - LOG("Wrapped MPI backend with MPISYC backend"); - } - - ~MPISYNCBackend() override { - START_LOG(gettid(), "Call()"); - MPI_Finalize(); - } - - RemoteRequest read_next_request() override { - START_LOG(gettid(), "call()"); - MPI_Status status; - char *buff = new char[CAPIO_SERVER_REQUEST_MAX_SIZE]; - LOG("initiating a synchronized MPI receive"); - MPI_Recv(buff, CAPIO_SERVER_REQUEST_MAX_SIZE, MPI_CHAR, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, - &status); // receive from server - - LOG("receive completed!"); - return {buff, rank_nodes_equivalence[std::to_string(status.MPI_SOURCE)]}; - } -}; - -#endif // CAPIO_SERVER_REMOTE_BACKEND_MPI_HPP \ No newline at end of file diff --git a/src/server/remote/handlers.hpp b/src/server/remote/handlers.hpp deleted file mode 100644 index 80d6e4117..000000000 --- a/src/server/remote/handlers.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef CAPIO_SERVER_REMOTE_HANDLERS_HPP -#define CAPIO_SERVER_REMOTE_HANDLERS_HPP - -#include "handlers/read.hpp" -#include "handlers/stat.hpp" - -#endif // CAPIO_SERVER_REMOTE_HANDLERS_HPP diff --git a/src/server/remote/handlers/read.hpp b/src/server/remote/handlers/read.hpp deleted file mode 100644 index 2ee7238f3..000000000 --- a/src/server/remote/handlers/read.hpp +++ /dev/null @@ -1,304 +0,0 @@ -#ifndef CAPIO_SERVER_REMOTE_HANDLERS_READ_HPP -#define CAPIO_SERVER_REMOTE_HANDLERS_READ_HPP - -#include "remote/backend.hpp" -#include "remote/requests.hpp" - -inline void serve_remote_read(const std::filesystem::path &path, const std::string &dest, int tid, - int fd, off64_t count, off64_t offset, bool complete, - bool is_getdents) { - START_LOG(gettid(), - "call(path=%s, dest=%s, tid=%d, fd=%d, count=%ld, offset=%ld, complete=%s, " - "is_getdents=%s)", - path.c_str(), dest.c_str(), tid, fd, count, offset, complete ? "true" : "false", - is_getdents ? "true" : "false"); - - // Send all the rest of the file not only the number of bytes requested - // Useful for caching - CapioFile &c_file = get_capio_file(path); - long int nbytes = c_file.get_stored_size() - offset; - off64_t prefetch_data_size = get_prefetch_data_size(); - - if (prefetch_data_size != 0 && nbytes > prefetch_data_size) { - nbytes = prefetch_data_size; - } - const off64_t file_size = c_file.get_stored_size(); - - // send request - serve_remote_read_request(tid, fd, count, nbytes, file_size, complete, is_getdents, dest); - // send data - backend->send_file(c_file.get_buffer() + offset, nbytes, dest); -} - -std::vector *files_available(const std::string &prefix, const std::string &app_name, - const std::string &path) { - START_LOG(gettid(), "call(prefix=%s, app_name=%s, path=%s)", prefix.c_str(), app_name.c_str(), - path.c_str()); - - auto files_to_send = new std::vector; - std::unordered_set &files = files_sent[app_name]; - auto capio_file_opt = get_capio_file_opt(path); - - if (capio_file_opt) { - if (capio_file_opt->get().is_complete()) { - files_to_send->emplace_back(path); - files.insert(path); - } - } else { - return files_to_send; - } - - for (auto &file_path : get_capio_file_paths()) { - auto file_location_opt = get_file_location_opt(file_path); - - if (files.find(file_path) == files.end() && file_location_opt && - strcmp(std::get<0>(file_location_opt->get()), node_name) == 0 && - file_path.native().compare(0, prefix.length(), prefix) == 0) { - - CapioFile &c_file = get_capio_file(file_path); - if (c_file.is_complete() && !c_file.is_dir()) { - files_to_send->emplace_back(file_path); - files.insert(file_path); - } - } - } - return files_to_send; -} - -inline void handle_read_reply(int tid, int fd, long count, off64_t file_size, off64_t nbytes, - bool complete, bool is_getdents) { - START_LOG( - gettid(), - "call(tid=%d, fd=%d, count=%ld, file_size=%ld, nbytes=%ld, complete=%s, is_getdents=%s)", - tid, fd, count, file_size, nbytes, complete ? "true" : "false", - is_getdents ? "true" : "false"); - - const std::filesystem::path &path = get_capio_file_path(tid, fd); - CapioFile &c_file = get_capio_file(path); - off64_t offset = get_capio_file_offset(tid, fd); - c_file.real_file_size = file_size; - c_file.insert_sector(offset, offset + nbytes); - c_file.set_complete(complete); - - off64_t end_of_sector = c_file.get_sector_end(offset); - c_file.create_buffer_if_needed(path, false); - off64_t bytes_read; - off64_t end_of_read = offset + count; - if (end_of_sector > end_of_read) { - end_of_sector = end_of_read; - bytes_read = count; - } else { - bytes_read = end_of_sector - offset; - } - if (is_getdents) { - send_dirent_to_client(tid, fd, c_file, offset, bytes_read); - } else { - send_data_to_client(tid, fd, c_file.get_buffer(), offset, bytes_read); - } -} - -void wait_for_data(const std::filesystem::path &path, const std::string &dest, int tid, int fd, - off64_t count, off64_t offset, bool is_getdents) { - START_LOG(gettid(), - "call(path=%s, dest=%s, tid=%d, fs=%d, count=%ld, offset=%ld, is_getdents=%s)", - path.c_str(), dest.c_str(), tid, fd, count, offset, is_getdents ? "true" : "false"); - - const CapioFile &c_file = get_capio_file(path); - // wait that nbytes are written - c_file.wait_for_data(offset + count); - serve_remote_read(path, dest, tid, fd, count, offset, c_file.is_complete(), is_getdents); -} - -inline void send_files_batch(const std::string &prefix, const std::string &dest, int tid, int fd, - off64_t count, bool is_getdents, - const std::vector *files_to_send) { - START_LOG(gettid(), "call(prefix=%s, dest=%s, tid=%d, fd=%d, count=%ld, is_getdents=%s)", - prefix.c_str(), dest.c_str(), tid, fd, count, is_getdents ? "true" : "false"); - - // send request - send_files_batch_request(prefix, tid, fd, count, is_getdents, dest, files_to_send); - - // send data - for (const std::string &path : *files_to_send) { - LOG("Sending file %s to target %s", path.c_str(), dest.c_str()); - CapioFile &c_file = get_capio_file(path); - backend->send_file(c_file.get_buffer(), c_file.get_stored_size(), dest); - } -} - -void wait_for_files_batch(const std::filesystem::path &prefix, const std::string &dest, int tid, - int fd, off64_t count, bool is_getdents, - const std::vector *files, Semaphore *n_files_ready) { - START_LOG(gettid(), "call(prefix=%s, dest=%s, tid=%d, fd=%d, count=%ld, is_getdents=%s)", - prefix.c_str(), dest.c_str(), tid, fd, count, is_getdents ? "true" : "false"); - - n_files_ready->lock(); - LOG("Files are available. sending batch of files"); - send_files_batch(prefix, dest, tid, fd, count, is_getdents, files); - - delete n_files_ready; -} - -inline void handle_remote_read_batch(const std::filesystem::path &path, const std::string &dest, - int tid, int fd, off64_t count, off64_t batch_size, - const std::string &app_name, - const std::filesystem::path &prefix, bool is_getdents) { - START_LOG( - gettid(), - "call(path=%s, dest=%s, tid=%d, fd=%d, count=%ld, batch_size=%ld, app_name=%s, prefix=%s, " - "is_getdents=%s)", - path.c_str(), dest.c_str(), tid, fd, count, batch_size, app_name.c_str(), prefix.c_str(), - is_getdents ? "true" : "false"); - - // FIXME: this assignment always overrides the request parameter, which is never used - batch_size = find_batch_size(prefix, metadata_conf_globs); - auto *files = files_available(prefix, app_name, path); - LOG("files==nullptr? %s", files == nullptr ? "true" : "false"); - if (files->size() == batch_size) { - LOG("files->size() == batch_size"); - send_files_batch(prefix, dest, tid, fd, count, is_getdents, files); - } else { - /* - * create a thread that waits for the completion of such - * files and then send those files - */ - LOG("files->size() != batch_size"); - auto *sem = new Semaphore(0); - std::thread t(wait_for_files_batch, prefix, dest, tid, fd, count, is_getdents, files, sem); - t.detach(); - LOG("Thread for batch started."); - std::lock_guard lg(nfiles_mutex); - clients_remote_pending_nfiles[app_name].emplace_back(prefix, batch_size, dest, files, sem); - } -} - -inline void -handle_remote_read_batch_reply(const std::string &source, int tid, int fd, off64_t count, - const std::vector> &files, - bool is_getdents) { - START_LOG(gettid(), "call(source=%s, tid=%d, fd=%d, count=%ld, is_getdents=%s)", source.c_str(), - tid, fd, count, is_getdents ? "true" : "false"); - - for (const auto &[path, nbytes] : files) { - auto c_file_opt = get_capio_file_opt(path); - if (c_file_opt) { - CapioFile &c_file = c_file_opt->get(); - c_file.create_buffer_if_needed(path, false); - size_t file_shm_size = c_file.get_buf_size(); - if (nbytes > file_shm_size) { - c_file.expand_buffer(nbytes); - } - c_file.first_write = false; - } else { - add_file_location(path, source.c_str(), -1); - CapioFile &c_file = create_capio_file(path, false, nbytes); - c_file.insert_sector(0, nbytes); - c_file.real_file_size = nbytes; - c_file.first_write = false; - c_file.set_complete(); - } - // as was done previously, write to the capio file buffer from its beginning - c_file_opt->get().read_from_node(source, 0, nbytes); - handle_read_reply(tid, fd, count, nbytes, nbytes, true, is_getdents); - } -} - -inline void handle_remote_read(const std::filesystem::path &path, const std::string &source, - int tid, int fd, off64_t count, off64_t offset, bool is_getdents) { - START_LOG(gettid(), - "call(path=%s, source=%s, tid=%d, fd=%d, count=%ld, offset=%ld, is_getdents=%s)", - path.c_str(), source.c_str(), tid, fd, count, offset, is_getdents ? "true" : "false"); - - CapioFile &c_file = get_capio_file(path); - bool data_available = (offset + count <= c_file.get_stored_size()); - if (c_file.is_complete() || - (c_file.get_mode() == CAPIO_FILE_MODE_NO_UPDATE && data_available)) { - serve_remote_read(path, source, tid, fd, count, offset, c_file.is_complete(), is_getdents); - } else { - std::thread t(wait_for_data, path, source, tid, fd, count, offset, is_getdents); - t.detach(); - } -} - -inline void handle_remote_read_reply(const std::string &source, int tid, int fd, off64_t count, - off64_t nbytes, off64_t file_size, bool complete, - bool is_getdents) { - START_LOG(gettid(), - "call(source=%s, tid=%d, fd=%d, count=%ld, nbytes=%ld, file_size=%ld, complete=%s, " - "is_getdents=%s)", - source.c_str(), tid, fd, count, nbytes, file_size, complete ? "true" : "false", - is_getdents ? "true" : "false"); - - const std::filesystem::path &path = get_capio_file_path(tid, fd); - off64_t offset = get_capio_file_offset(tid, fd); - CapioFile &c_file = get_capio_file(path); - - c_file.create_buffer_if_needed(path, false); - if (nbytes != 0) { - auto file_shm_size = c_file.get_buf_size(); - auto file_size_recv = offset + nbytes; - if (file_size_recv > file_shm_size) { - c_file.expand_buffer(file_size_recv); - } - c_file.read_from_node(source, offset, nbytes); - nbytes *= sizeof(char); - } - handle_read_reply(tid, fd, count, file_size, nbytes, complete, is_getdents); -} - -void remote_read_batch_handler(const RemoteRequest &request) { - const std::string &dest = request.get_source(); - int tid, fd, is_getdents; - off64_t count, batch_size; - char path[PATH_MAX], app_name[512], prefix[PATH_MAX]; - sscanf(request.get_content(), "%s %d %d %ld %ld %s %s %d", path, &tid, &fd, &count, &batch_size, - app_name, prefix, &is_getdents); - handle_remote_read_batch(path, dest, tid, fd, count, batch_size, app_name, prefix, is_getdents); -} - -// TODO: refactor this -void remote_read_batch_reply_handler(const RemoteRequest &request) { - std::string dest = request.get_source(); - std::string path, prefix, tmp; - std::vector> files; - - std::istringstream content(request.get_content()); - std::getline(content, prefix, ' '); - std::getline(content, tmp, ' '); - int tid = std::stoi(tmp); - std::getline(content, tmp, ' '); - int fd = std::stoi(tmp); - std::getline(content, tmp, ' '); - off64_t count = std::stol(tmp); - std::getline(content, tmp, ' '); - bool is_getdents = std::stoi(tmp); - - while (getline(content, path, ' ')) { - path = prefix.append(path); - std::getline(content, tmp, ' '); - files.emplace_back(path, std::stol(tmp)); - } - - handle_remote_read_batch_reply(dest, tid, fd, count, files, is_getdents); -} - -void remote_read_handler(const RemoteRequest &request) { - const std::string &dest = request.get_source(); - char path[PATH_MAX]; - int tid, fd, is_getdents; - off64_t count, offset; - sscanf(request.get_content(), "%s %d %d %ld %ld %d", path, &tid, &fd, &count, &offset, - &is_getdents); - handle_remote_read(path, dest, tid, fd, count, offset, is_getdents); -} - -void remote_read_reply_handler(const RemoteRequest &request) { - const std::string &dest = request.get_source(); - off64_t count, nbytes, file_size; - int tid, fd, complete, is_getdents; - sscanf(request.get_content(), "%d %d %ld %ld %ld %d %d", &tid, &fd, &count, &nbytes, &file_size, - &complete, &is_getdents); - handle_remote_read_reply(dest, tid, fd, count, nbytes, file_size, complete, is_getdents); -} - -#endif // CAPIO_SERVER_REMOTE_HANDLERS_READ_HPP diff --git a/src/server/remote/handlers/stat.hpp b/src/server/remote/handlers/stat.hpp deleted file mode 100644 index e66b1a153..000000000 --- a/src/server/remote/handlers/stat.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef CAPIO_SERVER_REMOTE_HANDLERS_STAT_HPP -#define CAPIO_SERVER_REMOTE_HANDLERS_STAT_HPP - -#include "remote/backend.hpp" -#include "remote/requests.hpp" - -inline void serve_remote_stat(const std::filesystem::path &path, const std::string &dest, - int source_tid) { - START_LOG(gettid(), "call(path=%s, dest=%s, source_tid%d)", path.c_str(), dest.c_str(), - source_tid); - - const CapioFile &c_file = get_capio_file(path); - off64_t file_size = c_file.get_file_size(); - bool is_dir = c_file.is_dir(); - serve_remote_stat_request(path, source_tid, file_size, is_dir, dest); -} - -void wait_for_completion(const std::filesystem::path &path, int source_tid, - const std::string &dest) { - START_LOG(gettid(), "call(path=%s, source_tid=%d, dest=%s)", path.c_str(), source_tid, - dest.c_str()); - - const CapioFile &c_file = get_capio_file(path); - c_file.wait_for_completion(); - LOG("File %s has been completed. serving stats data", path.c_str()); - serve_remote_stat(path, dest, source_tid); -} - -inline void handle_remote_stat(int source_tid, const std::filesystem::path &path, - const std::string &dest) { - START_LOG(gettid(), "call(source_tid=%d, path=%s, dest=%s)", source_tid, path.c_str(), - dest.c_str()); - - auto c_file = get_capio_file_opt(path); - if (c_file) { - LOG("File %s is present on capio file system", path.c_str()); - if (c_file->get().is_complete() || c_file->get().get_mode() == CAPIO_FILE_MODE_NO_UPDATE) { - LOG("file is complete. serving file"); - serve_remote_stat(path, dest, source_tid); - } else { // wait for completion - LOG("File is not complete. awaiting completion on different thread. parameters of wait " - "are: path=%s, source_tid=%d, dest=%s", - path.c_str(), source_tid, dest.c_str()); - std::thread t(wait_for_completion, path, source_tid, dest); - t.detach(); - } - } else { - LOG("CAPIO file is not in metadata. checking in globs for files to be created"); - if (match_globs(path) != -1) { - LOG("File is in globs. creating capio_file and starting thread awaiting for future " - "creation of file"); - create_capio_file(path, false, CAPIO_DEFAULT_FILE_INITIAL_SIZE); - std::thread t(wait_for_completion, path, source_tid, dest); - t.detach(); - } else { - ERR_EXIT("Error capio file is not present, nor is going to be created in the future."); - } - } -} - -inline void handle_remote_stat_reply(const std::filesystem::path &path, int source_tid, - off64_t size, bool dir) { - START_LOG(gettid(), "call(path=%s, source_tid=%d, size=%ld, dir=%s)", path.c_str(), source_tid, - size, dir ? "true" : "false"); - - write_response(source_tid, size); - write_response(source_tid, static_cast(dir)); -} - -void remote_stat_handler(const RemoteRequest &request) { - char path[PATH_MAX], dest[HOST_NAME_MAX]; - int tid; - sscanf(request.get_content(), "%d %s %s", &tid, dest, path); - handle_remote_stat(tid, path, dest); -} - -void remote_stat_reply_handler(const RemoteRequest &request) { - char path[PATH_MAX]; - off64_t size; - int dir, source_tid; - sscanf(request.get_content(), "%s %d %ld %d", path, &source_tid, &size, &dir); - handle_remote_stat_reply(path, source_tid, size, dir); -} - -#endif // CAPIO_SERVER_REMOTE_HANDLERS_STAT_HPP diff --git a/src/server/remote/listener.hpp b/src/server/remote/listener.hpp deleted file mode 100644 index bf343079a..000000000 --- a/src/server/remote/listener.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef CAPIO_SERVER_REMOTE_LISTENER_HPP -#define CAPIO_SERVER_REMOTE_LISTENER_HPP - -#include "capio/logger.hpp" - -#include "backend.hpp" -#include "backend/include.hpp" - -#include "handlers.hpp" - -typedef void (*CComsHandler_t)(const RemoteRequest &); - -static constexpr std::array -build_server_request_handlers_table() { - std::array _server_request_handlers{0}; - - _server_request_handlers[CAPIO_SERVER_REQUEST_READ] = remote_read_handler; - _server_request_handlers[CAPIO_SERVER_REQUEST_READ_REPLY] = remote_read_reply_handler; - _server_request_handlers[CAPIO_SERVER_REQUEST_READ_BATCH] = remote_read_batch_handler; - _server_request_handlers[CAPIO_SERVER_REQUEST_READ_BATCH_REPLY] = - remote_read_batch_reply_handler; - _server_request_handlers[CAPIO_SERVER_REQUEST_STAT] = remote_stat_handler; - _server_request_handlers[CAPIO_SERVER_REQUEST_STAT_REPLY] = remote_stat_reply_handler; - - return _server_request_handlers; -} - -inline Backend *select_backend(const std::string &backend_name, int argc, char *argv[]) { - START_LOG(gettid(), "call(backend_name=%s)", backend_name.c_str()); - - if (backend_name.empty()) { - LOG("backend selected: none"); - std::cout << CAPIO_SERVER_CLI_LOG_SERVER - << "Starting CAPIO with default backend (MPI) as no preferred backend was chosen" - << std::endl; - return new MPIBackend(argc, argv); - } - - if (backend_name == "mpi") { - LOG("backend selected: mpi"); - std::cout << CAPIO_SERVER_CLI_LOG_SERVER << "Starting CAPIO with MPI backend" << std::endl; - return new MPIBackend(argc, argv); - } - - if (backend_name == "mpisync") { - LOG("backend selected: mpisync"); - std::cout << CAPIO_SERVER_CLI_LOG_SERVER << "Starting CAPIO with MPI (SYNC) backend" - << std::endl; - return new MPISYNCBackend(argc, argv); - } - LOG("Backend %s does not exist in CAPIO. Reverting back to the default MPI backend", - backend_name.c_str()); - std::cout << CAPIO_SERVER_CLI_LOG_SERVER_WARNING << " Backend " << backend_name - << " does not exist. Reverting to the default MPI backend" << std::endl; - return new MPIBackend(argc, argv); -} - -[[noreturn]] void capio_remote_listener(Semaphore &internal_server_sem) { - static const std::array server_request_handlers = - build_server_request_handlers_table(); - - START_LOG(gettid(), "call()"); - - internal_server_sem.lock(); - - while (true) { - auto request = backend->read_next_request(); - - server_request_handlers[request.get_code()](request); - } -} - -#endif // CAPIO_SERVER_REMOTE_LISTENER_HPP diff --git a/src/server/remote/requests.hpp b/src/server/remote/requests.hpp deleted file mode 100644 index f58848816..000000000 --- a/src/server/remote/requests.hpp +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef CAPIO_REMOTE_REQUESTS_HPP -#define CAPIO_REMOTE_REQUESTS_HPP - -inline void serve_remote_stat_request(const std::filesystem::path &path, int source_tid, - off64_t file_size, bool is_dir, const std::string &dest) { - const char *const format = "%04d %s %d %ld %d"; - const int size = snprintf(nullptr, 0, format, CAPIO_SERVER_REQUEST_STAT_REPLY, path.c_str(), - source_tid, file_size, is_dir); - const std::unique_ptr message(new char[size + 1]); - - sprintf(message.get(), "%04d %s %d %ld %d", CAPIO_SERVER_REQUEST_STAT_REPLY, path.c_str(), - source_tid, file_size, is_dir); - - backend->send_request(message.get(), size + 1, dest); -} - -inline void serve_remote_read_request(int tid, int fd, int count, long int nbytes, - const off64_t file_size, bool complete, bool is_getdents, - const std::string &dest) { - START_LOG(gettid(), "call()"); - const char *const format = "%04d %d %d %d %ld %ld %d %d"; - const int size = snprintf(nullptr, 0, format, CAPIO_SERVER_REQUEST_READ_REPLY, tid, fd, count, - nbytes, file_size, complete, is_getdents); - const std::unique_ptr message(new char[size + 1]); - sprintf(message.get(), format, CAPIO_SERVER_REQUEST_READ_REPLY, tid, fd, count, nbytes, - file_size, complete, is_getdents); - LOG("Message = %s", message.get()); - - // send request - backend->send_request(message.get(), size + 1, dest); -} - -inline void send_files_batch_request(const std::string &prefix, int tid, int fd, int count, - bool is_getdents, const std::string &dest, - const std::vector *files_to_send) { - START_LOG(gettid(), "call()"); - const char *const format = "%04d %s %d %d %d %d"; - const int size = snprintf(nullptr, 0, format, CAPIO_SERVER_REQUEST_READ_BATCH_REPLY, - prefix.c_str(), tid, fd, count, is_getdents); - const std::unique_ptr header(new char[size + 1]); - sprintf(header.get(), format, CAPIO_SERVER_REQUEST_READ_BATCH_REPLY, prefix.c_str(), tid, fd, - count, is_getdents); - std::string message(header.get()); - for (const std::string &path : *files_to_send) { - CapioFile &c_file = get_capio_file(path); - message.append(" " + path.substr(prefix.length()) + " " + - std::to_string(c_file.get_stored_size())); - } - LOG("Message = %s", message.c_str()); - - // send request - backend->send_request(message.c_str(), message.length(), dest); -} - -inline void handle_remote_stat_request(int tid, const std::filesystem::path &path) { - START_LOG(gettid(), "call(tid=%d, path=%s)", tid, path.c_str()); - - std::string dest = std::get<0>(get_file_location(path)); - const char *const format = "%04d %d %s %s"; - const int size = - snprintf(nullptr, 0, format, CAPIO_SERVER_REQUEST_STAT, tid, node_name, path.c_str()); - const std::unique_ptr message(new char[size + 1]); - sprintf(message.get(), format, CAPIO_SERVER_REQUEST_STAT, tid, node_name, path.c_str()); - LOG("destination=%s, message=%s", dest.c_str(), message.get()); - - backend->send_request(message.get(), size + 1, dest); - LOG("message sent"); -} - -inline void handle_remote_read_batch_request(int tid, int fd, off64_t count, - const std::string &app_name, const std::string &prefix, - off64_t batch_size, bool is_getdents) { - START_LOG(gettid(), - "call(tid=%d, fd=%d, count=%ld, app_name=%s, prefix=%s, " - "batch_size=%ld, is_getdents=%s)", - tid, fd, count, app_name.c_str(), prefix.c_str(), batch_size, - is_getdents ? "true" : "false"); - - const std::filesystem::path &path = get_capio_file_path(tid, fd); - std::string dest = std::get<0>(get_file_location(path)); - - const char *const format = "%04d %s %d %d %ld %ld %s %s %d"; - const int size = - snprintf(nullptr, 0, format, CAPIO_SERVER_REQUEST_READ_BATCH, path.c_str(), tid, fd, count, - batch_size, app_name.c_str(), prefix.c_str(), is_getdents); - const std::unique_ptr message(new char[size + 1]); - sprintf(message.get(), format, CAPIO_SERVER_REQUEST_READ_BATCH, path.c_str(), tid, fd, count, - batch_size, app_name.c_str(), prefix.c_str(), is_getdents); - LOG("Message = %s", message.get()); - backend->send_request(message.get(), size + 1, dest); -} - -inline void handle_remote_read_request(int tid, int fd, off64_t count, bool is_getdents) { - START_LOG(gettid(), "call(tid=%d, fd=%d, count=%ld, is_getdents=%s)", tid, fd, count, - is_getdents ? "true" : "false"); - - // If it is not in cache then send the request to the remote node - const std::filesystem::path &path = get_capio_file_path(tid, fd); - off64_t offset = get_capio_file_offset(tid, fd); - std::string dest = std::get<0>(get_file_location(path)); - - const char *const format = "%04d %s %d %d %ld %ld %d"; - const int size = snprintf(nullptr, 0, format, CAPIO_SERVER_REQUEST_READ, path.c_str(), tid, fd, - count, offset, is_getdents); - const std::unique_ptr message(new char[size + 1]); - sprintf(message.get(), format, CAPIO_SERVER_REQUEST_READ, path.c_str(), tid, fd, count, offset, - is_getdents); - - LOG("Message = %s", message.get()); - backend->send_request(message.get(), size + 1, dest); -} - -#endif // CAPIO_REMOTE_REQUESTS_HPP diff --git a/src/server/utils/capio_file.hpp b/src/server/utils/capio_file.hpp deleted file mode 100644 index 56cb68c04..000000000 --- a/src/server/utils/capio_file.hpp +++ /dev/null @@ -1,495 +0,0 @@ -#ifndef CAPIO_SERVER_UTILS_CAPIO_FILE_HPP -#define CAPIO_SERVER_UTILS_CAPIO_FILE_HPP - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "capio/logger.hpp" -#include "capio/queue.hpp" - -#include "remote/backend.hpp" - -/* - * Only the server have all the information - * A process that only read from a file doesn't have the info on the _sectors - * A process that writes only have info on the sector that he wrote - * the file size is in shm because all the processes need this info - * and it's easy to provide it to them using the shm - */ - -struct compare { - bool operator()(const std::pair &lhs, - const std::pair &rhs) const { - return (lhs.first < rhs.first); - } -}; - -class CapioFile { - private: - char *_buf = nullptr; // buffer containing the data - off64_t _buf_size; - std::string_view _committed = CAPIO_FILE_COMMITTED_ON_TERMINATION; - bool _directory = false; - // _fd is useful only when the file is memory-mapped - int _fd = -1; - bool _home_node = false; - std::string_view _mode = CAPIO_FILE_MODE_UPDATE; - int _n_links = 1; - long int _n_close = 0; - long int _n_close_expected = -1; - int _n_opens = 0; - bool _permanent = false; - // _sectors stored in memory of the files (only the home node is forced to - // be up to date) - std::set, compare> _sectors; - // vector of (tid, fd) - std::vector> _threads_fd; - bool _complete = false; // whether the file is completed / committed - - /*sync variables*/ - mutable std::mutex _mutex; - mutable std::condition_variable _complete_cv; - mutable std::condition_variable _data_avail_cv; - - inline off64_t _get_stored_size() const { - auto it = _sectors.rbegin(); - return (it == _sectors.rend()) ? 0 : it->second; - } - - public: - bool first_write = true; - long int n_files = 0; // useful for directories - long int n_files_expected = -1; // useful for directories - /* - * file size in the home node. In a given moment could not be up-to-date. - * This member is useful because a node different from the home node - * could need to known the size of the file but not its content - */ - - std::size_t real_file_size = 0; - - CapioFile() - : _buf_size(0), _committed(CAPIO_FILE_COMMITTED_ON_TERMINATION), _directory(false), - _permanent(false) {} - - CapioFile(const std::string_view &committed, const std::string_view &mode, bool directory, - long int n_files_expected, bool permanent, off64_t init_size, - long int n_close_expected) - : _buf_size(init_size), _committed(committed), _directory(directory), _mode(mode), - _n_close_expected(n_close_expected), _permanent(permanent), - n_files_expected(n_files_expected + 2) {} - - CapioFile(bool directory, bool permanent, off64_t init_size, long int n_close_expected = -1) - : _buf_size(init_size), _committed(CAPIO_FILE_COMMITTED_ON_TERMINATION), - _directory(directory), _n_close_expected(n_close_expected), _permanent(permanent) {} - - CapioFile(const CapioFile &) = delete; - CapioFile &operator=(const CapioFile &) = delete; - - ~CapioFile() { - START_LOG(gettid(), "call()"); - LOG("Deleting capio_file"); - - if (_permanent && _home_node) { - if (_directory) { - delete[] _buf; - } else { - int res = munmap(_buf, _buf_size); - if (res == -1) { - ERR_EXIT("munmap CapioFile"); - } - } - } else { - delete[] _buf; - } - } - - [[nodiscard]] inline bool is_complete() const { - START_LOG(gettid(), "capio_file is complete? %s", this->_complete ? "true" : "false"); - std::lock_guard lg(_mutex); - return this->_complete; - } - - inline void wait_for_completion() const { - START_LOG(gettid(), "call()"); - LOG("Thread waiting for file to be committed"); - std::unique_lock lock(_mutex); - _complete_cv.wait(lock, [this] { return _complete; }); - } - - inline void wait_for_data(long offset) const { - START_LOG(gettid(), "call()"); - LOG("Thread waiting for data to be available"); - std::unique_lock lock(_mutex); - _data_avail_cv.wait(lock, [offset, this] { - return (offset <= this->_get_stored_size()) || this->_complete; - }); - } - - inline void set_complete(bool complete = true) { - START_LOG(gettid(), "setting capio_file._complete=%s", complete ? "true" : "false"); - std::lock_guard lg(_mutex); - if (this->_complete != complete) { - this->_complete = complete; - if (this->_complete) { - _complete_cv.notify_all(); - _data_avail_cv.notify_all(); - } - } - } - - inline void add_fd(int tid, int fd) { _threads_fd.emplace_back(tid, fd); } - - [[nodiscard]] inline bool buf_to_allocate() const { - std::lock_guard lg(_mutex); - return _buf == nullptr; - } - - inline void close() { - _n_close++; - _n_opens--; - } - - void commit() { - START_LOG(gettid(), "call()"); - - if (_permanent && !_directory && _home_node) { - off64_t size = get_file_size(); - if (ftruncate(_fd, size) == -1) { - ERR_EXIT("ftruncate commit capio_file"); - } - _buf_size = size; - if (::close(_fd) == -1) { - ERR_EXIT("close commit capio_file"); - } - } - } - - /* - * To be called when a process - * execute a read or a write syscall - */ - void create_buffer(const std::filesystem::path &path, bool home_node) { - START_LOG(gettid(), "call(path=%s, home_node=%s)", path.c_str(), - home_node ? "true" : "false"); - std::lock_guard lock(_mutex); - // TODO: will use malloc in order to be able to use realloc - _home_node = home_node; - if (_permanent && home_node) { - if (_directory) { - std::filesystem::create_directory(path); - std::filesystem::permissions(path, std::filesystem::perms::owner_all); - _buf = new char[_buf_size]; - } else { - LOG("creating mem mapped file"); - _fd = ::open(path.c_str(), O_RDWR | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); - if (_fd == -1) { - ERR_EXIT("open %s CapioFile constructor", path.c_str()); - } - if (ftruncate(_fd, _buf_size) == -1) { - ERR_EXIT("ftruncate CapioFile constructor"); - } - _buf = - (char *) mmap(nullptr, _buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0); - if (_buf == MAP_FAILED) { - ERR_EXIT("mmap CapioFile constructor"); - } - } - } else { - _buf = new char[_buf_size]; - } - } - - inline void create_buffer_if_needed(const std::filesystem::path &path, bool home_node) { - if (buf_to_allocate()) { - create_buffer(path, home_node); - } - } - - void memcpy_capio_file(char *new_p, char *old_p) const { - for (auto §or : _sectors) { - off64_t lbound = sector.first; - off64_t ubound = sector.second; - off64_t sector_length = ubound - lbound; - memcpy(new_p + lbound, old_p + lbound, sector_length); - } - } - - char *expand_buffer(off64_t data_size) { // TODO: use realloc - off64_t double_size = _buf_size * 2; - off64_t new_size = data_size > double_size ? data_size : double_size; - char *new_buf = new char[new_size]; - std::lock_guard lock(_mutex); - // memcpy(new_p, old_p, file_shm_size); //TODO memcpy only the - // sector - // stored in CapioFile - memcpy_capio_file(new_buf, _buf); - delete[] _buf; - _buf = new_buf; - _buf_size = new_size; - return new_buf; - } - - inline char *get_buffer() { return _buf; } - - [[nodiscard]] inline off64_t get_buf_size() const { return _buf_size; } - - [[nodiscard]] inline const std::string_view &get_committed() const { return _committed; } - - [[nodiscard]] inline const std::vector> &get_fds() const { - return _threads_fd; - } - - [[nodiscard]] inline off64_t get_file_size() const { - std::lock_guard lock(_mutex); - if (!_sectors.empty()) { - return _sectors.rbegin()->second; - } else { - return 0; - } - } - - [[nodiscard]] inline const std::string_view &get_mode() const { - START_LOG(gettid(), "call()"); - return _mode; - } - - /* - * Returns the offset to the end of the sector - * if the offset parameter is inside the - * sector, -1 otherwise - * - */ - [[nodiscard]] off64_t get_sector_end(off64_t offset) const { - START_LOG(gettid(), "call(offset=%ld)", offset); - - off64_t sector_end = -1; - auto it = _sectors.upper_bound(std::make_pair(offset, 0)); - - if (!_sectors.empty() && it != _sectors.begin()) { - --it; - if (offset <= it->second) { - sector_end = it->second; - } - } - - return sector_end; - } - - [[nodiscard]] inline const std::set, compare> &get_sectors() const { - return _sectors; - } - - /* - * get the size of the data stored in this node - * If the node is the home node then this is equals to - * the real size of the file - */ - [[nodiscard]] inline off64_t get_stored_size() const { - std::lock_guard lock(_mutex); - return this->_get_stored_size(); - } - - /* - * Insert the new sector automatically modifying the - * existent _sectors if needed. - * - * Params: - * off64_t new_start: the beginning of the sector to insert - * off64_t new_end: the beginning of the sector to insert - * - * new_start must be > new_end otherwise the behaviour - * in undefined - * - */ - void insert_sector(off64_t new_start, off64_t new_end) { - START_LOG(gettid(), "call(new_start=%ld, new_end=%ld)", new_start, new_end); - - auto p = std::make_pair(new_start, new_end); - std::lock_guard lock(_mutex); - - if (_sectors.empty()) { - LOG("Insert sector <%ld, %ld>", p.first, p.second); - _sectors.insert(p); - return; - } - auto it_lbound = _sectors.upper_bound(p); - if (it_lbound == _sectors.begin()) { - if (new_end < it_lbound->first) { - LOG("Insert sector <%ld, %ld>", p.first, p.second); - _sectors.insert(p); - } else { - auto it = it_lbound; - bool end_before = false; - bool end_inside = false; - while (it != _sectors.end() && !end_before && !end_inside) { - end_before = p.second < it->first; - if (!end_before) { - end_inside = p.second <= it->second; - if (!end_inside) { - ++it; - } - } - } - - if (end_inside) { - p.second = it->second; - ++it; - } - _sectors.erase(it_lbound, it); - LOG("Insert sector <%ld, %ld>", p.first, p.second); - _sectors.insert(p); - } - } else { - --it_lbound; - auto it = it_lbound; - if (p.first <= it_lbound->second) { - // new sector starts inside a sector - p.first = it_lbound->first; - } else { // in this way the sector will not be deleted - ++it_lbound; - } - bool end_before = false; - bool end_inside = false; - while (it != _sectors.end() && !end_before && !end_inside) { - end_before = p.second < it->first; - if (!end_before) { - end_inside = p.second <= it->second; - if (!end_inside) { - ++it; - } - } - } - - if (end_inside) { - p.second = it->second; - ++it; - } - _sectors.erase(it_lbound, it); - LOG("Insert sector <%ld, %ld>", p.first, p.second); - _sectors.insert(p); - } - } - - [[nodiscard]] inline bool is_closed() const { - return _n_close_expected == -1 || _n_close == _n_close_expected; - } - - [[nodiscard]] inline bool is_deletable() const { return _n_opens == 0 && _n_links <= 0; } - - [[nodiscard]] inline bool is_dir() const { return _directory; } - - inline void open() { _n_opens++; } - - /* - * From the manual: - * - * Adjust the file offset to the next location in the file - * greater than or equal to offset containing data. If - * offset points to data, then the file offset is set to - * offset. - * - * Fails if offset points past the end of the file. - * - */ - off64_t seek_data(off64_t offset) { - if (_sectors.empty()) { - if (offset == 0) { - return 0; - } else { - return -1; - } - } - auto it = _sectors.upper_bound(std::make_pair(offset, 0)); - if (it == _sectors.begin()) { - return it->first; - } - --it; - if (offset <= it->second) { - return offset; - } else { - ++it; - if (it == _sectors.end()) { - return -1; - } else { - return it->first; - } - } - } - - /* - * From the manual: - * - * Adjust the file offset to the next hole in the file - * greater than or equal to offset. If offset points into - * the middle of a hole, then the file offset is set to - * offset. If there is no hole past offset, then the file - * offset is adjusted to the end of the file (i.e., there is - * an implicit hole at the end of any file). - * - * - * Fails if offset points past the end of the file. - * - */ - [[nodiscard]] off64_t seek_hole(off64_t offset) const { - if (_sectors.empty()) { - if (offset == 0) { - return 0; - } else { - return -1; - } - } - auto it = _sectors.upper_bound(std::make_pair(offset, 0)); - if (it == _sectors.begin()) { - return offset; - } - --it; - if (offset <= it->second) { - return it->second; - } else { - ++it; - if (it == _sectors.end()) { - return -1; - } else { - return offset; - } - } - } - - inline void remove_fd(int tid, int fd) { - auto it = std::find(_threads_fd.begin(), _threads_fd.end(), std::make_pair(tid, fd)); - if (it != _threads_fd.end()) { - _threads_fd.erase(it); - } - } - - /** - * Save data inside the capio_file buffer - * @param buffer - * @return - */ - inline void read_from_node(const std::string &dest, off64_t offset, off64_t buffer_size) { - std::unique_lock lock(_mutex); - backend->recv_file(_buf + offset, dest, buffer_size); - _data_avail_cv.notify_all(); - } - - inline void read_from_queue(SPSCQueue &queue, size_t offset, long int num_bytes) { - START_LOG(gettid(), "call()"); - - std::unique_lock lock(_mutex); - queue.read(_buf + offset, num_bytes); - _data_avail_cv.notify_all(); - } - - inline void unlink() { _n_links--; } -}; - -#endif // CAPIO_SERVER_UTILS_CAPIO_FILE_HPP \ No newline at end of file diff --git a/src/server/utils/common.hpp b/src/server/utils/common.hpp index 670b6b376..f62b04c04 100644 --- a/src/server/utils/common.hpp +++ b/src/server/utils/common.hpp @@ -4,58 +4,10 @@ #include #include "capio/constants.hpp" -#include "capio/dirent.hpp" - -#include "capio_file.hpp" -#include "metadata.hpp" -#include "requests.hpp" #include "types.hpp" -void send_data_to_client(int tid, int fd, char *buf, off64_t offset, off64_t count) { - START_LOG(gettid(), "call(tid=%d, fd=%d, buf=0x%08x, offset=%ld, count=%ld)", tid, fd, buf, - offset, count); - - write_response(tid, offset + count); - data_buffers[tid].second->write(buf + offset, count); - set_capio_file_offset(tid, fd, offset + count); -} - -inline off64_t send_dirent_to_client(int tid, int fd, CapioFile &c_file, off64_t offset, - off64_t count) { - START_LOG(gettid(), "call(tid=%d, fd=%d, offset=%ld, count=%ld)", tid, fd, offset, count); - - struct linux_dirent64 *dir_entity; - - char *incoming = c_file.get_buffer(); - int first_entry = static_cast(offset / sizeof(linux_dirent64)); - off64_t end_of_read = std::min(offset + count, c_file.get_stored_size()); - int last_entry = static_cast(end_of_read / sizeof(linux_dirent64)); - off64_t actual_size = (last_entry - first_entry) * static_cast(sizeof(linux_dirent64)); - - if (actual_size > 0) { - auto dirents = std::unique_ptr(new linux_dirent64[actual_size]); - for (int i = first_entry; i < last_entry; i++) { - dir_entity = (struct linux_dirent64 *) (incoming + i * sizeof(linux_dirent64)); - auto ¤t_dirent = dirents[i - first_entry]; - current_dirent.d_reclen = sizeof(linux_dirent64); - current_dirent.d_ino = dir_entity->d_ino; - current_dirent.d_off = (i + 1) * current_dirent.d_reclen; - current_dirent.d_type = dir_entity->d_type; - strcpy(current_dirent.d_name, dir_entity->d_name); - - LOG("DIRENT NAME: %s - TARGET NAME: %s", dir_entity->d_name, current_dirent.d_name); - } - - send_data_to_client(tid, fd, reinterpret_cast(dirents.get()) - offset, offset, - actual_size); - } else { - write_response(tid, offset); - } - - return actual_size; -} inline bool is_int(const std::string &s) { START_LOG(gettid(), "call(%s)", s.c_str()); diff --git a/src/server/utils/filesystem.hpp b/src/server/utils/filesystem.hpp index 32465b7e8..82184fc79 100644 --- a/src/server/utils/filesystem.hpp +++ b/src/server/utils/filesystem.hpp @@ -8,7 +8,6 @@ #include "capio/dirent.hpp" -#include "capio_file.hpp" #include "common.hpp" #include "location.hpp" #include "metadata.hpp" diff --git a/src/server/utils/json.hpp b/src/server/utils/json.hpp deleted file mode 100644 index 16f0fc5dc..000000000 --- a/src/server/utils/json.hpp +++ /dev/null @@ -1,231 +0,0 @@ -#ifndef CAPIO_SERVER_UTILS_JSON_HPP -#define CAPIO_SERVER_UTILS_JSON_HPP - -#include - -#include "utils/metadata.hpp" -#include "utils/types.hpp" - -void parse_conf_file(const std::string &conf_file, const std::filesystem::path &capio_dir) { - START_LOG(gettid(), "call(config_file='%s', capio_dir='%s')", conf_file.c_str(), - capio_dir.c_str()); - - simdjson::ondemand::parser parser; - simdjson::padded_string json; - simdjson::ondemand::document entries; - simdjson::ondemand::array input_stream, output_stream, streaming, permanent_files; - simdjson::error_code error; - - try { - json = simdjson::padded_string::load(conf_file); - } catch (const simdjson::simdjson_error &e) { - std::cerr << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR - << "Exception thrown while opening config file: " << e.what() << std::endl; - LOG("Exception thrown while opening config file: %s", e.what()); - ERR_EXIT("Exception thrown while opening config file: %s", e.what()); - } - - entries = parser.iterate(json); - std::string_view wf_name; - error = entries["name"].get_string().get(wf_name); - if (error) { - ERR_EXIT("Error: workflow name is mandatory"); - } - workflow_name = std::string(wf_name); - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO - << "Parsing configuration for workflow: " << workflow_name << std::endl; - LOG("Parsing configuration for workflow: %s", std::string(workflow_name).c_str()); - - auto io_graph = entries["IO_Graph"]; - - for (auto app : io_graph) { - std::string_view app_name; - error = app["name"].get_string().get(app_name); - if (error) { - ERR_EXIT("Error: app name is mandatory"); - } - - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << "Parsing config for app " << app_name - << std::endl; - LOG("Parsing config for app %s", std::string(app_name).c_str()); - - if (app["input_stream"].get_array().get(input_stream)) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING - << "No input_stream section found for app " << app_name << std::endl; - LOG("No input_stream section found for app %s", std::string(app_name).c_str()); - } else { - // TODO: parse input_stream - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO - << "Completed input_stream parsing for app: " << app_name << std::endl; - LOG("Completed input_stream parsing for app: %s", std::string(app_name).c_str()); - } - - if (app["output_stream"].get_array().get(output_stream)) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING - << "No output_stream section found for app " << app_name << std::endl; - LOG("No output_stream section found for app %s", std::string(app_name).c_str()); - } else { - // TODO: parse output stream - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO - << "Completed output_stream parsing for app: " << app_name << std::endl; - LOG("Completed output_stream parsing for app: %s", std::string(app_name).c_str()); - } - - // PARSING STREAMING FILES - if (app["streaming"].get_array().get(streaming)) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING - << "No streaming section found for app: " << app_name << std::endl; - LOG("No streaming section found for app: %s", std::string(app_name).c_str()); - } else { - LOG("Began parsing streaming section for app %s", std::string(app_name).c_str()); - for (auto file : streaming) { - std::string_view committed, mode, commit_rule; - std::vector streaming_names; - long int n_close = -1; - long n_files, batch_size; - - simdjson::ondemand::array name = file["name"].get_array(); - if (name.is_empty()) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR - << "error: name for application is mandatory" << std::endl; - ERR_EXIT("error: name for application is mandatory"); - } - for (auto item : name) { - std::string_view elem = item.get_string().value(); - streaming_names.emplace_back(elem); - LOG("Found name: %s", std::string(elem).c_str()); - } - - // PARSING COMMITTED - error = file["committed"].get_string().get(committed); - if (error) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR - << "commit rule is mandatory in streaming section" << std::endl; - ERR_EXIT("error commit rule is mandatory in streaming section"); - } else { - auto pos = committed.find(':'); - if (pos != std::string::npos) { - commit_rule = committed.substr(0, pos); - if (commit_rule != CAPIO_FILE_COMMITTED_ON_CLOSE) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << "commit rule " - << commit_rule << std::endl; - ERR_EXIT("error commit rule: %s", std::string(commit_rule).c_str()); - } - - std::string n_close_str(committed.substr(pos + 1, committed.length())); - - if (!is_int(n_close_str)) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR - << "commit rule on_close invalid number" << std::endl; - ERR_EXIT("error commit rule on_close invalid number: !is_int()"); - } - n_close = std::stol(n_close_str); - } else { - commit_rule = committed; - } - } - LOG("Committed: %s", std::string(committed).c_str()); - // END PARSING COMMITTED - - error = file["mode"].get_string().get(mode); - if (error) { - mode = CAPIO_FILE_MODE_UPDATE; - } - LOG("Mode: %s", std::string(mode).c_str()); - - error = file["n_files"].get_int64().get(n_files); - if (error) { - n_files = -1; - } - LOG("n_files: %d", n_files); - - error = file["batch_size"].get_int64().get(batch_size); - if (error) { - batch_size = 0; - } - LOG("batch_size: %d", batch_size); - for (auto path : streaming_names) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO - << "Updating metadata for path: " << path << std::endl; - if (path.is_relative()) { - path = (capio_dir / path).lexically_normal(); - } - LOG("path: %s", path.c_str()); - - std::size_t pos = path.native().find('*'); - update_metadata_conf(path, pos, n_files, batch_size, std::string(commit_rule), - std::string(mode), std::string(app_name), false, n_close); - } - } - - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO - << "completed parsing of streaming section for app: " << app_name - << std::endl; - LOG("completed parsing of streaming section for app: %s", - std::string(app_name).c_str()); - } // END PARSING STREAMING FILES - - } // END OF APP MAIN LOOPS - LOG("Completed parsing of io_graph app main loops"); - - long int batch_size = 0; - if (entries["permanent"].get_array().get(permanent_files)) { // PARSING PERMANENT FILES - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING - << "No permanent section found for workflow: " << workflow_name << std::endl; - LOG("No permanent section found for workflow: %s", std::string(workflow_name).c_str()); - } else { - for (auto file : permanent_files) { - std::string_view name; - error = file.get_string().get(name); - if (error) { - ERR_EXIT("error name for permanent section is mandatory"); - } - LOG("Permanent name: %s", std::string(name).c_str()); - - std::filesystem::path path(name); - - if (path.is_relative()) { - path = (capio_dir / path).lexically_normal(); - } - // NOTE: here there was a copy of the previous structured block. - // pretty much sure it is a bug, but it might be wanted... - - std::size_t pos = path.native().find('*'); - if (pos == std::string::npos) { - auto it = metadata_conf.find(path); - if (it == metadata_conf.end()) { - update_metadata_conf(path, pos, -1, batch_size, - CAPIO_FILE_COMMITTED_ON_TERMINATION, - CAPIO_FILE_MODE_UPDATE, "", true, -1); - } else { - std::get<4>(it->second) = true; - } - } else { - std::string prefix_str = path.native().substr(0, pos); - long int i = match_globs(prefix_str); - if (i == -1) { - update_metadata_conf(path, pos, -1, batch_size, - CAPIO_FILE_COMMITTED_ON_TERMINATION, "", "", true, -1); - } else { - auto &tuple = metadata_conf_globs[i]; - std::get<6>(tuple) = true; - } - } - } - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << "Completed parsing of permanent files" - << std::endl; - LOG("Completed parsing of permanent files"); - } // END PARSING PERMANENT FILES - - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << "Completed parsing of io_graph" << std::endl; - LOG("Completed parsing of io_graph"); - - auto home_node_policies = entries["home_node_policy"].error(); - if (!home_node_policies) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING - << "Warning: capio does not support home node policies yet! skipping section " - << std::endl; - } -} - -#endif // CAPIO_SERVER_UTILS_JSON_HPP diff --git a/src/server/utils/location.hpp b/src/server/utils/location.hpp deleted file mode 100644 index 47815a5aa..000000000 --- a/src/server/utils/location.hpp +++ /dev/null @@ -1,301 +0,0 @@ -#ifndef CAPIO_SERVER_UTILS_LOCATIONS_HPP -#define CAPIO_SERVER_UTILS_LOCATIONS_HPP - -#include -#include - -#include "utils/types.hpp" - -constexpr char CAPIO_SERVER_FILES_LOCATION_NAME[] = "files_location_%s.txt"; -constexpr char CAPIO_SERVER_INVALIDATE_FILE_PATH_CHAR = '#'; - -std::unordered_map> files_location; -std::mutex files_location_mutex; - -int files_location_fd; -FILE *files_location_fp; -std::unordered_map files_location_fps; - -class Flock { - private: - int _fd; - short _l_type; - struct flock _lock; - - public: - inline explicit Flock(const int fd, const short l_type) : _fd(fd), _l_type(l_type), _lock() { - START_LOG(gettid(), "call(fd=%d, l_type=%d)", _fd, l_type); - - _lock.l_whence = SEEK_SET; - _lock.l_start = 0; - _lock.l_len = 0; - _lock.l_pid = getpid(); - } - - Flock(const Flock &) = delete; - Flock &operator=(const Flock &) = delete; - inline ~Flock() = default; - - inline void lock() { - START_LOG(gettid(), "call()"); - - _lock.l_type = _l_type; - if (fcntl(_fd, F_SETLKW, &_lock) < 0) { - close(_fd); - ERR_EXIT("CAPIO server failed to lock the file with error %d (%s)", errno, - strerror(errno)); - } - } - - inline void unlock() { - START_LOG(gettid(), "call()"); - - _lock.l_type = F_UNLCK; - if (fcntl(_fd, F_SETLK, &_lock) < 0) { - close(_fd); - ERR_EXIT("CAPIO server failed to unlock the file with error %d (%s)", errno, - strerror(errno)); - } - } -}; - -inline std::string get_file_location_name(const std::string &node) { - START_LOG(gettid(), "node=%s", node.c_str()); - - char file_location_name[HOST_NAME_MAX + 20]; - sprintf(file_location_name, CAPIO_SERVER_FILES_LOCATION_NAME, node.c_str()); - return {file_location_name}; -} - -inline FILE *get_file_location_descriptor(const std::string &name) { - START_LOG(gettid(), "name=%s", name.c_str()); - - if (files_location_fps.find(name) == files_location_fps.end()) { - FILE *descriptor; - if ((descriptor = fopen(name.c_str(), "a+")) == nullptr) { - ERR_EXIT("Error opening %s file: %d (%s)", name.c_str(), errno, strerror(errno)); - } - if (lseek(fileno(descriptor), 0, SEEK_SET) == -1) { - ERR_EXIT("Error during lseek in file %s", name.c_str()); - } - files_location_fps.emplace(name, descriptor); - } - - return files_location_fps.at(name); -} - -inline std::optional>> -get_file_location_opt(const std::filesystem::path &path) { - START_LOG(gettid(), "path=%s", path.c_str()); - - const std::lock_guard lg(files_metadata_mutex); - auto it = files_location.find(path); - if (it == files_location.end()) { - LOG("File was not found in files_locations. returning empty object"); - return {}; - } else { - LOG("File found on node %s", it->second); - return {it->second}; - } -} - -inline std::pair & -get_file_location(const std::filesystem::path &path) { - START_LOG(gettid(), "path=%s", path.c_str()); - - auto file_location_opt = get_file_location_opt(path); - if (file_location_opt) { - return file_location_opt->get(); - } else { - ERR_EXIT("error file location %s not present in CAPIO", path.c_str()); - } -} - -inline void add_file_location(const std::filesystem::path &path, const char *const p_node_str, - long offset) { - const std::lock_guard lg(files_location_mutex); - files_location.emplace(path, std::make_pair(p_node_str, offset)); -} - -void erase_from_files_location(const std::filesystem::path &path) { - const std::lock_guard lg(files_location_mutex); - files_location.erase(path); -} - -void rename_file_location(const std::filesystem::path &oldpath, - const std::filesystem::path &newpath) { - const std::lock_guard lg(files_location_mutex); - auto node_2 = files_location.extract(oldpath); - if (!node_2.empty()) { - node_2.key() = newpath; - files_location.insert(std::move(node_2)); - } -} - -/** - * Loads the location of @path_to_load in the current node cache - * @param path_to_load - * @return true if path_to_load is present on any node, false otherwise - */ -bool load_file_location(const std::filesystem::path &path_to_load) { - START_LOG(gettid(), "call(path_to_load=%s)", path_to_load.c_str()); - bool found = false; - auto line = reinterpret_cast(malloc((PATH_MAX + HOST_NAME_MAX + 10) * sizeof(char))); - - for (auto &node : backend->get_nodes()) { - const std::string name = get_file_location_name(node); - FILE *descriptor = get_file_location_descriptor(name); - - Flock file_lock(fileno(descriptor), F_RDLCK); - const std::lock_guard lg(file_lock); - - if (fseek(descriptor, 0, SEEK_SET) == -1) { - ERR_EXIT("fseek in load_file_location"); - } - LOG("%s offset has been moved to the beginning of the file", name.c_str()); - size_t len = 0; - - while (getline(&line, &len, descriptor) != -1) { - if (line[0] != CAPIO_SERVER_INVALIDATE_FILE_PATH_CHAR) { - std::string_view line_str(line); - auto separator = line_str.find_first_of(' '); - const std::filesystem::path path(line_str.substr(0, separator)); - if (path.compare(path_to_load) == 0) { - std::string node_str( - line_str.substr(separator + 1, line_str.length())); // remove ' ' - node_str.pop_back(); // remove \n from node name - auto node_c = new char[node.length() + 1]; // do not call delete[] on this - strcpy(node_c, node.c_str()); - LOG("path %s was found on node %s", path_to_load.c_str(), node_c); - off64_t offset = lseek(fileno(descriptor), 0, SEEK_CUR); - if (offset == -1) { - ERR_EXIT("ftell in load_file_location"); - } - add_file_location(path, node_c, offset); - found = true; - break; - } - } - } - - if (found) { - break; - } - } - free(line); - return found; -} - -/* - * Returns 1 if the location of the file path_to_check is found - * Returns 2 otherwise - */ -int delete_from_files_location(const std::filesystem::path &path) { - START_LOG(gettid(), "call(%s)", path.c_str()); - - int result = 2; - auto &[node, offset] = get_file_location(path); - LOG("Remove %s from node %s at offset %ld", path.c_str(), node, offset); - const std::string name = get_file_location_name(node); - FILE *descriptor = get_file_location_descriptor(name); - - Flock file_lock(fileno(descriptor), F_WRLCK); - const std::lock_guard lg(file_lock); - - if (offset == -1) { - if (fseek(descriptor, 0, SEEK_SET) == -1) { - ERR_EXIT("fseek in load_file_location"); - } - LOG("%s offset has been moved to the beginning of the file", name.c_str()); - auto line = - reinterpret_cast(malloc((PATH_MAX + HOST_NAME_MAX + 10) * sizeof(char))); - size_t len = 0; - offset = 0; - while (getline(&line, &len, descriptor) != -1) { - if (line[0] != CAPIO_SERVER_INVALIDATE_FILE_PATH_CHAR) { - std::string_view line_str(line); - auto separator = line_str.find_first_of(' '); - const std::filesystem::path current_path(line_str.substr(0, separator)); - if (path.compare(current_path) == 0) { - LOG("Path %s found on %s", current_path.c_str(), name.c_str()); - result = 1; - if (lseek(fileno(descriptor), offset, SEEK_SET) == -1) { - ERR_EXIT("fseek delete_from_file_location"); - } - if (write(files_location_fd, &CAPIO_SERVER_INVALIDATE_FILE_PATH_CHAR, - sizeof(char)) != 1) { - ERR_EXIT("fwrite unable to invalidate file %s in %s", current_path.c_str(), - name.c_str()); - } - LOG("Path %s has been deleted from %s", current_path.c_str(), name.c_str()); - break; - } - } - offset += static_cast(strlen(line)); - } - } else { - result = 1; - if (lseek(fileno(descriptor), offset, SEEK_SET) == -1) { - ERR_EXIT("lseek 2 delete_from_files_location"); - } - LOG("%s offset has been moved to %ld", name.c_str(), offset); - if (write(fileno(descriptor), &CAPIO_SERVER_INVALIDATE_FILE_PATH_CHAR, sizeof(char)) != 1) { - ERR_EXIT("write delete_from_files_location"); - } - LOG("Path %s has been deleted from %s", path.c_str(), name.c_str()); - } - - // Delete from local data structures - erase_from_files_location(path); - for (auto &pair : writers) { - pair.second.erase(path); - } - - return result; -} - -void loop_load_file_location(const std::filesystem::path &path_to_load) { - START_LOG(gettid(), "call(path_to_load=%s)", path_to_load.c_str()); - - while (!load_file_location(path_to_load)) { - std::this_thread::sleep_for(std::chrono::microseconds(200)); - } -} - -void open_files_location() { - START_LOG(gettid(), "call()"); - - std::string file_location_name = get_file_location_name(node_name); - if ((files_location_fp = fopen(file_location_name.c_str(), "w+")) == nullptr) { - ERR_EXIT("Error opening %s file: %d (%s)", file_location_name.c_str(), errno, - strerror(errno)); - } - if (chmod(file_location_name.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) { - ERR_EXIT("Error changing permissions for %s file: %d (%s)", file_location_name.c_str(), - errno, strerror(errno)); - } - if ((files_location_fd = fileno(files_location_fp)) == -1) { - ERR_EXIT("Error obtaining file descriptor for %s file: %d (%s)", file_location_name.c_str(), - errno, strerror(errno)); - } - files_location_fps.emplace(file_location_name, files_location_fp); -} - -void write_file_location(const std::filesystem::path &path_to_write) { - START_LOG(gettid(), "call(path_to_write=%s)", path_to_write.c_str()); - - Flock file_lock(files_location_fd, F_WRLCK); - const std::lock_guard lg(file_lock); - - long offset = lseek(files_location_fd, 0, SEEK_END); - if (offset == -1) { - ERR_EXIT("lseek in write_file_location"); - } - const std::string line = path_to_write.native() + " " + node_name + "\n"; - if (write(files_location_fd, line.c_str(), line.length()) == -1) { - ERR_EXIT("write in write_file_location"); - } - add_file_location(path_to_write, node_name, offset); -} - -#endif // CAPIO_SERVER_UTILS_LOCATIONS_HPP diff --git a/src/server/utils/metadata.hpp b/src/server/utils/metadata.hpp deleted file mode 100644 index e7e164ce2..000000000 --- a/src/server/utils/metadata.hpp +++ /dev/null @@ -1,276 +0,0 @@ -#ifndef CAPIO_SERVER_UTILS_METADATA_HPP -#define CAPIO_SERVER_UTILS_METADATA_HPP - -#include -#include - -#include "capio/filesystem.hpp" - -CSFilesMetadata_t files_metadata; -std::mutex files_metadata_mutex; - -std::unordered_map>>> - processes_files; -CSProcessFileMetadataMap_t processes_files_metadata; -std::mutex processes_files_mutex; - -CSMetadataConfMap_t metadata_conf; -CSMetadataConfGlobs_t metadata_conf_globs; - -long match_globs(const std::filesystem::path &path) { - START_LOG(gettid(), "call(path=%s)", path.c_str()); - - long int resolved_glob_offset = -1; - size_t max_length_prefix = 0; - // compute the most accurate and precise glob for a given path - for (std::size_t i = 0; i < metadata_conf_globs.size(); i++) { - std::string prefix_str = std::get<0>(metadata_conf_globs[i]); - size_t prefix_length = prefix_str.length(); - bool path_matches = - static_cast(path.native().compare(0, prefix_length, prefix_str) == 0); - LOG("path=%s, prefix_str=%s, path_matches=%s", path.c_str(), prefix_str.c_str(), - path_matches ? "True" : "False"); - if (path_matches && prefix_length > max_length_prefix) { - resolved_glob_offset = static_cast(i); - max_length_prefix = prefix_length; - LOG("Path matches with offset of %ld", resolved_glob_offset); - } - } - LOG("Result of glob %s: %d", path.c_str(), resolved_glob_offset); - return resolved_glob_offset; -} - -inline std::unordered_map> get_capio_fds() { - const std::lock_guard lg(processes_files_mutex); - std::unordered_map> tid_fd_pairs; - for (auto &process : processes_files) { - for (auto &file : process.second) { - tid_fd_pairs[process.first].push_back(file.first); - } - } - return tid_fd_pairs; -} - -inline std::vector get_capio_fds_for_tid(int tid) { - const std::lock_guard lg(processes_files_mutex); - std::vector fds; - auto it = processes_files.find(tid); - if (it != processes_files.end()) { - fds.reserve(it->second.size()); - for (auto &file : it->second) { - fds.push_back(file.first); - } - } - return fds; -} - -inline std::optional> -get_capio_file_opt(const std::filesystem::path &path) { - START_LOG(gettid(), "call(path=%s)", path.c_str()); - - const std::lock_guard lg(files_metadata_mutex); - auto it = files_metadata.find(path); - if (it == files_metadata.end()) { - LOG("File %s was not found in files_metadata. returning empty object", path.c_str()); - return {}; - } else { - LOG("File found. returning contained item"); - return {*it->second}; - } -} - -inline CapioFile &get_capio_file(const std::filesystem::path &path) { - START_LOG(gettid(), "call(path=%s)", path.c_str()); - - auto c_file_opt = get_capio_file_opt(path); - if (c_file_opt) { - return c_file_opt->get(); - } else { - ERR_EXIT("error file %s not present in CAPIO", path.c_str()); - } -} - -inline const std::filesystem::path &get_capio_file_path(int tid, int fd) { - const std::lock_guard lg(processes_files_mutex); - return processes_files_metadata[tid][fd]; -} - -inline off64_t get_capio_file_offset(int tid, int fd) { - const std::lock_guard lg(processes_files_mutex); - return *std::get<1>(processes_files[tid][fd]); -} - -inline void add_capio_file(const std::filesystem::path &path, CapioFile *c_file) { - START_LOG(gettid(), "call(path=%s, capio_file=%ld)", path.c_str(), c_file); - const std::lock_guard lg(files_metadata_mutex); - files_metadata[path] = c_file; - LOG("Capio file added to files_metadata. capio_file==nullptr? %s", - c_file == nullptr ? "TRUE" : "FALSE"); -} - -inline void add_capio_file_to_tid(int tid, int fd, const std::filesystem::path &path, - off64_t offset) { - START_LOG(gettid(), "call(tid=%d, fd=%d, path=%s, offset=%ld)", tid, fd, path.c_str(), offset); - - CapioFile &c_file = get_capio_file(path); - c_file.open(); - c_file.add_fd(tid, fd); - - const std::lock_guard lg(processes_files_mutex); - processes_files_metadata[tid][fd] = path; - processes_files[tid][fd] = std::make_pair(&c_file, std::make_shared(offset)); -} - -inline void clone_capio_file(pid_t parent_tid, pid_t child_tid) { - START_LOG(gettid(), "call(parent_tid=%d, child_tid=%d)", parent_tid, child_tid); - - for (auto &fd : get_capio_fds_for_tid(parent_tid)) { - add_capio_file_to_tid(child_tid, fd, processes_files_metadata[parent_tid][fd], - get_capio_file_offset(parent_tid, fd)); - } -} - -CapioFile &create_capio_file(const std::filesystem::path &path, bool is_dir, size_t init_size) { - START_LOG(gettid(), "call(path=%s, is_dir=%s, init_size=%ld)", path.c_str(), - is_dir ? "true" : "false", init_size); - - std::string shm_name = path; - std::replace(shm_name.begin(), shm_name.end(), '/', '_'); - CapioFile *c_file; - auto it = metadata_conf.find(path); - if (it == metadata_conf.end()) { - LOG("File was not found in metadata_conf. resolving it as glob"); - long int pos = match_globs(path); - LOG("Glob %s offset is %d", path.c_str(), pos); - if (pos == -1) { - LOG("Glob %s was not found.", path.c_str()); - if (is_dir) { - init_size = CAPIO_DEFAULT_DIR_INITIAL_SIZE; - LOG("Glob %s is a directory", path.c_str()); - } - c_file = new CapioFile(is_dir, false, init_size); - add_capio_file(path, c_file); - } else { - LOG("Path %s is found in metadata_conf_globs", path.c_str()); - auto &[glob, committed, mode, app_name, n_files, batch_size, permanent, n_close] = - metadata_conf_globs[pos]; - if (in_dir(path, glob)) { - n_files = 0; - } - if (n_files > 0) { - init_size = CAPIO_DEFAULT_DIR_INITIAL_SIZE; - is_dir = true; - } - metadata_conf[path] = - std::make_tuple(committed, mode, app_name, n_files, permanent, n_close); - LOG("Creating new capio_file. committed=%s, mode=%s", committed.c_str(), mode.c_str()); - c_file = new CapioFile(committed, mode, is_dir, n_files, permanent, init_size, n_close); - add_capio_file(path, c_file); - } - } else { - LOG("File was found in metadata_conf. handling as normal file"); - auto &[committed, mode, app_name, n_files, permanent, n_close] = it->second; - if (n_files > 0) { - is_dir = true; - init_size = CAPIO_DEFAULT_DIR_INITIAL_SIZE; - } - LOG("Creating new capio_file. committed=%s, mode=%s", committed.c_str(), mode.c_str()); - c_file = new CapioFile(committed, mode, is_dir, n_files, permanent, init_size, n_close); - add_capio_file(path, c_file); - } - return *c_file; -} - -inline void delete_capio_file_from_tid(int tid, int fd) { - START_LOG(gettid(), "call(tid=%d, fd=%d)", tid, fd); - - const std::lock_guard lg(processes_files_mutex); - CapioFile &c_file = get_capio_file(processes_files_metadata[tid][fd]); - c_file.remove_fd(tid, fd); - processes_files_metadata[tid].erase(fd); - processes_files[tid].erase(fd); -} - -inline void delete_capio_file(const std::filesystem::path &path) { - START_LOG(gettid(), "call(path=%s)", path.c_str()); - - CapioFile &c_file = get_capio_file(path); - for (auto &[tid, fd] : c_file.get_fds()) { - delete_capio_file_from_tid(tid, fd); - } - - const std::lock_guard lg(files_metadata_mutex); - files_metadata.erase(path); -} - -inline std::vector get_capio_file_paths() { - const std::lock_guard lg(files_metadata_mutex); - std::vector paths(files_metadata.size()); - for (const auto &it : files_metadata) { - paths.emplace_back(it.first); - } - return paths; -} - -inline void dup_capio_file(int tid, int old_fd, int new_fd) { - START_LOG(gettid(), "call(old_fd=%d, new_fd=%d)", old_fd, new_fd); - const std::lock_guard lg(processes_files_mutex); - const std::string &path = processes_files_metadata[tid][old_fd]; - processes_files_metadata[tid][new_fd] = path; - processes_files[tid][new_fd] = processes_files[tid][old_fd]; - CapioFile &c_file = get_capio_file(path); - c_file.open(); - c_file.add_fd(tid, new_fd); -} - -inline void rename_capio_file(const std::filesystem::path &oldpath, - const std::filesystem::path &newpath) { - START_LOG(gettid(), "call(oldpath=%s, newpath=%s)", oldpath.c_str(), newpath.c_str()); - - { - const std::lock_guard lg(processes_files_mutex); - for (auto &pair : processes_files_metadata) { - for (auto &pair_2 : pair.second) { - if (pair_2.second == oldpath) { - pair_2.second = newpath; - } - } - } - } - { - const std::lock_guard lg(files_metadata_mutex); - auto node = files_metadata.extract(oldpath); - node.key() = newpath; - files_metadata.insert(std::move(node)); - } -} - -inline off64_t set_capio_file_offset(int tid, int fd, off64_t offset) { - START_LOG(gettid(), "call(tid=%d, fd=%d, offset=%ld)", tid, fd, offset); - - const std::lock_guard lg(processes_files_mutex); - return *std::get<1>(processes_files[tid][fd]) = offset; -} - -void update_metadata_conf(std::filesystem::path &path, size_t pos, long int n_files, - size_t batch_size, const std::string &committed, const std::string &mode, - const std::string &app_name, bool permanent, long int n_close) { - START_LOG(gettid(), - "call(path=%s, pos=%ld, n_files=%ld, batch_size=%ld, committed=%s, " - "mode=%s, app_name=%s, permanent=%s, n_close=%ld)", - path.c_str(), pos, n_files, batch_size, committed.c_str(), mode.c_str(), - app_name.c_str(), permanent ? "true" : "false", n_close); - - if (pos == std::string::npos && n_files == -1) { - LOG("Path is not a glob (does not end with *), and n_files is -1"); - metadata_conf[path] = - std::make_tuple(committed, mode, app_name, n_files, permanent, n_close); - } else { - std::string prefix_str = path.native().substr(0, pos); - LOG("prefix_str=%s", prefix_str.c_str()); - metadata_conf_globs.emplace_back(prefix_str, committed, mode, app_name, n_files, batch_size, - permanent, n_close); - } -} - -#endif // CAPIO_SERVER_UTILS_METADATA_HPP diff --git a/src/server/utils/requests.hpp b/src/server/utils/requests.hpp deleted file mode 100644 index 720ef2e09..000000000 --- a/src/server/utils/requests.hpp +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef CAPIO_SERVER_REQUESTS_HPP -#define CAPIO_SERVER_REQUESTS_HPP - -#include - -#include "capio/requests.hpp" -#include "types.hpp" - -CSBufRequest_t *buf_requests; -CSBufResponse_t *bufs_response; - -/** - * Initialize request and response buffers - * @return - */ -inline void init_server() { - buf_requests = new CSBufRequest_t(SHM_COMM_CHAN_NAME, CAPIO_REQ_BUFF_CNT, CAPIO_REQ_MAX_SIZE, - workflow_name); - bufs_response = new CSBufResponse_t(); -} - -/** - * Delete request and response buffers - * @return - */ -inline void destroy_server() { - delete buf_requests; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_requests cleanup completed" - << std::endl; - - delete bufs_response; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_response cleanup completed" - << std::endl; -} - -/** - * Add a new response buffer for thread @param tid - * @param tid - * @return - */ -inline void register_listener(long tid) { - // TODO: replace numbers with constexpr - auto *p_buf_response = - new CircularBuffer(SHM_COMM_CHAN_NAME_RESP + std::to_string(tid), CAPIO_REQ_BUFF_CNT, - sizeof(off_t), workflow_name); - bufs_response->insert(std::make_pair(tid, p_buf_response)); -} - -/** - * Delete the response buffer associated with thread @param tid - * @param tid - * @return - */ -inline void remove_listener(int tid) { - auto it_resp = bufs_response->find(tid); - if (it_resp != bufs_response->end()) { - delete it_resp->second; - bufs_response->erase(it_resp); - } -} - -/** - * Read next incoming request into @param str and returns the request code - * @param str - * @return request code - */ -inline auto read_next_request(char *str) { - char req[CAPIO_REQ_MAX_SIZE]; - buf_requests->read(req); - START_LOG(gettid(), "call(req=%s)", req); - int code = -1; - auto [ptr, ec] = std::from_chars(req, req + 4, code); - if (ec == std::errc()) { - strcpy(str, ptr + 1); - } else { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << "Received invalid code: " << code - << std::endl; - ERR_EXIT("Invalid request %d%s", code, ptr); - } - return code; -} - -/** - * Write offset to response buffer of process @param tid - * @param tid - * @param offset - * @return - */ -inline void write_response(int tid, off64_t offset) { - START_LOG(gettid(), "call(tid=%d, offset=%ld)", tid, offset); - - return bufs_response->at(tid)->write(&offset); -} - -#endif // CAPIO_SERVER_REQUESTS_HPP diff --git a/src/server/utils/signals.hpp b/src/server/utils/signals.hpp index d55253b34..7882fce6a 100644 --- a/src/server/utils/signals.hpp +++ b/src/server/utils/signals.hpp @@ -1,10 +1,9 @@ #ifndef CAPIO_SERVER_HANDLERS_SIGNALS_HPP #define CAPIO_SERVER_HANDLERS_SIGNALS_HPP +#include #include -#include "remote/backend.hpp" - #ifdef CAPIO_COVERAGE extern "C" void __gcov_dump(void); #endif @@ -20,12 +19,9 @@ void sig_term_handler(int signum, siginfo_t *info, void *ptr) { std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << "Segfault detected!" << std::endl; } - // free all the memory used - for (auto &it : get_capio_fds()) { - for (auto &fd : it.second) { - delete_capio_file_from_tid(it.first, fd); - } - } + + // TODO: free all the memory used + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "shm cleanup completed" << std::endl; for (auto &p : data_buffers) { @@ -44,9 +40,8 @@ void sig_term_handler(int signum, siginfo_t *info, void *ptr) { __gcov_dump(); #endif - destroy_server(); - delete backend; + delete cl_engine; delete shm_canary; std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << "shutdown completed" << std::endl; diff --git a/src/server/utils/types.hpp b/src/server/utils/types.hpp index b71caf185..616d664cc 100644 --- a/src/server/utils/types.hpp +++ b/src/server/utils/types.hpp @@ -8,9 +8,6 @@ #include "capio/queue.hpp" -#include "utils/capio_file.hpp" - -#include "capio_file.hpp" typedef std::unordered_map CSPidsMap_T; typedef std::unordered_map CSAppsMap_t; @@ -18,7 +15,6 @@ typedef std::unordered_map> CSFiles typedef std::unordered_map> CSProcessFileMetadataMap_t; typedef std::unordered_map> CSDataBufferMap_t; -typedef std::unordered_map CSFilesMetadata_t; typedef std::unordered_map< std::string, std::tuple> CSMetadataConfMap_t; From 00ac7d97969e2fb5566c08a115dd96e2e771a757 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Thu, 18 Jul 2024 15:17:20 +0200 Subject: [PATCH 002/151] storage_engine for metadata handling --- .github/workflows/ci-tests.yaml | 16 -- CMakeLists.txt | 2 +- src/common/capio/constants.hpp | 1 + src/common/capio/requests.hpp | 34 ++--- src/posix/utils/env.hpp | 2 +- src/posix/utils/requests.hpp | 3 +- src/server/capio_server.cpp | 7 +- src/server/cl-engine/cl_engine.hpp | 85 +++-------- ...cations.hpp => capio_cl_configuration.hpp} | 13 +- src/server/cl-engine/src/client_manager.hpp | 65 ++++++++ src/server/cl-engine/src/handlers.hpp | 11 ++ src/server/cl-engine/src/handlers/clone.hpp | 4 + src/server/cl-engine/src/handlers/close.hpp | 15 ++ src/server/cl-engine/src/handlers/consent.hpp | 31 ++++ src/server/cl-engine/src/handlers/create.hpp | 15 ++ src/server/cl-engine/src/handlers/exit.hpp | 15 ++ .../cl-engine/src/handlers/handshake.hpp | 21 +++ src/server/cl-engine/src/json_parser.hpp | 4 +- src/server/storage-engine/src/capio_file.hpp | 69 +++++++++ src/server/storage-engine/storage_engine.hpp | 141 ++++++++++++++++++ src/server/utils/distributed_semaphore.hpp | 52 +++++++ src/server/utils/signals.hpp | 5 +- tests/CMakeLists.txt | 1 - tests/unit/server/CMakeLists.txt | 42 ------ tests/unit/server/src/capio_file.cpp | 62 -------- 25 files changed, 503 insertions(+), 213 deletions(-) rename src/server/cl-engine/src/{capio_file_locations.hpp => capio_cl_configuration.hpp} (95%) create mode 100644 src/server/cl-engine/src/client_manager.hpp create mode 100644 src/server/cl-engine/src/handlers.hpp create mode 100644 src/server/cl-engine/src/handlers/clone.hpp create mode 100644 src/server/cl-engine/src/handlers/close.hpp create mode 100644 src/server/cl-engine/src/handlers/consent.hpp create mode 100644 src/server/cl-engine/src/handlers/create.hpp create mode 100644 src/server/cl-engine/src/handlers/exit.hpp create mode 100644 src/server/cl-engine/src/handlers/handshake.hpp create mode 100644 src/server/storage-engine/src/capio_file.hpp create mode 100644 src/server/storage-engine/storage_engine.hpp create mode 100644 src/server/utils/distributed_semaphore.hpp delete mode 100644 tests/unit/server/CMakeLists.txt delete mode 100644 tests/unit/server/src/capio_file.cpp diff --git a/.github/workflows/ci-tests.yaml b/.github/workflows/ci-tests.yaml index b06f95e46..98b980f1f 100644 --- a/.github/workflows/ci-tests.yaml +++ b/.github/workflows/ci-tests.yaml @@ -60,17 +60,6 @@ jobs: --gtest_break_on_failure \ --gtest_print_time=1 - echo "Run CAPIO syscall Unit tests" - docker run --rm \ - --env CAPIO_DIR=/tmp \ - --env CAPIO_LOG_LEVEL=-1 \ - --env LD_PRELOAD=libcapio_posix.so \ - --name capio-docker \ - alphaunito/capio:latest \ - capio_server_unit_tests \ - --gtest_break_on_failure \ - --gtest_print_time=1 - echo "Run CAPIO integration tests" docker run --rm \ --env CAPIO_DIR=/tmp \ @@ -176,11 +165,6 @@ jobs: --gtest_break_on_failure \ --gtest_print_time=1 - echo "Run CAPIO server Unit tests" - capio_server_unit_tests \ - --gtest_break_on_failure \ - --gtest_print_time=1 - echo "Run CAPIO syscall Unit tests" LD_PRELOAD=libcapio_posix.so \ capio_syscall_unit_tests \ diff --git a/CMakeLists.txt b/CMakeLists.txt index 4197b3718..6f8f488c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED TRUE) set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) # Set compiler flags -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -pedantic -O0") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -pedantic -O0 -Wno-gnu-zero-variadic-macro-arguments") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") ##################################### diff --git a/src/common/capio/constants.hpp b/src/common/capio/constants.hpp index 1fbd8aab5..7bd03dc9a 100644 --- a/src/common/capio/constants.hpp +++ b/src/common/capio/constants.hpp @@ -15,6 +15,7 @@ constexpr std::array CAPIO_DIR_FORBIDDEN_PATHS = {std::string_view{"/proc/"}, // CAPIO default values for shared memory constexpr char CAPIO_DEFAULT_WORKFLOW_NAME[] = "CAPIO"; +constexpr char CAPIO_DEFAULT_APP_NAME[] = "default_app"; constexpr char CAPIO_SHM_CANARY_ERROR[] = "FATAL ERROR: Shared memories for workflow %s already " "exists. One of two (or both) reasons are to blame: \n " diff --git a/src/common/capio/requests.hpp b/src/common/capio/requests.hpp index 8020f492a..e6d0ce733 100644 --- a/src/common/capio/requests.hpp +++ b/src/common/capio/requests.hpp @@ -2,25 +2,21 @@ #define CAPIO_COMMON_REQUESTS_HPP constexpr const int CAPIO_REQUEST_CONSENT = 0; -constexpr const int CAPIO_REQUEST_ACCESS = 1; -constexpr const int CAPIO_REQUEST_CLONE = 2; -constexpr const int CAPIO_REQUEST_CLOSE = 3; -constexpr const int CAPIO_REQUEST_CREATE = 4; -constexpr const int CAPIO_REQUEST_EXIT_GROUP = 5; -constexpr const int CAPIO_REQUEST_GETDENTS = 6; -constexpr const int CAPIO_REQUEST_GETDENTS64 = 7; -constexpr const int CAPIO_REQUEST_HANDSHAKE_NAMED = 8; -constexpr const int CAPIO_REQUEST_HANDSHAKE_ANONYMOUS = 9; -constexpr const int CAPIO_REQUEST_MKDIR = 10; -constexpr const int CAPIO_REQUEST_OPEN = 11; -constexpr const int CAPIO_REQUEST_READ = 12; -constexpr const int CAPIO_REQUEST_RENAME = 13; -constexpr const int CAPIO_REQUEST_SEEK = 14; -constexpr const int CAPIO_REQUEST_STAT = 15; -constexpr const int CAPIO_REQUEST_UNLINK = 16; -constexpr const int CAPIO_REQUEST_WRITE = 17; -constexpr const int CAPIO_REQUEST_RMDIR = 18; +constexpr const int CAPIO_REQUEST_CLONE = 1; +constexpr const int CAPIO_REQUEST_CLOSE = 2; +constexpr const int CAPIO_REQUEST_CREATE = 3; +constexpr const int CAPIO_REQUEST_EXIT_GROUP = 4; +constexpr const int CAPIO_REQUEST_HANDSHAKE_NAMED = 5; +constexpr const int CAPIO_REQUEST_HANDSHAKE_ANONYMOUS = 6; +constexpr const int CAPIO_REQUEST_MKDIR = 7; +constexpr const int CAPIO_REQUEST_OPEN = 8; +constexpr const int CAPIO_REQUEST_READ = 9; +constexpr const int CAPIO_REQUEST_RENAME = 10; +constexpr const int CAPIO_REQUEST_SEEK = 11; +constexpr const int CAPIO_REQUEST_UNLINK = 12; +constexpr const int CAPIO_REQUEST_WRITE = 13; +constexpr const int CAPIO_REQUEST_RMDIR = 14; -constexpr const int CAPIO_NR_REQUESTS = 19; +constexpr const int CAPIO_NR_REQUESTS = 15; #endif // CAPIO_COMMON_REQUESTS_HPP diff --git a/src/posix/utils/env.hpp b/src/posix/utils/env.hpp index 9da26571c..54cab6275 100644 --- a/src/posix/utils/env.hpp +++ b/src/posix/utils/env.hpp @@ -10,7 +10,7 @@ const char *get_capio_app_name() { static char *capio_app_name = std::getenv("CAPIO_APP_NAME"); if (capio_app_name == nullptr) { - return "default_app"; + return CAPIO_DEFAULT_APP_NAME; } return capio_app_name; } diff --git a/src/posix/utils/requests.hpp b/src/posix/utils/requests.hpp index 6f6178785..9e779908a 100644 --- a/src/posix/utils/requests.hpp +++ b/src/posix/utils/requests.hpp @@ -136,7 +136,8 @@ inline void open_request(const int fd, const std::filesystem::path &path, const // return amount of readable bytes inline off64_t read_request(const std::filesystem::path &path, const off64_t count, const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(path=%s, count=%ld, tid=%ld)", path, count, tid); + START_LOG(capio_syscall(SYS_gettid), "call(path=%s, count=%ld, tid=%ld)", path.c_str(), count, + tid); char req[CAPIO_REQ_MAX_SIZE]; sprintf(req, "%04d %s %ld %ld", CAPIO_REQUEST_READ, path.c_str(), tid, count); LOG("Sending read request %s", req); diff --git a/src/server/capio_server.cpp b/src/server/capio_server.cpp index 9bf495e2e..2ca94b736 100644 --- a/src/server/capio_server.cpp +++ b/src/server/capio_server.cpp @@ -21,6 +21,7 @@ #include std::string workflow_name; +char node_name[HOST_NAME_MAX]; #include "utils/types.hpp" @@ -35,6 +36,7 @@ CSDataBufferMap_t data_buffers; #include "utils/signals.hpp" #include "cl-engine/cl_engine.hpp" +#include "storage-engine/storage_engine.hpp" std::string parseCLI(int argc, char **argv) { Logger *log; @@ -171,6 +173,8 @@ int main(int argc, char **argv) { std::cout << CAPIO_LOG_SERVER_BANNER; + gethostname(node_name, HOST_NAME_MAX); + const std::string config_path = parseCLI(argc, argv); START_LOG(gettid(), "call()"); @@ -179,7 +183,8 @@ int main(int argc, char **argv) { setup_signal_handlers(); - cl_engine = new ClEngine(config_path); + storage_engine = new StorageEngine(); + cl_engine = new ClEngine(config_path); cl_engine->start(); return 0; diff --git a/src/server/cl-engine/cl_engine.hpp b/src/server/cl-engine/cl_engine.hpp index dd04e3a36..5ff48bba7 100644 --- a/src/server/cl-engine/cl_engine.hpp +++ b/src/server/cl-engine/cl_engine.hpp @@ -1,42 +1,39 @@ #ifndef CAPIO_CL_ENGINE_MAIN_HPP #define CAPIO_CL_ENGINE_MAIN_HPP #include "capio/requests.hpp" -#include "src/capio_file_locations.hpp" +#include "src/capio_cl_configuration.hpp" +#include "src/client_manager.hpp" #include "src/json_parser.hpp" +#include "src/handlers.hpp" + class ClEngine { private: //// Variables - CapioFileLocations *locations; std::array request_handlers; CSBufRequest_t *buf_requests; - CSBufResponse_t *bufs_response; //// Class methods static constexpr std::array build_request_handlers_table() { std::array _request_handlers{0}; - _request_handlers[CAPIO_REQUEST_ACCESS] = nullptr; - _request_handlers[CAPIO_REQUEST_CLONE] = nullptr; - _request_handlers[CAPIO_REQUEST_CLOSE] = nullptr; - _request_handlers[CAPIO_REQUEST_CREATE] = nullptr; - _request_handlers[CAPIO_REQUEST_EXIT_GROUP] = nullptr; - _request_handlers[CAPIO_REQUEST_GETDENTS] = nullptr; - _request_handlers[CAPIO_REQUEST_GETDENTS64] = nullptr; - _request_handlers[CAPIO_REQUEST_HANDSHAKE_NAMED] = nullptr; - _request_handlers[CAPIO_REQUEST_HANDSHAKE_ANONYMOUS] = nullptr; + _request_handlers[CAPIO_REQUEST_CONSENT] = consent_to_proceed_handler; + _request_handlers[CAPIO_REQUEST_CLONE] = clone_handler; + _request_handlers[CAPIO_REQUEST_CLOSE] = close_handler; + _request_handlers[CAPIO_REQUEST_CREATE] = create_handler; + _request_handlers[CAPIO_REQUEST_EXIT_GROUP] = exit_handler; + _request_handlers[CAPIO_REQUEST_HANDSHAKE_NAMED] = handshake_named_handler; + _request_handlers[CAPIO_REQUEST_HANDSHAKE_ANONYMOUS] = handshake_anonymous_handler; _request_handlers[CAPIO_REQUEST_MKDIR] = nullptr; _request_handlers[CAPIO_REQUEST_OPEN] = nullptr; _request_handlers[CAPIO_REQUEST_READ] = nullptr; _request_handlers[CAPIO_REQUEST_RENAME] = nullptr; - _request_handlers[CAPIO_REQUEST_RMDIR] = nullptr; - _request_handlers[CAPIO_REQUEST_SEEK] = nullptr; _request_handlers[CAPIO_REQUEST_SEEK] = nullptr; - _request_handlers[CAPIO_REQUEST_STAT] = nullptr; _request_handlers[CAPIO_REQUEST_UNLINK] = nullptr; _request_handlers[CAPIO_REQUEST_WRITE] = nullptr; + _request_handlers[CAPIO_REQUEST_RMDIR] = nullptr; return _request_handlers; } @@ -66,64 +63,24 @@ class ClEngine { explicit ClEngine(const std::filesystem::path &json_path) { START_LOG(gettid(), "call(path=%s)", json_path.c_str()); - locations = JsonParser::parse(json_path); - request_handlers = build_request_handlers_table(); + client_manager = new ClientManager(); + capio_configuration = JsonParser::parse(json_path); + request_handlers = build_request_handlers_table(); + + buf_requests = new CSBufRequest_t(SHM_COMM_CHAN_NAME, CAPIO_REQ_BUFF_CNT, + CAPIO_REQ_MAX_SIZE, workflow_name); - buf_requests = new CSBufRequest_t(SHM_COMM_CHAN_NAME, CAPIO_REQ_BUFF_CNT, - CAPIO_REQ_MAX_SIZE, workflow_name); - bufs_response = new CSBufResponse_t(); - locations->print(); + capio_configuration->print(); std::cout << CAPIO_SERVER_CLI_LOG_SERVER - << " CL-Engine initialization completed. ready to listen for incoming requests" << std::endl; + << " CL-Engine initialization completed. ready to listen for incoming requests" + << std::endl; } ~ClEngine() { delete buf_requests; std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_requests cleanup completed" << std::endl; - - delete bufs_response; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_response cleanup completed" - << std::endl; - } - - /** - * Add a new response buffer for thread @param tid - * @param tid - * @return - */ - inline void register_new_client(long tid) const { - // TODO: replace numbers with constexpr - auto *p_buf_response = - new CircularBuffer(SHM_COMM_CHAN_NAME_RESP + std::to_string(tid), - CAPIO_REQ_BUFF_CNT, sizeof(off_t), workflow_name); - bufs_response->insert(std::make_pair(tid, p_buf_response)); - } - - /** - * Delete the response buffer associated with thread @param tid - * @param tid - * @return - */ - inline void remove_client(int tid) { - auto it_resp = bufs_response->find(tid); - if (it_resp != bufs_response->end()) { - delete it_resp->second; - bufs_response->erase(it_resp); - } - } - - /** - * Write offset to response buffer of process @param tid - * @param tid - * @param offset - * @return - */ - inline void reply_to_client(int tid, off64_t offset) { - START_LOG(gettid(), "call(tid=%d, offset=%ld)", tid, offset); - - return bufs_response->at(tid)->write(&offset); } [[noreturn]] void start() { diff --git a/src/server/cl-engine/src/capio_file_locations.hpp b/src/server/cl-engine/src/capio_cl_configuration.hpp similarity index 95% rename from src/server/cl-engine/src/capio_file_locations.hpp rename to src/server/cl-engine/src/capio_cl_configuration.hpp index 6dcff4230..e201b0fb9 100644 --- a/src/server/cl-engine/src/capio_file_locations.hpp +++ b/src/server/cl-engine/src/capio_cl_configuration.hpp @@ -1,7 +1,7 @@ #ifndef CAPIO_ENGINE_HPP #define CAPIO_ENGINE_HPP -class CapioFileLocations { +class CapioCLConfiguration { private: std::unordered_map, // Vector for producers @@ -136,11 +136,21 @@ class CapioFileLocations { std::get<2>(_locations.at(path)) = commit_rule; } + inline auto getCommitRule(const std::string &path) { + this->newFile(path); + return std::get<2>(_locations.at(path)); + } + inline void setFireRule(const std::string &path, const std::string &fire_rule) { this->newFile(path); std::get<3>(_locations.at(path)) = fire_rule; } + inline auto getFireRule(const std::string &path) { + this->newFile(path); + return std::get<3>(_locations.at(path)); + } + inline void setPermanent(const std::string &path, bool value) { this->newFile(path); std::get<5>(_locations.at(path)) = value; @@ -184,4 +194,5 @@ class CapioFileLocations { inline auto consumers(const std::string &path) { return std::get<1>(_locations.at(path)); } }; +inline CapioCLConfiguration *capio_configuration; #endif // CAPIO_ENGINE_HPP diff --git a/src/server/cl-engine/src/client_manager.hpp b/src/server/cl-engine/src/client_manager.hpp new file mode 100644 index 000000000..046709fc5 --- /dev/null +++ b/src/server/cl-engine/src/client_manager.hpp @@ -0,0 +1,65 @@ +#ifndef CLIENT_MANAGER_HPP +#define CLIENT_MANAGER_HPP + +class ClientManager { + private: + CSBufResponse_t *bufs_response; + std::unordered_map *app_names; + + public: + ClientManager() { + bufs_response = new CSBufResponse_t(); + app_names = new std::unordered_map; + } + + ~ClientManager() { + delete bufs_response; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_response cleanup completed" + << std::endl; + } + + /** + * Add a new response buffer for thread @param tid + * @param tid + * @return + */ + inline void register_new_client(long tid, const std::string &app_name) const { + // TODO: replace numbers with constexpr + auto *p_buf_response = + new CircularBuffer(SHM_COMM_CHAN_NAME_RESP + std::to_string(tid), + CAPIO_REQ_BUFF_CNT, sizeof(off_t), workflow_name); + bufs_response->insert(std::make_pair(tid, p_buf_response)); + app_names->emplace(tid, app_name); + std::cout << CAPIO_SERVER_CLI_LOG_SERVER << "Handshaked a new client: <" << tid << "," + << app_name << ">" << std::endl; + } + + /** + * Delete the response buffer associated with thread @param tid + * @param tid + * @return + */ + inline void remove_client(int tid) { + auto it_resp = bufs_response->find(tid); + if (it_resp != bufs_response->end()) { + delete it_resp->second; + bufs_response->erase(it_resp); + } + } + + /** + * Write offset to response buffer of process @param tid + * @param tid + * @param offset + * @return + */ + inline void reply_to_client(long tid, off64_t offset) { + START_LOG(gettid(), "call(tid=%ld, offset=%ld)", tid, offset); + + return bufs_response->at(tid)->write(&offset); + } +}; + +inline ClientManager *client_manager; + +#endif // CLIENT_MANAGER_HPP diff --git a/src/server/cl-engine/src/handlers.hpp b/src/server/cl-engine/src/handlers.hpp new file mode 100644 index 000000000..c28e2298c --- /dev/null +++ b/src/server/cl-engine/src/handlers.hpp @@ -0,0 +1,11 @@ +#ifndef HANDLERS_HPP +#define HANDLERS_HPP + +#include "handlers/clone.hpp" +#include "handlers/close.hpp" +#include "handlers/consent.hpp" +#include "handlers/create.hpp" +#include "handlers/exit.hpp" +#include "handlers/handshake.hpp" + +#endif // HANDLERS_HPP diff --git a/src/server/cl-engine/src/handlers/clone.hpp b/src/server/cl-engine/src/handlers/clone.hpp new file mode 100644 index 000000000..c6052e5a5 --- /dev/null +++ b/src/server/cl-engine/src/handlers/clone.hpp @@ -0,0 +1,4 @@ +#ifndef CAPIO_CLONE_HPP +#define CAPIO_CLONE_HPP +void clone_handler(const char *const str) { START_LOG(gettid(), "call(str=%s)", str); } +#endif // CAPIO_CLONE_HPP diff --git a/src/server/cl-engine/src/handlers/close.hpp b/src/server/cl-engine/src/handlers/close.hpp new file mode 100644 index 000000000..8ec4309b2 --- /dev/null +++ b/src/server/cl-engine/src/handlers/close.hpp @@ -0,0 +1,15 @@ +#ifndef CAPIO_CLOSE_HPP +#define CAPIO_CLOSE_HPP +#include +#include + +inline void close_handler(const char *const str) { + int tid; + char path[PATH_MAX]; + sscanf(str, "%d %s", &tid, path); + std::filesystem::path filename(path); + auto n_close = std::get<1>(storage_engine->get_metadata(filename)) + 1; + storage_engine->update_n_close(filename, n_close); +} + +#endif // CAPIO_CLOSE_HPP diff --git a/src/server/cl-engine/src/handlers/consent.hpp b/src/server/cl-engine/src/handlers/consent.hpp new file mode 100644 index 000000000..44cecf697 --- /dev/null +++ b/src/server/cl-engine/src/handlers/consent.hpp @@ -0,0 +1,31 @@ +#ifndef CONSENT_HPP +#define CONSENT_HPP +#include +#include + +/* +This handler only checks if the client is allowed to continue +*/ + +inline void consent_to_proceed_handler(const char *const str) { + int tid; + char path[1024]; + sscanf(str, "%d %s", &tid, path); + + std::filesystem::path path_fs(path); + + //Skip operations on CAPIO_DIR + if(path_fs == get_capio_dir()) { + client_manager->reply_to_client(tid, 1); + return; + } + + if (storage_engine->is_committed(path_fs, tid) || + capio_configuration->getFireRule(path_fs) == CAPIO_FILE_MODE_NO_UPDATE) { + client_manager->reply_to_client(tid, 1); + } else { + storage_engine->add_thread_awaiting_for_commit(path, tid); + } +} + +#endif // CONSENT_HPP diff --git a/src/server/cl-engine/src/handlers/create.hpp b/src/server/cl-engine/src/handlers/create.hpp new file mode 100644 index 000000000..120546735 --- /dev/null +++ b/src/server/cl-engine/src/handlers/create.hpp @@ -0,0 +1,15 @@ +#ifndef CAPIO_CREATE_HPP +#define CAPIO_CREATE_HPP + +#include +#include + +inline void create_handler(const char *const str) { + int tid; + char path[PATH_MAX]; + sscanf(str, "%d %s", &tid, path); + std::filesystem::path filename(path); + storage_engine->create_capio_file(path, tid); +} + +#endif // CAPIO_CREATE_HPP diff --git a/src/server/cl-engine/src/handlers/exit.hpp b/src/server/cl-engine/src/handlers/exit.hpp new file mode 100644 index 000000000..d4d61831e --- /dev/null +++ b/src/server/cl-engine/src/handlers/exit.hpp @@ -0,0 +1,15 @@ +#ifndef CAPIO_EXIT_HPP +#define CAPIO_EXIT_HPP +#include +#include + +inline void exit_handler(const char *const str) { + //TODO: register files open for each tid ti register a close + int tid; + char path[PATH_MAX]; + sscanf(str, "%d", &tid); + + storage_engine->close_all_files(tid); +} + +#endif // CAPIO_EXIT_HPP diff --git a/src/server/cl-engine/src/handlers/handshake.hpp b/src/server/cl-engine/src/handlers/handshake.hpp new file mode 100644 index 000000000..1f645c483 --- /dev/null +++ b/src/server/cl-engine/src/handlers/handshake.hpp @@ -0,0 +1,21 @@ +#ifndef HANDSHAKE_HPP +#define HANDSHAKE_HPP + +#include "capio/constants.hpp" + +inline void handshake_anonymous_handler(const char *const str) { + int tid, pid; + sscanf(str, "%d %d", &tid, &pid); + START_LOG(gettid(), "call(tid=%ld, pid=%ld)", tid, pid); + client_manager->register_new_client(tid, CAPIO_DEFAULT_APP_NAME); +} + +inline void handshake_named_handler(const char *const str) { + int tid, pid; + char app_name[1024]; + sscanf(str, "%d %d %s", &tid, &pid, app_name); + START_LOG(gettid(), "call(tid=%ld, pid=%ld, app_name=%s)", tid, pid, app_name); + client_manager->register_new_client(tid, app_name); +} + +#endif // HANDSHAKE_HPP diff --git a/src/server/cl-engine/src/json_parser.hpp b/src/server/cl-engine/src/json_parser.hpp index e690ebe13..1d2e67864 100644 --- a/src/server/cl-engine/src/json_parser.hpp +++ b/src/server/cl-engine/src/json_parser.hpp @@ -4,8 +4,8 @@ class JsonParser { public: - static CapioFileLocations *parse(const std::filesystem::path &source) { - auto locations = new CapioFileLocations(); + static CapioCLConfiguration *parse(const std::filesystem::path &source) { + auto locations = new CapioCLConfiguration(); auto capio_dir = get_capio_dir(); START_LOG(gettid(), "call(config_file='%s', capio_dir='%s')", source.c_str(), diff --git a/src/server/storage-engine/src/capio_file.hpp b/src/server/storage-engine/src/capio_file.hpp new file mode 100644 index 000000000..611897685 --- /dev/null +++ b/src/server/storage-engine/src/capio_file.hpp @@ -0,0 +1,69 @@ +#ifndef CAPIO_FILE_HPP +#define CAPIO_FILE_HPP + +#include "utils/distributed_semaphore.hpp" + +// TODO: concurrency is kinda guaranteed but more checks needs to be performed. +// TODO: as writes are below PIPE_MAX the distributed semaphore might not be needed + +class CapioFile { + private: + std::string name; + std::string metadataname; + FILE *metadata_file_fd = nullptr; + long _size = 0, _n_close = 0; + int _committed = 0; + + public: + explicit CapioFile(std::filesystem::path filename) : name(filename) { + metadataname = name + ".capio"; + metadata_file_fd = + fopen(metadataname.c_str(), + "w+"); // open (or create if not exists) metadata associated with file + } + + ~CapioFile() { fclose(metadata_file_fd); } + + /** + * Query metadata related to the descirbed file + * @return a tuple of + */ + std::tuple get_metadata() { + + fseek(metadata_file_fd, 0, SEEK_SET); + fscanf(metadata_file_fd, "%ld %ld %d", &_size, &_n_close, &_committed); + + return {_size, _n_close, _committed}; + } + + void update_metadata(long filesize, long n_close, bool committed) { + DistributedSemaphore lock(metadataname, 100); + get_metadata(); + + if (filesize != _size) { + _size = filesize; + } + if (n_close != _n_close) { + _n_close = n_close; + } + if (committed != _committed) { + _committed = committed; + } + + fseek(metadata_file_fd, 0, SEEK_SET); + fprintf(metadata_file_fd, "%ld %ld %d", filesize, n_close, committed ? 1 : 0); + } + + void update_size(long size) { update_metadata(size, _n_close, _committed); } + + void update_n_close(long n_close) { update_metadata(_size, n_close, _committed); } + + void set_committed() { update_metadata(_size, _n_close, _committed); } + + bool is_committed() { + get_metadata(); + return _committed; + } +}; + +#endif // CAPIO_FILE_HPP diff --git a/src/server/storage-engine/storage_engine.hpp b/src/server/storage-engine/storage_engine.hpp new file mode 100644 index 000000000..45a4b6c13 --- /dev/null +++ b/src/server/storage-engine/storage_engine.hpp @@ -0,0 +1,141 @@ +#ifndef STORAGE_ENGINE_HPP +#define STORAGE_ENGINE_HPP +#include + +#include "src/capio_file.hpp" + +class StorageEngine { + private: + // path -> CapioFile (only for metadata) + std::unordered_map *open_metadata_descriptors; + + // path -> [tids waiting for response on path] + std::unordered_map *> *pending_requests_on_file; + + // tid, path -> number of open on file (used to know opened and how many times a file has been + // open by a thread) + std::unordered_map *> *threads_opened_files; + + std::mutex metadata_mutex; + + /** + * check if metadata associated with a given file exists. + * If it exists, then there is an assumption that the associated file is present + * @param path + * @return + */ + static bool exists(std::filesystem::path &path) { + std::filesystem::path check = path.string() + ".capio"; + return std::filesystem::exists(check); + } + + void unlock_awaiting_threads_for_commit(const std::filesystem::path &path) const { + if (open_metadata_descriptors->at(path)->is_committed()) { + auto queue = pending_requests_on_file->at(path); + for (long tid : *queue) { + client_manager->reply_to_client(tid, 1); + } + } + } + + public: + StorageEngine() { + open_metadata_descriptors = new std::unordered_map; + pending_requests_on_file = new std::unordered_map *>; + threads_opened_files = + new std::unordered_map *>; + } + + ~StorageEngine() { delete open_metadata_descriptors; } + + void create_capio_file(std::filesystem::path filename, long tid) { + std::lock_guard lg(metadata_mutex); + if (open_metadata_descriptors->find(filename) == open_metadata_descriptors->end()) { + open_metadata_descriptors->emplace(filename, new CapioFile(filename)); + } + + // register a new open on a file + if (threads_opened_files->find(tid) == threads_opened_files->end()) { + threads_opened_files->emplace(tid, new std::unordered_map); + } + + if (threads_opened_files->at(tid)->find(filename) == threads_opened_files->at(tid)->end()) { + threads_opened_files->at(tid)->emplace(filename, 1); + } else { + threads_opened_files->at(tid)->at(filename) = + threads_opened_files->at(tid)->at(filename) + 1; + } + } + + void deleteFile(std::filesystem::path path) { + std::lock_guard lg(metadata_mutex); + + std::filesystem::path metadata_name = path.string() + ".capio"; + std::filesystem::remove(metadata_name); + std::filesystem::remove(path); + open_metadata_descriptors->erase(open_metadata_descriptors->find(path)); + } + + auto get_metadata(std::filesystem::path &filename) { + std::lock_guard lg(metadata_mutex); + auto metadata = open_metadata_descriptors->at(filename)->get_metadata(); + unlock_awaiting_threads_for_commit( + filename); // if committed unlock threads waiting to continue + return metadata; + } + + void update_metadata(std::filesystem::path &filename, long filesize, long n_close, + bool committed) { + std::lock_guard lg(metadata_mutex); + open_metadata_descriptors->at(filename)->update_metadata(filesize, n_close, committed); + if (committed) { + unlock_awaiting_threads_for_commit(filename); + } + } + + void update_size(std::filesystem::path &filename, long size) { + std::lock_guard lg(metadata_mutex); + + open_metadata_descriptors->at(filename)->update_size(size); + } + + void update_n_close(std::filesystem::path &filename, long n_close) { + std::lock_guard lg(metadata_mutex); + + open_metadata_descriptors->at(filename)->update_n_close(n_close); + } + + void set_committed(std::filesystem::path &filename) const { + open_metadata_descriptors->at(filename)->set_committed(); + unlock_awaiting_threads_for_commit(filename); + } + + bool is_committed(std::filesystem::path &filename, long tid) { + this->create_capio_file(filename, tid); + return open_metadata_descriptors->at(filename)->is_committed(); + } + + void add_thread_awaiting_for_commit(const std::filesystem::path &filename, long tid) const { + if (pending_requests_on_file->find(filename) == pending_requests_on_file->end()) { + pending_requests_on_file->emplace(filename, new std::vector); + } + pending_requests_on_file->at(filename)->emplace_back(tid); + } + + void close_all_files(int tid) { + auto map = threads_opened_files->at(tid); + + for (auto entry : *map) { + std::filesystem::path filename(entry.first); + long opens = entry.second; + update_n_close(filename, opens); + // TODO: check if is committed + } + + threads_opened_files->erase(threads_opened_files->find(tid)); + } +}; + +StorageEngine *storage_engine; + +#endif // STORAGE_ENGINE_HPP diff --git a/src/server/utils/distributed_semaphore.hpp b/src/server/utils/distributed_semaphore.hpp new file mode 100644 index 000000000..22e800789 --- /dev/null +++ b/src/server/utils/distributed_semaphore.hpp @@ -0,0 +1,52 @@ +#ifndef DISTRIBUTEDSEMAPHORE_HPP +#define DISTRIBUTEDSEMAPHORE_HPP +class DistributedSemaphore { + private: + std::string name; + struct timespec sleep {}; + bool locked; + int fp; + + void lock() { + START_LOG(gettid(), "call(locking=%s)", name.c_str()); + if (!locked) { + while (fp == -1) { + nanosleep(&sleep, nullptr); + fp = open(name.c_str(), O_EXCL | O_CREAT | O_WRONLY, 0777); + } + LOG("Locked %s", name.c_str()); + if (write(fp, node_name, strlen(node_name)) == -1) { + ERR_EXIT("Unable to insert lock holder %s on lock file %s", node_name, + name.c_str()); + } + } + LOG("Completed spinlock on lock file %s", name.c_str()); + locked = true; + } + + void unlock() const { + START_LOG(gettid(), "call(unlocking=%s)", name.c_str()); + if (locked) { + close(fp); + unlink(name.c_str()); + } + LOG("Unlocked %s", name.c_str()); + } + + public: + DistributedSemaphore(std::string locking, int sleep_time) + : name(locking), locked(false), fp(-1) { + START_LOG(gettid(), "call(locking=%s, sleep_time=%ld)", name.c_str(), sleep_time); + sleep.tv_nsec = sleep_time; + + this->lock(); + } + + ~DistributedSemaphore() { + START_LOG(gettid(), "call()"); + if (locked) { + this->unlock(); + } + } +}; +#endif // DISTRIBUTEDSEMAPHORE_HPP diff --git a/src/server/utils/signals.hpp b/src/server/utils/signals.hpp index 7882fce6a..5592b19bd 100644 --- a/src/server/utils/signals.hpp +++ b/src/server/utils/signals.hpp @@ -3,6 +3,7 @@ #include #include +#include #ifdef CAPIO_COVERAGE extern "C" void __gcov_dump(void); @@ -19,8 +20,9 @@ void sig_term_handler(int signum, siginfo_t *info, void *ptr) { std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << "Segfault detected!" << std::endl; } - // TODO: free all the memory used + delete cl_engine; + delete storage_engine; std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "shm cleanup completed" << std::endl; @@ -40,7 +42,6 @@ void sig_term_handler(int signum, siginfo_t *info, void *ptr) { __gcov_dump(); #endif - delete cl_engine; delete shm_canary; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fccf68a2f..79aac6cfa 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,6 +18,5 @@ include(GoogleTest) # Targets ##################################### add_subdirectory(unit/posix) -add_subdirectory(unit/server) add_subdirectory(unit/syscall) add_subdirectory(integration) diff --git a/tests/unit/server/CMakeLists.txt b/tests/unit/server/CMakeLists.txt deleted file mode 100644 index 38931c6f3..000000000 --- a/tests/unit/server/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -##################################### -# Target information -##################################### -set(TARGET_NAME capio_server_unit_tests) -set(TARGET_INCLUDE_FOLDER "${PROJECT_SOURCE_DIR}/src/server") -set(TARGET_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/src/capio_file.cpp -) - -##################################### -# Target definition -##################################### -add_executable(${TARGET_NAME} ${TARGET_SOURCES}) - -##################################### -# Include files and directories -##################################### -file(GLOB_RECURSE CAPIO_SERVER_HEADERS "${TARGET_INCLUDE_FOLDER}/*.hpp") -target_sources(${TARGET_NAME} PRIVATE - "${CAPIO_COMMON_HEADERS}" - "${CAPIO_SERVER_HEADERS}" -) -target_include_directories(${TARGET_NAME} PRIVATE ${TARGET_INCLUDE_FOLDER}) - -##################################### -# Link libraries -##################################### -target_link_libraries(${TARGET_NAME} PRIVATE GTest::gtest_main rt) - -##################################### -# Configure tests -##################################### -gtest_discover_tests(${TARGET_NAME} - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} -) - -##################################### -# Install rules -##################################### -install(TARGETS ${TARGET_NAME} - LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR} -) \ No newline at end of file diff --git a/tests/unit/server/src/capio_file.cpp b/tests/unit/server/src/capio_file.cpp deleted file mode 100644 index 51e7c6041..000000000 --- a/tests/unit/server/src/capio_file.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include - -#include - -#include "capio/env.hpp" -#include "utils/capio_file.hpp" - -TEST(ServerTest, TestInsertSingleSector) { - CapioFile c_file; - c_file.insert_sector(1, 3); - auto §ors = c_file.get_sectors(); - EXPECT_EQ(sectors.size(), 1); - EXPECT_NE(sectors.find({1L, 3L}), sectors.end()); -} - -TEST(ServerTest, TestInsertTwoNonOverlappingSectors) { - CapioFile c_file; - c_file.insert_sector(5, 7); - c_file.insert_sector(1, 3); - auto §ors = c_file.get_sectors(); - EXPECT_EQ(sectors.size(), 2); - auto it = sectors.begin(); - EXPECT_EQ(std::make_pair(1L, 3L), *it); - std::advance(it, 1); - EXPECT_EQ(std::make_pair(5L, 7L), *it); -} - -TEST(ServerTest, TestInsertTwoOverlappingSectors) { - CapioFile c_file; - c_file.insert_sector(2, 4); - c_file.insert_sector(1, 3); - auto §ors = c_file.get_sectors(); - EXPECT_EQ(sectors.size(), 1); - EXPECT_NE(sectors.find({1L, 4L}), sectors.end()); -} - -TEST(ServerTest, TestInsertTwoOverlappingSectorsSameStart) { - CapioFile c_file; - c_file.insert_sector(1, 4); - c_file.insert_sector(1, 3); - auto §ors = c_file.get_sectors(); - EXPECT_EQ(sectors.size(), 1); - EXPECT_NE(sectors.find({1L, 4L}), sectors.end()); -} - -TEST(ServerTest, TestInsertTwoOverlappingSectorsSameEnd) { - CapioFile c_file; - c_file.insert_sector(1, 4); - c_file.insert_sector(2, 4); - auto §ors = c_file.get_sectors(); - EXPECT_EQ(sectors.size(), 1); - EXPECT_NE(sectors.find({1L, 4L}), sectors.end()); -} - -TEST(ServerTest, TestInsertTwoOverlappingSectorsNested) { - CapioFile c_file; - c_file.insert_sector(1, 4); - c_file.insert_sector(2, 3); - auto §ors = c_file.get_sectors(); - EXPECT_EQ(sectors.size(), 1); - EXPECT_NE(sectors.find({1L, 4L}), sectors.end()); -} \ No newline at end of file From 4ebc302fff25e69ac7d1299be019cf198b770d9b Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Fri, 19 Jul 2024 11:46:29 +0200 Subject: [PATCH 003/151] Improved systemcall handlers --- .github/workflows/ci-tests.yaml | 2 +- src/common/capio/constants.hpp | 2 +- src/common/capio/requests.hpp | 6 +- src/posix/handlers/fchmod.hpp | 2 +- src/posix/handlers/fchown.hpp | 2 +- src/posix/handlers/fork.hpp | 1 - src/posix/handlers/getcwd.hpp | 3 +- src/posix/handlers/getdents.hpp | 2 - src/posix/handlers/lseek.hpp | 14 +- src/posix/handlers/mkdir.hpp | 20 +-- src/posix/handlers/open.hpp | 129 ++++++++++------ src/posix/handlers/read.hpp | 22 ++- src/posix/handlers/rename.hpp | 23 +-- src/posix/handlers/unlink.hpp | 21 ++- src/posix/handlers/write.hpp | 2 +- src/posix/utils/clone.hpp | 1 - src/posix/utils/filesystem.hpp | 36 +++-- src/posix/utils/requests.hpp | 68 +++------ src/server/capio_server.cpp | 4 +- src/server/cl-engine/cl_engine.hpp | 15 +- .../cl-engine/src/capio_cl_configuration.hpp | 18 +++ src/server/cl-engine/src/client_manager.hpp | 59 +++++++- src/server/cl-engine/src/handlers.hpp | 6 +- src/server/cl-engine/src/handlers/clone.hpp | 4 - src/server/cl-engine/src/handlers/close.hpp | 10 +- src/server/cl-engine/src/handlers/consent.hpp | 20 ++- src/server/cl-engine/src/handlers/create.hpp | 5 +- src/server/cl-engine/src/handlers/exit.hpp | 5 +- src/server/cl-engine/src/handlers/open.hpp | 15 ++ src/server/cl-engine/src/handlers/read.hpp | 34 +++++ src/server/cl-engine/src/handlers/rename.hpp | 14 ++ src/server/cl-engine/src/handlers/seek.hpp | 23 +++ src/server/cl-engine/src/handlers/write.hpp | 18 +++ src/server/storage-engine/src/capio_file.hpp | 69 --------- src/server/storage-engine/storage_engine.hpp | 141 ------------------ src/server/utils/common.hpp | 3 - src/server/utils/filesystem.hpp | 108 -------------- src/server/utils/producer.hpp | 38 ----- src/server/utils/signals.hpp | 5 - src/server/utils/types.hpp | 1 - 40 files changed, 405 insertions(+), 566 deletions(-) delete mode 100644 src/server/cl-engine/src/handlers/clone.hpp create mode 100644 src/server/cl-engine/src/handlers/open.hpp create mode 100644 src/server/cl-engine/src/handlers/read.hpp create mode 100644 src/server/cl-engine/src/handlers/rename.hpp create mode 100644 src/server/cl-engine/src/handlers/seek.hpp create mode 100644 src/server/cl-engine/src/handlers/write.hpp delete mode 100644 src/server/storage-engine/src/capio_file.hpp delete mode 100644 src/server/storage-engine/storage_engine.hpp delete mode 100644 src/server/utils/filesystem.hpp delete mode 100644 src/server/utils/producer.hpp diff --git a/.github/workflows/ci-tests.yaml b/.github/workflows/ci-tests.yaml index 98b980f1f..d48fcd2c8 100644 --- a/.github/workflows/ci-tests.yaml +++ b/.github/workflows/ci-tests.yaml @@ -153,7 +153,7 @@ jobs: sudo cmake --install ../build --prefix /usr/local - name: "Run tests" id: run-tests - timeout-minutes: 5 + timeout-minutes: 2 env: CAPIO_DIR: ${{ github.workspace }} CAPIO_LOG_LEVEL: -1 diff --git a/src/common/capio/constants.hpp b/src/common/capio/constants.hpp index 7bd03dc9a..96714ed12 100644 --- a/src/common/capio/constants.hpp +++ b/src/common/capio/constants.hpp @@ -15,7 +15,7 @@ constexpr std::array CAPIO_DIR_FORBIDDEN_PATHS = {std::string_view{"/proc/"}, // CAPIO default values for shared memory constexpr char CAPIO_DEFAULT_WORKFLOW_NAME[] = "CAPIO"; -constexpr char CAPIO_DEFAULT_APP_NAME[] = "default_app"; +constexpr char CAPIO_DEFAULT_APP_NAME[] = "default_app"; constexpr char CAPIO_SHM_CANARY_ERROR[] = "FATAL ERROR: Shared memories for workflow %s already " "exists. One of two (or both) reasons are to blame: \n " diff --git a/src/common/capio/requests.hpp b/src/common/capio/requests.hpp index e6d0ce733..1fd8a8136 100644 --- a/src/common/capio/requests.hpp +++ b/src/common/capio/requests.hpp @@ -13,10 +13,8 @@ constexpr const int CAPIO_REQUEST_OPEN = 8; constexpr const int CAPIO_REQUEST_READ = 9; constexpr const int CAPIO_REQUEST_RENAME = 10; constexpr const int CAPIO_REQUEST_SEEK = 11; -constexpr const int CAPIO_REQUEST_UNLINK = 12; -constexpr const int CAPIO_REQUEST_WRITE = 13; -constexpr const int CAPIO_REQUEST_RMDIR = 14; +constexpr const int CAPIO_REQUEST_WRITE = 12; -constexpr const int CAPIO_NR_REQUESTS = 15; +constexpr const int CAPIO_NR_REQUESTS = 13; #endif // CAPIO_COMMON_REQUESTS_HPP diff --git a/src/posix/handlers/fchmod.hpp b/src/posix/handlers/fchmod.hpp index c338461c2..22ffa5c3d 100644 --- a/src/posix/handlers/fchmod.hpp +++ b/src/posix/handlers/fchmod.hpp @@ -4,7 +4,7 @@ #if defined(SYS_chmod) int fchmod_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - int fd = static_cast(arg0); + int fd = static_cast(arg0); long tid = syscall_no_intercept(SYS_gettid); START_LOG(syscall_no_intercept(SYS_gettid), "call(fd=%d)", fd); diff --git a/src/posix/handlers/fchown.hpp b/src/posix/handlers/fchown.hpp index 78b034658..5d5982d07 100644 --- a/src/posix/handlers/fchown.hpp +++ b/src/posix/handlers/fchown.hpp @@ -5,7 +5,7 @@ #if defined(SYS_chown) int fchown_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - int fd = static_cast(arg0); + int fd = static_cast(arg0); long tid = syscall_no_intercept(SYS_gettid); START_LOG(syscall_no_intercept(SYS_gettid), "call(fd=%d)", fd); diff --git a/src/posix/handlers/fork.hpp b/src/posix/handlers/fork.hpp index f4067186c..6ba163c7b 100644 --- a/src/posix/handlers/fork.hpp +++ b/src/posix/handlers/fork.hpp @@ -15,7 +15,6 @@ int fork_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg if (pid == 0) { // child auto child_tid = static_cast(syscall_no_intercept(SYS_gettid)); init_process(child_tid); - clone_request(parent_tid, child_tid); *result = 0; } else { *result = pid; diff --git a/src/posix/handlers/getcwd.hpp b/src/posix/handlers/getcwd.hpp index 33af40c6c..2431d73ed 100644 --- a/src/posix/handlers/getcwd.hpp +++ b/src/posix/handlers/getcwd.hpp @@ -6,9 +6,8 @@ int getcwd_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { auto buf = reinterpret_cast(arg0); auto size = static_cast(arg1); - long tid = syscall_no_intercept(SYS_gettid); - START_LOG(tid, "call(buf=0x%08x, size=%ld)", buf, size); + START_LOG(syscall_no_intercept(SYS_gettid), "call(buf=0x%08x, size=%ld)", buf, size); return CAPIO_POSIX_SYSCALL_SKIP; } diff --git a/src/posix/handlers/getdents.hpp b/src/posix/handlers/getdents.hpp index 231d2b27f..9bcdd6ce6 100644 --- a/src/posix/handlers/getdents.hpp +++ b/src/posix/handlers/getdents.hpp @@ -3,8 +3,6 @@ #if defined(SYS_getdents) || defined(SYS_getdents64) - - // TODO: too similar to capio_read, refactoring needed inline int getdents_handler_impl(long arg0, long arg1, long arg2, long *result, bool is64bit) { auto fd = static_cast(arg0); diff --git a/src/posix/handlers/lseek.hpp b/src/posix/handlers/lseek.hpp index 0faf3fb82..2a04498d7 100644 --- a/src/posix/handlers/lseek.hpp +++ b/src/posix/handlers/lseek.hpp @@ -13,10 +13,18 @@ int lseek_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long ar START_LOG(tid, "call(fd=%d, offset=%ld, whence=%d)", fd, offset, whence); if (exists_capio_fd(fd)) { - (get_capio_fd_path(fd), tid); - } + off64_t computed_offset = 0; + + if (whence == SEEK_CUR) { + computed_offset = get_capio_fd_offset(fd) + offset; + } else { + computed_offset = offset; + } - seek_request(get_capio_fd_path(fd), offset, whence, tid); + // computed_offset = seek_request(get_capio_fd_path(fd), computed_offset, whence, tid, fd); + + set_capio_fd_offset(fd, computed_offset); + } return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } diff --git a/src/posix/handlers/mkdir.hpp b/src/posix/handlers/mkdir.hpp index 445ebb447..825e85ed0 100644 --- a/src/posix/handlers/mkdir.hpp +++ b/src/posix/handlers/mkdir.hpp @@ -34,7 +34,8 @@ inline off64_t capio_mkdirat(int dirfd, const std::string_view &pathname, mode_t } if (is_capio_path(path)) { - mkdir_request(path, tid); + + create_request(-1, path, tid); } return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } @@ -42,23 +43,6 @@ inline off64_t capio_mkdirat(int dirfd, const std::string_view &pathname, mode_t inline off64_t capio_rmdir(const std::string_view &pathname, long tid) { START_LOG(tid, "call(pathname=%s)", pathname.data()); - if (is_forbidden_path(pathname)) { - LOG("Path %s is forbidden: skip", pathname.data()); - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; - } - - std::filesystem::path path(pathname); - if (path.is_relative()) { - path = capio_posix_realpath(path); - if (path.empty()) { - LOG("path_to_check.len = 0!"); - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; - } - } - - if (is_capio_path(path)) { - rmdir_request(path, tid); - } return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } diff --git a/src/posix/handlers/open.hpp b/src/posix/handlers/open.hpp index 4483c39cc..5428d3c7c 100644 --- a/src/posix/handlers/open.hpp +++ b/src/posix/handlers/open.hpp @@ -6,85 +6,124 @@ #include "utils/common.hpp" #include "utils/filesystem.hpp" -inline int capio_openat(int dirfd, const std::string_view &pathname, int flags, long tid) { - START_LOG(tid, "call(dirfd=%d, pathname=%s, flags=%X)", dirfd, pathname.data(), flags); - - if (is_forbidden_path(pathname)) { - LOG("Path %s is forbidden: skip", pathname.data()); - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; - } - +std::string compute_abs_path(char *pathname, int dirfd) { + START_LOG(syscall_no_intercept(SYS_gettid), "call(pathname=%s, dirfd=%d)", pathname, dirfd); std::filesystem::path path(pathname); if (path.is_relative()) { if (dirfd == AT_FDCWD) { path = capio_posix_realpath(path); if (path.empty()) { - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; + LOG("path empty AT_FDCWD"); + return ""; } } else { if (!is_directory(dirfd)) { LOG("dirfd does not point to a directory"); - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; + return ""; } const std::filesystem::path dir_path = get_dir_path(dirfd); if (dir_path.empty()) { - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; + LOG("path empty"); + return ""; } path = (dir_path / path).lexically_normal(); + LOG("path = %s", path.c_str()); } } + return path; +} - if (is_capio_path(path)) { +int creat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { + std::string pathname(reinterpret_cast(arg0)); + long tid = syscall_no_intercept(SYS_gettid); + int flags = O_CREAT | O_WRONLY | O_TRUNC; + mode_t mode = static_cast(arg2); + START_LOG(tid, "call(path=%s, flags=%d, mode=%d)", pathname.data(), flags, mode); - auto fd = static_cast(syscall_no_intercept(SYS_open, path.c_str(), flags)); + std::string path = compute_abs_path(pathname.data(), -1); - bool create = (flags & O_CREAT) == O_CREAT; - bool excl = (flags & O_EXCL) == O_EXCL; + if (is_capio_path(path)) { + create_request(-1, path.data(), tid); + LOG("Create request sent"); + } - if (excl | create) { - LOG("excl"); - create_request(fd, path, tid); - } else { - LOG("!excl && !create"); - open_request(fd, path, tid); - } + int fd = static_cast(syscall_no_intercept(SYS_creat, arg0, arg1, arg2, arg3, arg4, arg5)); - int actual_flags = flags & ~O_CLOEXEC; - if ((flags & O_DIRECTORY) == O_DIRECTORY) { - actual_flags = actual_flags | O_LARGEFILE; - } - add_capio_fd(tid, path, fd, 0, CAPIO_DEFAULT_FILE_INITIAL_SIZE, actual_flags, - (flags & O_CLOEXEC) == O_CLOEXEC); + LOG("fd=%d", fd); - return fd; + if (is_capio_path(path) && fd >= 0) { + LOG("Registering path and fd"); + add_capio_fd(tid, path, fd, 0, CAPIO_DEFAULT_FILE_INITIAL_SIZE, flags, + (flags & O_CLOEXEC) == O_CLOEXEC); } - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; -} -int creat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - const std::string_view pathname(reinterpret_cast(arg0)); - long tid = syscall_no_intercept(SYS_gettid); - - return posix_return_value(capio_openat(AT_FDCWD, pathname, O_CREAT | O_WRONLY | O_TRUNC, tid), - result); + *result = fd; + return CAPIO_POSIX_SYSCALL_SUCCESS; } int open_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - const std::string_view pathname(reinterpret_cast(arg0)); - int flags = static_cast(arg1); - long tid = syscall_no_intercept(SYS_gettid); + std::string pathname(reinterpret_cast(arg0)); + int flags = static_cast(arg1); + mode_t mode = static_cast(arg2); + long tid = syscall_no_intercept(SYS_gettid); + START_LOG(tid, "call(path=%s, flags=%d, mode=%d)", pathname.data(), flags, mode); + + std::string path = compute_abs_path(pathname.data(), -1); + + if (is_capio_path(path)) { + if ((flags & O_CREAT) == O_CREAT) { + LOG("O_CREAT"); + create_request(-1, path.data(), tid); + } else { + LOG("not O_CREAT"); + open_request(-1, path.data(), tid); + } + } + + int fd = static_cast(syscall_no_intercept(SYS_open, arg0, arg1, arg2, arg3, arg4, arg5)); - return posix_return_value(capio_openat(AT_FDCWD, pathname, flags, tid), result); + if (is_capio_path(path) && fd >= 0) { + LOG("Adding capio path"); + add_capio_fd(tid, path, fd, 0, CAPIO_DEFAULT_FILE_INITIAL_SIZE, flags, + (flags & O_CLOEXEC) == O_CLOEXEC); + } + + *result = fd; + return CAPIO_POSIX_SYSCALL_SUCCESS; } int openat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { int dirfd = static_cast(arg0); - const std::string_view pathname(reinterpret_cast(arg1)); - int flags = static_cast(arg2); - long tid = syscall_no_intercept(SYS_gettid); + std::string pathname(reinterpret_cast(arg1)); + int flags = static_cast(arg2); + mode_t mode = static_cast(arg3); + long tid = syscall_no_intercept(SYS_gettid); + START_LOG(tid, "call(path=%s, flags=%d, mode=%d)", pathname.data(), flags, mode); + + std::string path = compute_abs_path(pathname.data(), dirfd); + + if (is_capio_path(path)) { + if ((flags & O_CREAT) == O_CREAT) { + LOG("O_CREAT"); + create_request(-1, path.data(), tid); + } else { + LOG("not O_CREAT"); + open_request(-1, path.data(), tid); + } + } + + int fd = static_cast(syscall_no_intercept(SYS_openat, arg0, arg1, arg2, arg3, arg4, arg5)); + LOG("fd=%d", fd); + + if (is_capio_path(path) && fd >= 0) { + LOG("Adding capio path"); + add_capio_fd(tid, path, fd, 0, CAPIO_DEFAULT_FILE_INITIAL_SIZE, flags, + (flags & O_CLOEXEC) == O_CLOEXEC); + } - return posix_return_value(capio_openat(dirfd, pathname, flags, tid), result); + *result = fd; + return CAPIO_POSIX_SYSCALL_SUCCESS; } #endif // SYS_creat || SYS_open || SYS_openat diff --git a/src/posix/handlers/read.hpp b/src/posix/handlers/read.hpp index 101b6720e..9c71ce232 100644 --- a/src/posix/handlers/read.hpp +++ b/src/posix/handlers/read.hpp @@ -3,18 +3,8 @@ #if defined(SYS_read) || defined(SYS_readv) - inline off64_t capio_read(int fd, void *buffer, off64_t count, long tid) { START_LOG(tid, "call(fd=%d, buf=0x%08x, count=%ld)", fd, buffer, count); - - if (exists_capio_fd(fd)) { - if (count >= SSIZE_MAX) { - ERR_EXIT("CAPIO does not support read bigger than SSIZE_MAX yet"); - } - - read_request(get_capio_fd_path(fd), count, tid); - } - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } inline ssize_t capio_readv(int fd, const struct iovec *iov, int iovcnt, long tid) { @@ -51,7 +41,10 @@ int read_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg auto count = static_cast(arg2); long tid = syscall_no_intercept(SYS_gettid); - return posix_return_value(capio_read(fd, buffer, count, tid), result); + if (exists_capio_fd(fd)) { + read_request(get_capio_fd_path(fd), get_capio_fd_offset(fd) + count, tid, fd); + } + return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } int readv_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { @@ -60,7 +53,12 @@ int readv_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long ar auto iovcnt = static_cast(arg2); long tid = syscall_no_intercept(SYS_gettid); - return posix_return_value(capio_readv(fd, iov, iovcnt, tid), result); + // return posix_return_value(capio_readv(fd, iov, iovcnt, tid), result); + if (exists_capio_fd(fd)) { + read_request(get_capio_fd_path(fd), get_capio_fd_offset(fd) + iovcnt * sizeof(iovec), tid, + fd); + } + return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } #endif // SYS_read || SYS_readv diff --git a/src/posix/handlers/rename.hpp b/src/posix/handlers/rename.hpp index 309be86ec..42dc1f092 100644 --- a/src/posix/handlers/rename.hpp +++ b/src/posix/handlers/rename.hpp @@ -12,23 +12,28 @@ int rename_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long a START_LOG(tid, "call(oldpath=%s, newpath=%s)", oldpath.c_str(), newpath.c_str()); auto oldpath_abs = capio_absolute(oldpath); + LOG("oldpath absolute: %s", oldpath_abs.c_str()); auto newpath_abs = capio_absolute(newpath); + LOG("newpath absolute: %s", newpath_abs.c_str()); - /* if (is_prefix(oldpath_abs, newpath_abs)) { // TODO: The check is more complex - errno = EINVAL; - *result = -errno; - return CAPIO_POSIX_SYSCALL_SUCCESS; - }*/ + if (is_prefix(oldpath_abs, newpath_abs)) { // TODO: The check is more complex + errno = EINVAL; + *result = -errno; + return CAPIO_POSIX_SYSCALL_SUCCESS; + } + LOG("newpath is not prefix of old"); if (is_capio_path(oldpath_abs)) { + LOG("Oldpath is capio_path"); rename_capio_path(oldpath_abs, newpath_abs); - rename_request(tid, oldpath_abs, newpath_abs); + } - } else if (is_capio_path(newpath_abs)) { - rename_request(tid, oldpath_abs, newpath_abs); + if (is_capio_path(oldpath_abs) || is_capio_path(newpath_abs)) { + LOG("Either old or new or both paths are capio_paths. sending request"); + rename_request(oldpath_abs, newpath_abs, tid); } - return CAPIO_POSIX_SYSCALL_SUCCESS; + return CAPIO_POSIX_SYSCALL_SKIP; } #endif // SYS_rename diff --git a/src/posix/handlers/unlink.hpp b/src/posix/handlers/unlink.hpp index d64aa2f90..4ad36234e 100644 --- a/src/posix/handlers/unlink.hpp +++ b/src/posix/handlers/unlink.hpp @@ -9,7 +9,6 @@ off64_t capio_unlink_abs(const std::filesystem::path &abs_path, long tid, bool i START_LOG(tid, "call(abs_path=%s, is_dir=%s)", abs_path.c_str(), is_dir ? "true" : "false"); if (is_capio_path(abs_path)) { - is_dir ? rmdir_request(abs_path, tid) : unlink_request(abs_path, tid); LOG("Removing %s from capio_files_path", abs_path.c_str()); delete_capio_path(abs_path); } @@ -53,10 +52,17 @@ inline off64_t capio_unlinkat(int dirfd, const std::string_view &pathname, int f } int unlink_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - const std::string_view pathname(reinterpret_cast(arg0)); + std::string_view pathname(reinterpret_cast(arg0)); long tid = syscall_no_intercept(SYS_gettid); - return posix_return_value(capio_unlinkat(AT_FDCWD, pathname, 0, tid), result); + START_LOG(tid, "call(path=%s)", pathname.data()); + + if (is_capio_path(pathname)) { + LOG("Deleting path"); + delete_capio_path(pathname.data()); + } + + return CAPIO_POSIX_SYSCALL_SKIP; } int unlinkat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, @@ -66,7 +72,14 @@ int unlinkat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long int flags = static_cast(arg2); long tid = syscall_no_intercept(SYS_gettid); - return posix_return_value(capio_unlinkat(dirfd, pathname, flags, tid), result); + START_LOG(tid, "call(path=%s)", pathname.data()); + auto path = capio_posix_realpath(pathname); + if (is_capio_path(path)) { + LOG("Deleting path"); + delete_capio_path(path); + } + + return CAPIO_POSIX_SYSCALL_SKIP; } #endif // SYS_unlink || SYS_unlinkat diff --git a/src/posix/handlers/write.hpp b/src/posix/handlers/write.hpp index b817d6313..734361b27 100644 --- a/src/posix/handlers/write.hpp +++ b/src/posix/handlers/write.hpp @@ -10,7 +10,7 @@ inline off64_t capio_write(int fd, const void *buffer, off64_t count, long tid) START_LOG(tid, "call(fd=%d, buf=0x%08x, count=%ld)", fd, buffer, count); if (exists_capio_fd(fd)) { - write_request(get_capio_fd_path(fd), count, tid); + write_request(get_capio_fd_path(fd), count, tid, fd); } return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } diff --git a/src/posix/utils/clone.hpp b/src/posix/utils/clone.hpp index 8185eb876..c2ded01e4 100644 --- a/src/posix/utils/clone.hpp +++ b/src/posix/utils/clone.hpp @@ -78,7 +78,6 @@ void hook_clone_parent(long child_tid) { LOG("Initializing child thread %d", child_tid); init_process(child_tid); - clone_request(parent_tid, child_tid); LOG("Child thread %d initialized", child_tid); register_capio_tid(child_tid); diff --git a/src/posix/utils/filesystem.hpp b/src/posix/utils/filesystem.hpp index 224deeedb..1820135fe 100644 --- a/src/posix/utils/filesystem.hpp +++ b/src/posix/utils/filesystem.hpp @@ -55,10 +55,15 @@ inline void add_capio_path(const std::string &path) { */ inline void add_capio_fd(long tid, const std::string &path, int fd, off64_t offset, off64_t init_size, int flags, bool is_cloexec) { + START_LOG(tid, "call(path=%s, fd=%d)", path.c_str(), fd); add_capio_path(path); + LOG("Added capio path %s", path.c_str()); capio_files_paths->at(path).insert(fd); + LOG("Inserted tid %d for path %s", tid, path.c_str()); capio_files_descriptors->insert({fd, path}); + LOG("Inserted file descriptor tuple"); files->insert({fd, {std::make_shared(offset), init_size, flags, is_cloexec}}); + LOG("Registered file"); } /** @@ -110,6 +115,7 @@ inline std::filesystem::path capio_absolute(const std::filesystem::path &path) { * @return */ inline void delete_capio_fd(int fd) { + START_LOG(syscall_no_intercept(SYS_gettid), "call(fd=%d)", fd); auto &path = capio_files_descriptors->at(fd); capio_files_paths->at(path).erase(fd); capio_files_descriptors->erase(fd); @@ -122,11 +128,17 @@ inline void delete_capio_fd(int fd) { * @return */ inline void delete_capio_path(const std::string &path) { - auto it = capio_files_paths->at(path).begin(); - while (it != capio_files_paths->at(path).end()) { - delete_capio_fd(*it++); + START_LOG(syscall_no_intercept(SYS_gettid), "call(path=%s)", path.c_str()); + if (capio_files_paths->find(path) != capio_files_paths->end()) { + auto it = capio_files_paths->at(path).begin(); + LOG("Proceeding to remove fds"); + while (it != capio_files_paths->at(path).end()) { + delete_capio_fd(*it++); + } + LOG("Proceeding to remove path from capio_files_paths"); + capio_files_paths->erase(path); + LOG("Cleanup completed"); } - capio_files_paths->erase(path); } /** @@ -265,11 +277,17 @@ inline void init_filesystem() { * @return */ inline void rename_capio_path(const std::string &oldpath, const std::string &newpath) { - auto entry = capio_files_paths->extract(oldpath); - entry.key() = newpath; - capio_files_paths->insert(std::move(entry)); - for (auto fd : capio_files_paths->at(newpath)) { - capio_files_descriptors->at(fd).assign(newpath); + START_LOG(syscall_no_intercept(SYS_gettid), "call(oldpath=%s, newpath=%s)", oldpath.c_str(), + newpath.c_str()); + if (capio_files_paths->find(oldpath) != capio_files_paths->find(newpath)) { + auto entry = capio_files_paths->extract(oldpath); + entry.key() = newpath; + capio_files_paths->insert(std::move(entry)); + for (auto fd : capio_files_paths->at(newpath)) { + capio_files_descriptors->at(fd).assign(newpath); + } + }else { + LOG("Warning: olpath not found in capio_files_paths"); } } diff --git a/src/posix/utils/requests.hpp b/src/posix/utils/requests.hpp index 9e779908a..e1cac1c92 100644 --- a/src/posix/utils/requests.hpp +++ b/src/posix/utils/requests.hpp @@ -42,24 +42,17 @@ inline void consent_to_proceed_request(const std::filesystem::path &path, const bufs_response->at(tid)->read(&res); } -inline void seek_request(const std::filesystem::path &path, const long offset, const int whence, - const long tid) { +inline off64_t seek_request(const std::filesystem::path &path, const long offset, const int whence, + const long tid, int fd) { START_LOG(capio_syscall(SYS_gettid), "call(path=%s, offset=%ld, tid=%ld)", path.c_str(), offset, tid); char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %s %ld %d", CAPIO_REQUEST_SEEK, tid, path.c_str(), offset, whence); + sprintf(req, "%04d %ld %s %ld %d %d", CAPIO_REQUEST_SEEK, tid, path.c_str(), offset, fd, + whence); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); off64_t res; bufs_response->at(tid)->read(&res); -} - -// non blocking -inline void clone_request(const long parent_tid, const long child_tid) { - START_LOG(capio_syscall(SYS_gettid), "call(parent_tid=%ld, child_tid=%ld)", parent_tid, - child_tid); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %ld", CAPIO_REQUEST_CLONE, parent_tid, child_tid); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); + return res; } // non blocking @@ -70,18 +63,6 @@ inline void close_request(const std::filesystem::path &path, const long tid) { buf_requests->write(req, CAPIO_REQ_MAX_SIZE); } -// block until server registers rename -inline void rename_request(const long tid, const std::filesystem::path &old_path, - const std::filesystem::path &newpath) { - START_LOG(capio_syscall(SYS_gettid), "call(tid=%ld, old_path=%s, new_path=%s)", tid, - old_path.c_str(), newpath.c_str()); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %s %s %ld", CAPIO_REQUEST_RENAME, old_path.c_str(), newpath.c_str(), tid); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res; - bufs_response->at(tid)->read(&res); -} - // non blocking inline void create_request(const int fd, const std::filesystem::path &path, const long tid) { START_LOG(capio_syscall(SYS_gettid), "call(fd=%ld, path=%s, tid=%ld)", fd, path.c_str(), tid); @@ -115,14 +96,6 @@ inline void handshake_named_request(const long tid, const long pid, const std::s buf_requests->write(req, CAPIO_REQ_MAX_SIZE); } -// non blocking -inline void mkdir_request(const std::filesystem::path &path, const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(path=%s, tid=%ld)", path.c_str(), tid); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %s", CAPIO_REQUEST_MKDIR, tid, path.c_str()); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); -} - // block until open is possible inline void open_request(const int fd, const std::filesystem::path &path, const long tid) { START_LOG(capio_syscall(SYS_gettid), "call(fd=%ld, path=%s, tid=%ld)", fd, path.c_str(), tid); @@ -134,12 +107,12 @@ inline void open_request(const int fd, const std::filesystem::path &path, const } // return amount of readable bytes -inline off64_t read_request(const std::filesystem::path &path, const off64_t count, - const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(path=%s, count=%ld, tid=%ld)", path.c_str(), count, - tid); +inline off64_t read_request(const std::filesystem::path &path, const off64_t end_of_Read, + const long tid, const long fd) { + START_LOG(capio_syscall(SYS_gettid), "call(path=%s, end_of_Read=%ld, tid=%ld, fd=%ld)", + path.c_str(), end_of_Read, tid, fd); char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %s %ld %ld", CAPIO_REQUEST_READ, path.c_str(), tid, count); + sprintf(req, "%04d %s %ld %ld %ld", CAPIO_REQUEST_READ, path.c_str(), tid, fd, end_of_Read); LOG("Sending read request %s", req); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); off64_t res; @@ -149,27 +122,22 @@ inline off64_t read_request(const std::filesystem::path &path, const off64_t cou } // non blocking -inline void unlink_request(const std::filesystem::path &path, const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(path=%s, tid=%ld)", path.c_str(), tid); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %s", CAPIO_REQUEST_UNLINK, tid, path.c_str()); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); -} - -// non blocking -inline void rmdir_request(const std::filesystem::path &dir_path, long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(dir_path=%s, tid=%ld)", dir_path.c_str(), tid); +inline void rename_request(const std::filesystem::path &old_path, + const std::filesystem::path &new_path, const long tid) { + START_LOG(capio_syscall(SYS_gettid), "call(old=%s, new=%s, tid=%ld)", old_path.c_str(), + new_path.c_str(), tid); char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %s %ld", CAPIO_REQUEST_RMDIR, dir_path.c_str(), tid); + sprintf(req, "%04d %ld %s %s", CAPIO_REQUEST_RENAME, tid, old_path.c_str(), new_path.c_str()); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); } // non blocking as write is not in the pre port of capio semantics -inline void write_request(const std::filesystem::path &path, const off64_t count, const long tid) { +inline void write_request(const std::filesystem::path &path, const off64_t count, const long tid, + const long fd) { START_LOG(capio_syscall(SYS_gettid), "call(path=%s, count=%ld, tid=%ld)", path.c_str(), count, tid); char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %s %ld", CAPIO_REQUEST_WRITE, tid, path.c_str(), count); + sprintf(req, "%04d %ld %ld %s %ld", CAPIO_REQUEST_WRITE, tid, fd, path.c_str(), count); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); } diff --git a/src/server/capio_server.cpp b/src/server/capio_server.cpp index 2ca94b736..675b340df 100644 --- a/src/server/capio_server.cpp +++ b/src/server/capio_server.cpp @@ -36,7 +36,6 @@ CSDataBufferMap_t data_buffers; #include "utils/signals.hpp" #include "cl-engine/cl_engine.hpp" -#include "storage-engine/storage_engine.hpp" std::string parseCLI(int argc, char **argv) { Logger *log; @@ -183,8 +182,7 @@ int main(int argc, char **argv) { setup_signal_handlers(); - storage_engine = new StorageEngine(); - cl_engine = new ClEngine(config_path); + cl_engine = new ClEngine(config_path); cl_engine->start(); return 0; diff --git a/src/server/cl-engine/cl_engine.hpp b/src/server/cl-engine/cl_engine.hpp index 5ff48bba7..389109e86 100644 --- a/src/server/cl-engine/cl_engine.hpp +++ b/src/server/cl-engine/cl_engine.hpp @@ -20,20 +20,17 @@ class ClEngine { std::array _request_handlers{0}; _request_handlers[CAPIO_REQUEST_CONSENT] = consent_to_proceed_handler; - _request_handlers[CAPIO_REQUEST_CLONE] = clone_handler; _request_handlers[CAPIO_REQUEST_CLOSE] = close_handler; _request_handlers[CAPIO_REQUEST_CREATE] = create_handler; _request_handlers[CAPIO_REQUEST_EXIT_GROUP] = exit_handler; _request_handlers[CAPIO_REQUEST_HANDSHAKE_NAMED] = handshake_named_handler; _request_handlers[CAPIO_REQUEST_HANDSHAKE_ANONYMOUS] = handshake_anonymous_handler; - _request_handlers[CAPIO_REQUEST_MKDIR] = nullptr; - _request_handlers[CAPIO_REQUEST_OPEN] = nullptr; - _request_handlers[CAPIO_REQUEST_READ] = nullptr; - _request_handlers[CAPIO_REQUEST_RENAME] = nullptr; - _request_handlers[CAPIO_REQUEST_SEEK] = nullptr; - _request_handlers[CAPIO_REQUEST_UNLINK] = nullptr; - _request_handlers[CAPIO_REQUEST_WRITE] = nullptr; - _request_handlers[CAPIO_REQUEST_RMDIR] = nullptr; + _request_handlers[CAPIO_REQUEST_MKDIR] = create_handler; + _request_handlers[CAPIO_REQUEST_OPEN] = open_handler; + _request_handlers[CAPIO_REQUEST_READ] = read_handler; + _request_handlers[CAPIO_REQUEST_RENAME] = rename_handler; + _request_handlers[CAPIO_REQUEST_SEEK] = seek_handler; + _request_handlers[CAPIO_REQUEST_WRITE] = write_handler; return _request_handlers; } diff --git a/src/server/cl-engine/src/capio_cl_configuration.hpp b/src/server/cl-engine/src/capio_cl_configuration.hpp index e201b0fb9..fa7f95f6c 100644 --- a/src/server/cl-engine/src/capio_cl_configuration.hpp +++ b/src/server/cl-engine/src/capio_cl_configuration.hpp @@ -103,6 +103,24 @@ class CapioCLConfiguration { std::cout << std::endl; }; + // TODO: might need to be improved + bool file_to_be_handled(std::filesystem::path::iterator::reference path) { + for (const auto &entry : _locations) { + auto capio_path = entry.first; + if (path == capio_path) { + return true; + } + + if (capio_path.find('*') != std::string::npos) { // check for globs + if (capio_path.find(path) == 0) { // if path and capio_path begins in the same way + return true; + } + } + } + + return false; + }; + inline void add(std::string &path, std::vector &producers, std::vector &consumers, const std::string &commit_rule, const std::string &fire_rule, bool permanent, bool exclude) { diff --git a/src/server/cl-engine/src/client_manager.hpp b/src/server/cl-engine/src/client_manager.hpp index 046709fc5..78ef8bb7e 100644 --- a/src/server/cl-engine/src/client_manager.hpp +++ b/src/server/cl-engine/src/client_manager.hpp @@ -6,14 +6,23 @@ class ClientManager { CSBufResponse_t *bufs_response; std::unordered_map *app_names; + std::unordered_map *> *thread_awaiting_file_creation; + std::unordered_map *> *thread_awaiting_data; + public: ClientManager() { - bufs_response = new CSBufResponse_t(); - app_names = new std::unordered_map; + bufs_response = new CSBufResponse_t(); + app_names = new std::unordered_map; + thread_awaiting_file_creation = new std::unordered_map *>; + thread_awaiting_data = + new std::unordered_map *>; } ~ClientManager() { delete bufs_response; + delete app_names; + delete thread_awaiting_file_creation; + delete thread_awaiting_data; std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_response cleanup completed" << std::endl; } @@ -30,8 +39,6 @@ class ClientManager { CAPIO_REQ_BUFF_CNT, sizeof(off_t), workflow_name); bufs_response->insert(std::make_pair(tid, p_buf_response)); app_names->emplace(tid, app_name); - std::cout << CAPIO_SERVER_CLI_LOG_SERVER << "Handshaked a new client: <" << tid << "," - << app_name << ">" << std::endl; } /** @@ -58,6 +65,50 @@ class ClientManager { return bufs_response->at(tid)->write(&offset); } + + void add_thread_awaiting_creation(std::string path, int tid) { + if (thread_awaiting_file_creation->find(path) == thread_awaiting_file_creation->end()) { + thread_awaiting_file_creation->emplace(path, new std::vector); + } + thread_awaiting_file_creation->at(path)->emplace_back(tid); + } + + void unlock_thread_awaiting_creation(std::string path) { + if (thread_awaiting_file_creation->find(path) != thread_awaiting_file_creation->end()) { + auto th = thread_awaiting_file_creation->at(path); + for (auto tid : *th) { + reply_to_client(tid, 1); + } + } + } + + // register tid to wait for file size of certain size + void add_thread_awaiting_data(std::string path, int tid, size_t expected_size) { + if (thread_awaiting_data->find(path) == thread_awaiting_data->end()) { + thread_awaiting_data->emplace(path, new std::unordered_map); + } + thread_awaiting_data->at(path)->emplace(tid, expected_size); + } + + void unlock_thread_awaiting_data(std::string path) { + auto path_size = std::filesystem::file_size(path); + + if (thread_awaiting_data->find(path) == thread_awaiting_data->end()) { + auto th = thread_awaiting_data->at(path); + std::vector item_to_delete; + for (auto item : *th) { + if (item.second >= std::filesystem::file_size(path)) { + reply_to_client(item.first, path_size); + item_to_delete.emplace_back(item.first); + } + } + + // cleanup of served clients + for (auto itm : item_to_delete) { + th->erase(itm); + } + } + } }; inline ClientManager *client_manager; diff --git a/src/server/cl-engine/src/handlers.hpp b/src/server/cl-engine/src/handlers.hpp index c28e2298c..1e3d32fa6 100644 --- a/src/server/cl-engine/src/handlers.hpp +++ b/src/server/cl-engine/src/handlers.hpp @@ -1,11 +1,15 @@ #ifndef HANDLERS_HPP #define HANDLERS_HPP -#include "handlers/clone.hpp" #include "handlers/close.hpp" #include "handlers/consent.hpp" #include "handlers/create.hpp" #include "handlers/exit.hpp" #include "handlers/handshake.hpp" +#include "handlers/open.hpp" +#include "handlers/read.hpp" +#include "handlers/rename.hpp" +#include "handlers/seek.hpp" +#include "handlers/write.hpp" #endif // HANDLERS_HPP diff --git a/src/server/cl-engine/src/handlers/clone.hpp b/src/server/cl-engine/src/handlers/clone.hpp deleted file mode 100644 index c6052e5a5..000000000 --- a/src/server/cl-engine/src/handlers/clone.hpp +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef CAPIO_CLONE_HPP -#define CAPIO_CLONE_HPP -void clone_handler(const char *const str) { START_LOG(gettid(), "call(str=%s)", str); } -#endif // CAPIO_CLONE_HPP diff --git a/src/server/cl-engine/src/handlers/close.hpp b/src/server/cl-engine/src/handlers/close.hpp index 8ec4309b2..38210e6f3 100644 --- a/src/server/cl-engine/src/handlers/close.hpp +++ b/src/server/cl-engine/src/handlers/close.hpp @@ -1,15 +1,19 @@ #ifndef CAPIO_CLOSE_HPP #define CAPIO_CLOSE_HPP #include -#include inline void close_handler(const char *const str) { int tid; char path[PATH_MAX]; sscanf(str, "%d %s", &tid, path); + std::filesystem::path filename(path); - auto n_close = std::get<1>(storage_engine->get_metadata(filename)) + 1; - storage_engine->update_n_close(filename, n_close); + + if (path == get_capio_dir() || !capio_configuration->file_to_be_handled(filename)) { + return; + } + + // TODO: gestire le CoC } #endif // CAPIO_CLOSE_HPP diff --git a/src/server/cl-engine/src/handlers/consent.hpp b/src/server/cl-engine/src/handlers/consent.hpp index 44cecf697..3cc8eb164 100644 --- a/src/server/cl-engine/src/handlers/consent.hpp +++ b/src/server/cl-engine/src/handlers/consent.hpp @@ -1,7 +1,7 @@ #ifndef CONSENT_HPP #define CONSENT_HPP + #include -#include /* This handler only checks if the client is allowed to continue @@ -11,20 +11,28 @@ inline void consent_to_proceed_handler(const char *const str) { int tid; char path[1024]; sscanf(str, "%d %s", &tid, path); + START_LOG(gettid(), "call(tid=%d, path=%s)", tid, path); std::filesystem::path path_fs(path); - //Skip operations on CAPIO_DIR - if(path_fs == get_capio_dir()) { + // Skip operations on CAPIO_DIR + // TODO: check if it is coherent with CAPIO_CL + if (path_fs == get_capio_dir()) { + LOG("Ignore calls on exactly CAPIO_DIR"); + client_manager->reply_to_client(tid, 1); + return; + } + + if (!capio_configuration->file_to_be_handled(path_fs)) { + LOG("Ignore calls as fiel should not be treated by CAPIO"); client_manager->reply_to_client(tid, 1); return; } - if (storage_engine->is_committed(path_fs, tid) || - capio_configuration->getFireRule(path_fs) == CAPIO_FILE_MODE_NO_UPDATE) { + if (std::filesystem::exists(path)) { client_manager->reply_to_client(tid, 1); } else { - storage_engine->add_thread_awaiting_for_commit(path, tid); + client_manager->add_thread_awaiting_creation(path, tid); } } diff --git a/src/server/cl-engine/src/handlers/create.hpp b/src/server/cl-engine/src/handlers/create.hpp index 120546735..b6e998c4f 100644 --- a/src/server/cl-engine/src/handlers/create.hpp +++ b/src/server/cl-engine/src/handlers/create.hpp @@ -2,14 +2,13 @@ #define CAPIO_CREATE_HPP #include -#include inline void create_handler(const char *const str) { int tid; char path[PATH_MAX]; sscanf(str, "%d %s", &tid, path); - std::filesystem::path filename(path); - storage_engine->create_capio_file(path, tid); + + client_manager->unlock_thread_awaiting_creation(path); } #endif // CAPIO_CREATE_HPP diff --git a/src/server/cl-engine/src/handlers/exit.hpp b/src/server/cl-engine/src/handlers/exit.hpp index d4d61831e..89d0e5540 100644 --- a/src/server/cl-engine/src/handlers/exit.hpp +++ b/src/server/cl-engine/src/handlers/exit.hpp @@ -1,15 +1,14 @@ #ifndef CAPIO_EXIT_HPP #define CAPIO_EXIT_HPP #include -#include inline void exit_handler(const char *const str) { - //TODO: register files open for each tid ti register a close + // TODO: register files open for each tid ti register a close int tid; char path[PATH_MAX]; sscanf(str, "%d", &tid); - storage_engine->close_all_files(tid); + // TODO: handle commits on termination } #endif // CAPIO_EXIT_HPP diff --git a/src/server/cl-engine/src/handlers/open.hpp b/src/server/cl-engine/src/handlers/open.hpp new file mode 100644 index 000000000..3ece4ca48 --- /dev/null +++ b/src/server/cl-engine/src/handlers/open.hpp @@ -0,0 +1,15 @@ +#ifndef OPEN_HPP +#define OPEN_HPP +inline void open_handler(const char *const str) { + int tid, fd; + char path[PATH_MAX]; + sscanf(str, "%ld %d %s", &tid, &fd, path); + START_LOG(gettid(), "call(tid=%d, fd=%d, path=%s", tid, fd, path); + + if (std::filesystem::exists(path)) { + client_manager->reply_to_client(tid, 1); + } else { + client_manager->add_thread_awaiting_creation(path, tid); + } +} +#endif // OPEN_HPP diff --git a/src/server/cl-engine/src/handlers/read.hpp b/src/server/cl-engine/src/handlers/read.hpp new file mode 100644 index 000000000..0020cc20f --- /dev/null +++ b/src/server/cl-engine/src/handlers/read.hpp @@ -0,0 +1,34 @@ +#ifndef READ_HPP +#define READ_HPP + +inline void read_handler(const char *const str) { + + long tid, end_of_read, fd; + char path[PATH_MAX]; + + sscanf(str, "%s %ld %ld %ld", path, &tid, &fd, &end_of_read); + START_LOG(gettid(), "call(path=%s, tid=%ld, count=%ld)", path, tid, end_of_read); + + std::filesystem::path path_fs(path); + // Skip operations on CAPIO_DIR + // TODO: check if it is coherent with CAPIO_CL + if (path_fs == get_capio_dir()) { + LOG("Ignore calls on exactly CAPIO_DIR"); + client_manager->reply_to_client(tid, 1); + return; + } + + if (!capio_configuration->file_to_be_handled(path_fs)) { + LOG("Ignore calls as file should not be treated by CAPIO"); + client_manager->reply_to_client(tid, 1); + return; + } + + if (std::filesystem::file_size(path) >= end_of_read) { + client_manager->reply_to_client(tid, 1); + } else { + client_manager->add_thread_awaiting_data(path, tid, end_of_read); + } +} + +#endif // READ_HPP diff --git a/src/server/cl-engine/src/handlers/rename.hpp b/src/server/cl-engine/src/handlers/rename.hpp new file mode 100644 index 000000000..5a47da0fe --- /dev/null +++ b/src/server/cl-engine/src/handlers/rename.hpp @@ -0,0 +1,14 @@ +#ifndef CAPIO_RENAME_HPP +#define CAPIO_RENAME_HPP +#include + +inline void rename_handler(const char *const str) { + int tid; + char old_path[PATH_MAX], new_path[PATH_MAX]; + sscanf(str, "%d %s %s", &tid, old_path, new_path); + + client_manager->unlock_thread_awaiting_creation(new_path); + // TODO: gestire le rename? +} + +#endif // CAPIO_RENAME_HPP diff --git a/src/server/cl-engine/src/handlers/seek.hpp b/src/server/cl-engine/src/handlers/seek.hpp new file mode 100644 index 000000000..4d837f081 --- /dev/null +++ b/src/server/cl-engine/src/handlers/seek.hpp @@ -0,0 +1,23 @@ +#ifndef CAPIO_SEEK_HPP +#define CAPIO_SEEK_HPP + +inline void seek_handler(const char *const str) { + long tid, target_offset; + int whence, fd; + char path[PATH_MAX]; + sscanf(str, "%ld %s %ld %d %d", &tid, path, &target_offset, &fd, &whence); + START_LOG(gettid(), "call()"); + std::filesystem::path filename(path); + + // TODO: MIGHT NOT BE NEEDED + if (path == get_capio_dir() || !capio_configuration->file_to_be_handled(filename)) { + return; + } + auto path_size = std::filesystem::file_size(path); + if (path_size >= target_offset) { + client_manager->reply_to_client(tid, path_size); + } else { + } +} + +#endif // CAPIO_SEEK_HPP diff --git a/src/server/cl-engine/src/handlers/write.hpp b/src/server/cl-engine/src/handlers/write.hpp new file mode 100644 index 000000000..0f162481d --- /dev/null +++ b/src/server/cl-engine/src/handlers/write.hpp @@ -0,0 +1,18 @@ +#ifndef WRITE_HPP +#define WRITE_HPP + +inline void write_handler(const char *const str) { + int tid, count, fd; + char path[PATH_MAX]; + sscanf(str, "%ld %ld %s %ld", &tid, &fd, path, &count); + + std::filesystem::path filename(path); + + if (path == get_capio_dir() || !capio_configuration->file_to_be_handled(filename)) { + return; + } + + client_manager->unlock_thread_awaiting_data(path); +} + +#endif // WRITE_HPP diff --git a/src/server/storage-engine/src/capio_file.hpp b/src/server/storage-engine/src/capio_file.hpp deleted file mode 100644 index 611897685..000000000 --- a/src/server/storage-engine/src/capio_file.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef CAPIO_FILE_HPP -#define CAPIO_FILE_HPP - -#include "utils/distributed_semaphore.hpp" - -// TODO: concurrency is kinda guaranteed but more checks needs to be performed. -// TODO: as writes are below PIPE_MAX the distributed semaphore might not be needed - -class CapioFile { - private: - std::string name; - std::string metadataname; - FILE *metadata_file_fd = nullptr; - long _size = 0, _n_close = 0; - int _committed = 0; - - public: - explicit CapioFile(std::filesystem::path filename) : name(filename) { - metadataname = name + ".capio"; - metadata_file_fd = - fopen(metadataname.c_str(), - "w+"); // open (or create if not exists) metadata associated with file - } - - ~CapioFile() { fclose(metadata_file_fd); } - - /** - * Query metadata related to the descirbed file - * @return a tuple of - */ - std::tuple get_metadata() { - - fseek(metadata_file_fd, 0, SEEK_SET); - fscanf(metadata_file_fd, "%ld %ld %d", &_size, &_n_close, &_committed); - - return {_size, _n_close, _committed}; - } - - void update_metadata(long filesize, long n_close, bool committed) { - DistributedSemaphore lock(metadataname, 100); - get_metadata(); - - if (filesize != _size) { - _size = filesize; - } - if (n_close != _n_close) { - _n_close = n_close; - } - if (committed != _committed) { - _committed = committed; - } - - fseek(metadata_file_fd, 0, SEEK_SET); - fprintf(metadata_file_fd, "%ld %ld %d", filesize, n_close, committed ? 1 : 0); - } - - void update_size(long size) { update_metadata(size, _n_close, _committed); } - - void update_n_close(long n_close) { update_metadata(_size, n_close, _committed); } - - void set_committed() { update_metadata(_size, _n_close, _committed); } - - bool is_committed() { - get_metadata(); - return _committed; - } -}; - -#endif // CAPIO_FILE_HPP diff --git a/src/server/storage-engine/storage_engine.hpp b/src/server/storage-engine/storage_engine.hpp deleted file mode 100644 index 45a4b6c13..000000000 --- a/src/server/storage-engine/storage_engine.hpp +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef STORAGE_ENGINE_HPP -#define STORAGE_ENGINE_HPP -#include - -#include "src/capio_file.hpp" - -class StorageEngine { - private: - // path -> CapioFile (only for metadata) - std::unordered_map *open_metadata_descriptors; - - // path -> [tids waiting for response on path] - std::unordered_map *> *pending_requests_on_file; - - // tid, path -> number of open on file (used to know opened and how many times a file has been - // open by a thread) - std::unordered_map *> *threads_opened_files; - - std::mutex metadata_mutex; - - /** - * check if metadata associated with a given file exists. - * If it exists, then there is an assumption that the associated file is present - * @param path - * @return - */ - static bool exists(std::filesystem::path &path) { - std::filesystem::path check = path.string() + ".capio"; - return std::filesystem::exists(check); - } - - void unlock_awaiting_threads_for_commit(const std::filesystem::path &path) const { - if (open_metadata_descriptors->at(path)->is_committed()) { - auto queue = pending_requests_on_file->at(path); - for (long tid : *queue) { - client_manager->reply_to_client(tid, 1); - } - } - } - - public: - StorageEngine() { - open_metadata_descriptors = new std::unordered_map; - pending_requests_on_file = new std::unordered_map *>; - threads_opened_files = - new std::unordered_map *>; - } - - ~StorageEngine() { delete open_metadata_descriptors; } - - void create_capio_file(std::filesystem::path filename, long tid) { - std::lock_guard lg(metadata_mutex); - if (open_metadata_descriptors->find(filename) == open_metadata_descriptors->end()) { - open_metadata_descriptors->emplace(filename, new CapioFile(filename)); - } - - // register a new open on a file - if (threads_opened_files->find(tid) == threads_opened_files->end()) { - threads_opened_files->emplace(tid, new std::unordered_map); - } - - if (threads_opened_files->at(tid)->find(filename) == threads_opened_files->at(tid)->end()) { - threads_opened_files->at(tid)->emplace(filename, 1); - } else { - threads_opened_files->at(tid)->at(filename) = - threads_opened_files->at(tid)->at(filename) + 1; - } - } - - void deleteFile(std::filesystem::path path) { - std::lock_guard lg(metadata_mutex); - - std::filesystem::path metadata_name = path.string() + ".capio"; - std::filesystem::remove(metadata_name); - std::filesystem::remove(path); - open_metadata_descriptors->erase(open_metadata_descriptors->find(path)); - } - - auto get_metadata(std::filesystem::path &filename) { - std::lock_guard lg(metadata_mutex); - auto metadata = open_metadata_descriptors->at(filename)->get_metadata(); - unlock_awaiting_threads_for_commit( - filename); // if committed unlock threads waiting to continue - return metadata; - } - - void update_metadata(std::filesystem::path &filename, long filesize, long n_close, - bool committed) { - std::lock_guard lg(metadata_mutex); - open_metadata_descriptors->at(filename)->update_metadata(filesize, n_close, committed); - if (committed) { - unlock_awaiting_threads_for_commit(filename); - } - } - - void update_size(std::filesystem::path &filename, long size) { - std::lock_guard lg(metadata_mutex); - - open_metadata_descriptors->at(filename)->update_size(size); - } - - void update_n_close(std::filesystem::path &filename, long n_close) { - std::lock_guard lg(metadata_mutex); - - open_metadata_descriptors->at(filename)->update_n_close(n_close); - } - - void set_committed(std::filesystem::path &filename) const { - open_metadata_descriptors->at(filename)->set_committed(); - unlock_awaiting_threads_for_commit(filename); - } - - bool is_committed(std::filesystem::path &filename, long tid) { - this->create_capio_file(filename, tid); - return open_metadata_descriptors->at(filename)->is_committed(); - } - - void add_thread_awaiting_for_commit(const std::filesystem::path &filename, long tid) const { - if (pending_requests_on_file->find(filename) == pending_requests_on_file->end()) { - pending_requests_on_file->emplace(filename, new std::vector); - } - pending_requests_on_file->at(filename)->emplace_back(tid); - } - - void close_all_files(int tid) { - auto map = threads_opened_files->at(tid); - - for (auto entry : *map) { - std::filesystem::path filename(entry.first); - long opens = entry.second; - update_n_close(filename, opens); - // TODO: check if is committed - } - - threads_opened_files->erase(threads_opened_files->find(tid)); - } -}; - -StorageEngine *storage_engine; - -#endif // STORAGE_ENGINE_HPP diff --git a/src/server/utils/common.hpp b/src/server/utils/common.hpp index f62b04c04..988c986c0 100644 --- a/src/server/utils/common.hpp +++ b/src/server/utils/common.hpp @@ -6,9 +6,6 @@ #include "capio/constants.hpp" #include "types.hpp" - - - inline bool is_int(const std::string &s) { START_LOG(gettid(), "call(%s)", s.c_str()); bool res = false; diff --git a/src/server/utils/filesystem.hpp b/src/server/utils/filesystem.hpp deleted file mode 100644 index 82184fc79..000000000 --- a/src/server/utils/filesystem.hpp +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef CAPIO_SERVER_UTILS_FILESYSTEM_HPP -#define CAPIO_SERVER_UTILS_FILESYSTEM_HPP - -#include -#include - -#include - -#include "capio/dirent.hpp" - -#include "common.hpp" -#include "location.hpp" -#include "metadata.hpp" -#include "types.hpp" - -/* - * type == 0 -> regular entry - * type == 1 -> "." entry - * type == 2 -> ".." entry - */ - -void write_entry_dir(int tid, const std::filesystem::path &file_path, - const std::filesystem::path &dir, int type) { - START_LOG(gettid(), "call(file_path=%s, dir=%s, type=%d)", file_path.c_str(), dir.c_str(), - type); - - struct linux_dirent64 ld {}; - ld.d_ino = std::hash{}(file_path); - std::filesystem::path file_name; - if (type == 0) { - file_name = file_path.filename(); - LOG("FILENAME: %s", file_name.c_str()); - } else if (type == 1) { - file_name = "."; - } else { - file_name = ".."; - } - - strcpy(ld.d_name, file_name.c_str()); - LOG("FILENAME LD: %s", ld.d_name); - ld.d_reclen = sizeof(linux_dirent64); - - CapioFile &c_file = get_capio_file(dir); - c_file.create_buffer_if_needed(dir, true); - void *file_shm = c_file.get_buffer(); - off64_t file_size = c_file.get_stored_size(); - off64_t data_size = file_size + ld.d_reclen; - size_t file_shm_size = c_file.get_buf_size(); - ld.d_off = data_size; - - if (data_size > file_shm_size) { - file_shm = c_file.expand_buffer(data_size); - } - - ld.d_type = (c_file.is_dir() ? DT_DIR : DT_REG); - - memcpy((char *) file_shm + file_size, &ld, sizeof(ld)); - off64_t base_offset = file_size; - - LOG("STORED FILENAME LD: %s", - ((struct linux_dirent64 *) ((char *) file_shm + file_size))->d_name); - - c_file.insert_sector(base_offset, data_size); - ++c_file.n_files; - int pid = pids[tid]; - writers[pid][dir] = true; - - if (c_file.n_files == c_file.n_files_expected) { - c_file.set_complete(); - } -} - -void update_dir(int tid, const std::filesystem::path &file_path) { - START_LOG(gettid(), "call(file_path=%s)", file_path.c_str()); - const std::filesystem::path dir = get_parent_dir_path(file_path); - CapioFile &c_file = get_capio_file(dir.c_str()); - if (c_file.first_write) { - c_file.first_write = false; - write_file_location(dir); - } - write_entry_dir(tid, file_path, dir, 0); -} - -off64_t create_dir(int tid, const std::filesystem::path &path) { - START_LOG(tid, "call(path=%s)", path.c_str()); - - if (!get_file_location_opt(path)) { - CapioFile &c_file = create_capio_file(path, true, CAPIO_DEFAULT_DIR_INITIAL_SIZE); - if (c_file.first_write) { - c_file.first_write = false; - // TODO: it works only if there is one prod per file - if (is_capio_dir(path)) { - add_file_location(path, node_name, -1); - } else { - write_file_location(path); - update_dir(tid, path); - } - write_entry_dir(tid, path, path, 1); - const std::filesystem::path parent_dir = get_parent_dir_path(path); - write_entry_dir(tid, parent_dir, path, 2); - } - return 0; - } else { - return 1; - } -} - -#endif // CAPIO_SERVER_UTILS_FILESYSTEM_HPP diff --git a/src/server/utils/producer.hpp b/src/server/utils/producer.hpp deleted file mode 100644 index 5de77eaae..000000000 --- a/src/server/utils/producer.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef CAPIO_SERVER_UTILS_PRODUCER_HPP -#define CAPIO_SERVER_UTILS_PRODUCER_HPP - -#include - -#include "metadata.hpp" - -std::string get_producer_name(const std::filesystem::path &path) { - START_LOG(gettid(), "call( %s)", path.c_str()); - std::string producer_name; - // we handle also prefixes - auto it_metadata = metadata_conf.find(path); - if (it_metadata == metadata_conf.end()) { - long int pos = match_globs(path); - if (pos != -1) { - producer_name = std::get<3>(metadata_conf_globs[pos]); - } - } else { - producer_name = std::get<2>(it_metadata->second); - } - - return producer_name; -} - -bool is_producer(int tid, const std::filesystem::path &path) { - START_LOG(gettid(), "call(%d, %s)", tid, path.c_str()); - bool res = false; - - if (apps.find(tid) != apps.end()) { - std::string app_name = apps[tid]; - std::string prod_name = get_producer_name(path); - res = app_name == prod_name; - } - - return res; -} - -#endif // CAPIO_SERVER_UTILS_PRODUCER_HPP diff --git a/src/server/utils/signals.hpp b/src/server/utils/signals.hpp index 5592b19bd..1286adedf 100644 --- a/src/server/utils/signals.hpp +++ b/src/server/utils/signals.hpp @@ -3,7 +3,6 @@ #include #include -#include #ifdef CAPIO_COVERAGE extern "C" void __gcov_dump(void); @@ -20,10 +19,6 @@ void sig_term_handler(int signum, siginfo_t *info, void *ptr) { std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << "Segfault detected!" << std::endl; } - // TODO: free all the memory used - delete cl_engine; - delete storage_engine; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "shm cleanup completed" << std::endl; for (auto &p : data_buffers) { diff --git a/src/server/utils/types.hpp b/src/server/utils/types.hpp index 616d664cc..002f958e4 100644 --- a/src/server/utils/types.hpp +++ b/src/server/utils/types.hpp @@ -8,7 +8,6 @@ #include "capio/queue.hpp" - typedef std::unordered_map CSPidsMap_T; typedef std::unordered_map CSAppsMap_t; typedef std::unordered_map> CSFilesSentMap_t; From 034eed9d7eb8bb2563efe76e0ecef50e4e3851ab Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Tue, 30 Jul 2024 14:09:53 +0200 Subject: [PATCH 004/151] Support for commit rule --- .github/workflows/ci-tests.yaml | 60 +++++++++--------- .github/workflows/release.yml | 62 ------------------- src/common/capio/requests.hpp | 5 +- src/posix/handlers/read.hpp | 42 ++----------- src/posix/utils/filesystem.hpp | 2 +- src/posix/utils/requests.hpp | 13 ---- src/server/cl-engine/cl_engine.hpp | 5 +- src/server/cl-engine/src/client_manager.hpp | 29 ++++++++- src/server/cl-engine/src/file_manager.hpp | 29 +++++++++ .../cl-engine/src/file_manager_header.hpp | 11 ++++ src/server/cl-engine/src/handlers.hpp | 1 - src/server/cl-engine/src/handlers/close.hpp | 4 +- src/server/cl-engine/src/handlers/consent.hpp | 2 +- src/server/cl-engine/src/handlers/create.hpp | 2 +- src/server/cl-engine/src/handlers/exit.hpp | 6 +- src/server/cl-engine/src/handlers/read.hpp | 2 +- src/server/cl-engine/src/handlers/rename.hpp | 2 +- src/server/cl-engine/src/handlers/seek.hpp | 23 ------- src/server/cl-engine/src/handlers/write.hpp | 3 +- 19 files changed, 121 insertions(+), 182 deletions(-) delete mode 100644 .github/workflows/release.yml create mode 100644 src/server/cl-engine/src/file_manager.hpp create mode 100644 src/server/cl-engine/src/file_manager_header.hpp delete mode 100644 src/server/cl-engine/src/handlers/seek.hpp diff --git a/.github/workflows/ci-tests.yaml b/.github/workflows/ci-tests.yaml index d48fcd2c8..c351985ad 100644 --- a/.github/workflows/ci-tests.yaml +++ b/.github/workflows/ci-tests.yaml @@ -183,33 +183,33 @@ jobs: - name: "Show server logs on failure" if: ${{ always() && steps.run-tests.outcome == 'failure' && matrix.build_type == 'Debug' }} run: tail -v -n +1 capio_logs/server/$(hostname)/server_thread_*.log - - name: "Generate coverage report" - if: ${{ matrix.build_type == 'Debug' }} - run: | - pip install --upgrade gcovr - gcovr \ - --exclude-throw-branches \ - --xml coverage.xml \ - --gcov-executable "${{ startsWith(matrix.cxx, 'clang-') && format('llvm-cov-{0} gcov', env.CXX_VERSION) || format('gcov-{0}', env.CXX_VERSION) }}" \ - ../build - - name: "Upload coverage report" - if: ${{ matrix.build_type == 'Debug' }} - uses: actions/upload-artifact@v4 - with: - name: ${{ format('{0}-tests', matrix.cxx) }} - path: ./coverage.xml - retention-days: 1 - if-no-files-found: error - upload-to-codecov: - name: "Codecov report upload" - needs: [ "unit-tests" , "codespell-check" , "format-check" ] - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - name: "Download artifacts" - uses: actions/download-artifact@v4 - - name: "Upload coverage to Codecov" - uses: codecov/codecov-action@v4 - with: - fail_ci_if_error: true - token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file +# - name: "Generate coverage report" +# if: ${{ matrix.build_type == 'Debug' }} +# run: | +# pip install --upgrade gcovr +# gcovr \ +# --exclude-throw-branches \ +# --xml coverage.xml \ +# --gcov-executable "${{ startsWith(matrix.cxx, 'clang-') && format('llvm-cov-{0} gcov', env.CXX_VERSION) || format('gcov-{0}', env.CXX_VERSION) }}" \ +# ../build +# - name: "Upload coverage report" +# if: ${{ matrix.build_type == 'Debug' }} +# uses: actions/upload-artifact@v4 +# with: +# name: ${{ format('{0}-tests', matrix.cxx) }} +# path: ./coverage.xml +# retention-days: 1 +# if-no-files-found: error +# upload-to-codecov: +# name: "Codecov report upload" +# needs: [ "unit-tests" , "codespell-check" , "format-check" ] +# runs-on: ubuntu-22.04 +# steps: +# - uses: actions/checkout@v4 +# - name: "Download artifacts" +# uses: actions/download-artifact@v4 +# - name: "Upload coverage to Codecov" +# uses: codecov/codecov-action@v4 +# with: +# fail_ci_if_error: true +# token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index e730bed93..000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: "Release new version" -on: - workflow_run: - workflows: - - "CI Tests" - branches: - - master - types: - - completed -jobs: - docker: - name: "Build Docker container" - runs-on: ubuntu-22.04 - if: ${{ github.event.workflow_run.conclusion == 'success' }} - steps: - - uses: actions/checkout@v4 - - uses: docker/setup-qemu-action@v3 - - uses: docker/setup-buildx-action@v3 - - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: "Get CAPIO version" - run: echo "CAPIO_VERSION=$(cat CMakeLists.txt | grep " VERSION" | awk '{print $2}')" >> $GITHUB_ENV - - name: "Check if Docker image already exists" - run: echo "NEW_IMAGE=$(docker buildx imagetools inspect alphaunito/capio:${CAPIO_VERSION} > /dev/null 2>&1; echo $?)" >> $GITHUB_ENV - - name: "Build Docker image" - if: ${{ env.NEW_IMAGE == 1 }} - uses: docker/build-push-action@v5 - with: - build-args: | - CMAKE_BUILD_TYPE=Release - push: true - tags: | - alphaunito/capio:${{ env.CAPIO_VERSION }} - alphaunito/capio:latest - github: - name: "Create GitHub Release" - runs-on: ubuntu-22.04 - permissions: - contents: write - if: ${{ github.event.workflow_run.conclusion == 'success' }} - steps: - - uses: actions/checkout@v4 - - name: "Get CAPIO version" - run: echo "CAPIO_VERSION=$(cat CMakeLists.txt | grep " VERSION" | awk '{print $2}')" >> $GITHUB_ENV - - name: "Check tag existence" - uses: mukunku/tag-exists-action@v1.6.0 - id: check-tag - with: - tag: ${{ env.CAPIO_VERSION }} - - name: "Create Release" - id: create-release - uses: actions/create-release@v1 - if: ${{ steps.check-tag.outputs.exists == 'false' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ env.CAPIO_VERSION }} - release_name: ${{ env.CAPIO_VERSION }} - draft: false - prerelease: false \ No newline at end of file diff --git a/src/common/capio/requests.hpp b/src/common/capio/requests.hpp index 1fd8a8136..61076f88a 100644 --- a/src/common/capio/requests.hpp +++ b/src/common/capio/requests.hpp @@ -12,9 +12,8 @@ constexpr const int CAPIO_REQUEST_MKDIR = 7; constexpr const int CAPIO_REQUEST_OPEN = 8; constexpr const int CAPIO_REQUEST_READ = 9; constexpr const int CAPIO_REQUEST_RENAME = 10; -constexpr const int CAPIO_REQUEST_SEEK = 11; -constexpr const int CAPIO_REQUEST_WRITE = 12; +constexpr const int CAPIO_REQUEST_WRITE = 11; -constexpr const int CAPIO_NR_REQUESTS = 13; +constexpr const int CAPIO_NR_REQUESTS = 12; #endif // CAPIO_COMMON_REQUESTS_HPP diff --git a/src/posix/handlers/read.hpp b/src/posix/handlers/read.hpp index 9c71ce232..cee2e6814 100644 --- a/src/posix/handlers/read.hpp +++ b/src/posix/handlers/read.hpp @@ -3,38 +3,6 @@ #if defined(SYS_read) || defined(SYS_readv) -inline off64_t capio_read(int fd, void *buffer, off64_t count, long tid) { - START_LOG(tid, "call(fd=%d, buf=0x%08x, count=%ld)", fd, buffer, count); -} - -inline ssize_t capio_readv(int fd, const struct iovec *iov, int iovcnt, long tid) { - START_LOG(tid, "call(fd=%d, iov.iov_base=0x%08x, iov.iov_len=%ld, iovcnt=%d)", fd, - iov->iov_base, iov->iov_len, iovcnt); - - if (exists_capio_fd(fd)) { - LOG("fd %d exists and is a capio fd", fd); - ssize_t tot_bytes = 0; - ssize_t res = 0; - int i = 0; - LOG("iov setup completed. starting read request loop"); - while (i < iovcnt && res >= 0) { - size_t iov_len = iov[i].iov_len; - if (iov_len != 0) { - res = capio_read(fd, iov[i].iov_base, iov[i].iov_len, tid); - if (res == -1) { - return CAPIO_POSIX_SYSCALL_ERRNO; - } - tot_bytes += res; - } - ++i; - } - return tot_bytes; - } else { - LOG("fd %d is not a capio fd. returning -2", fd); - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; - } -} - int read_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { int fd = static_cast(arg0); void *buffer = reinterpret_cast(arg1); @@ -42,7 +10,9 @@ int read_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg long tid = syscall_no_intercept(SYS_gettid); if (exists_capio_fd(fd)) { - read_request(get_capio_fd_path(fd), get_capio_fd_offset(fd) + count, tid, fd); + auto computed_offset = get_capio_fd_offset(fd) + count; + read_request(get_capio_fd_path(fd), computed_offset , tid, fd); + set_capio_fd_offset(fd, computed_offset); } return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } @@ -53,10 +23,10 @@ int readv_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long ar auto iovcnt = static_cast(arg2); long tid = syscall_no_intercept(SYS_gettid); - // return posix_return_value(capio_readv(fd, iov, iovcnt, tid), result); if (exists_capio_fd(fd)) { - read_request(get_capio_fd_path(fd), get_capio_fd_offset(fd) + iovcnt * sizeof(iovec), tid, - fd); + auto computed_offset = get_capio_fd_offset(fd) + iovcnt * sizeof(iovec); + read_request(get_capio_fd_path(fd), computed_offset, tid, fd); + set_capio_fd_offset(fd, computed_offset); } return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } diff --git a/src/posix/utils/filesystem.hpp b/src/posix/utils/filesystem.hpp index 1820135fe..a613dbb18 100644 --- a/src/posix/utils/filesystem.hpp +++ b/src/posix/utils/filesystem.hpp @@ -286,7 +286,7 @@ inline void rename_capio_path(const std::string &oldpath, const std::string &new for (auto fd : capio_files_paths->at(newpath)) { capio_files_descriptors->at(fd).assign(newpath); } - }else { + } else { LOG("Warning: olpath not found in capio_files_paths"); } } diff --git a/src/posix/utils/requests.hpp b/src/posix/utils/requests.hpp index e1cac1c92..f1b1e068b 100644 --- a/src/posix/utils/requests.hpp +++ b/src/posix/utils/requests.hpp @@ -42,19 +42,6 @@ inline void consent_to_proceed_request(const std::filesystem::path &path, const bufs_response->at(tid)->read(&res); } -inline off64_t seek_request(const std::filesystem::path &path, const long offset, const int whence, - const long tid, int fd) { - START_LOG(capio_syscall(SYS_gettid), "call(path=%s, offset=%ld, tid=%ld)", path.c_str(), offset, - tid); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %s %ld %d %d", CAPIO_REQUEST_SEEK, tid, path.c_str(), offset, fd, - whence); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res; - bufs_response->at(tid)->read(&res); - return res; -} - // non blocking inline void close_request(const std::filesystem::path &path, const long tid) { START_LOG(capio_syscall(SYS_gettid), "call(path=%s, tid=%ld)", path.c_str(), tid); diff --git a/src/server/cl-engine/cl_engine.hpp b/src/server/cl-engine/cl_engine.hpp index 389109e86..25fc1aecc 100644 --- a/src/server/cl-engine/cl_engine.hpp +++ b/src/server/cl-engine/cl_engine.hpp @@ -1,8 +1,12 @@ #ifndef CAPIO_CL_ENGINE_MAIN_HPP #define CAPIO_CL_ENGINE_MAIN_HPP + +#include "src/file_manager_header.hpp" + #include "capio/requests.hpp" #include "src/capio_cl_configuration.hpp" #include "src/client_manager.hpp" +#include "src/file_manager.hpp" #include "src/json_parser.hpp" #include "src/handlers.hpp" @@ -29,7 +33,6 @@ class ClEngine { _request_handlers[CAPIO_REQUEST_OPEN] = open_handler; _request_handlers[CAPIO_REQUEST_READ] = read_handler; _request_handlers[CAPIO_REQUEST_RENAME] = rename_handler; - _request_handlers[CAPIO_REQUEST_SEEK] = seek_handler; _request_handlers[CAPIO_REQUEST_WRITE] = write_handler; return _request_handlers; diff --git a/src/server/cl-engine/src/client_manager.hpp b/src/server/cl-engine/src/client_manager.hpp index 78ef8bb7e..5237fd330 100644 --- a/src/server/cl-engine/src/client_manager.hpp +++ b/src/server/cl-engine/src/client_manager.hpp @@ -9,6 +9,10 @@ class ClientManager { std::unordered_map *> *thread_awaiting_file_creation; std::unordered_map *> *thread_awaiting_data; + // TODO: this is an approx. here opnly the creator will commit the file. + // TODO: more complex checks needs to be done but this is a temporary fix + std::unordered_map *> *files_to_be_committed_by_tid; + public: ClientManager() { bufs_response = new CSBufResponse_t(); @@ -16,6 +20,8 @@ class ClientManager { thread_awaiting_file_creation = new std::unordered_map *>; thread_awaiting_data = new std::unordered_map *>; + + files_to_be_committed_by_tid = new std::unordered_map *>; } ~ClientManager() { @@ -23,6 +29,7 @@ class ClientManager { delete app_names; delete thread_awaiting_file_creation; delete thread_awaiting_data; + delete files_to_be_committed_by_tid; std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_response cleanup completed" << std::endl; } @@ -39,6 +46,7 @@ class ClientManager { CAPIO_REQ_BUFF_CNT, sizeof(off_t), workflow_name); bufs_response->insert(std::make_pair(tid, p_buf_response)); app_names->emplace(tid, app_name); + files_to_be_committed_by_tid->emplace(tid, new std::vector); } /** @@ -52,6 +60,7 @@ class ClientManager { delete it_resp->second; bufs_response->erase(it_resp); } + files_to_be_committed_by_tid->erase(tid); } /** @@ -91,24 +100,38 @@ class ClientManager { } void unlock_thread_awaiting_data(std::string path) { + START_LOG(gettid(), "call(path=%s)", path.c_str()); auto path_size = std::filesystem::file_size(path); - if (thread_awaiting_data->find(path) == thread_awaiting_data->end()) { + if (thread_awaiting_data->find(path) != thread_awaiting_data->end()) { + LOG("Path has thread awaiting"); auto th = thread_awaiting_data->at(path); std::vector item_to_delete; + for (auto item : *th) { - if (item.second >= std::filesystem::file_size(path)) { + LOG("Handling thread"); + if (CapioFileManager::is_committed(path) || + item.second >= std::filesystem::file_size(path)) { + LOG("Thread %ld can be unlocked", item.first); reply_to_client(item.first, path_size); item_to_delete.emplace_back(item.first); } } - // cleanup of served clients for (auto itm : item_to_delete) { + LOG("Cleanup of thread %ld", itm); th->erase(itm); } } } + + void add_producer_file_path(int tid, std::string &path) const { + files_to_be_committed_by_tid->at(tid)->emplace_back(path); + } + + [[nodiscard]] auto get_produced_files(int tid) const { + return files_to_be_committed_by_tid->at(tid); + } }; inline ClientManager *client_manager; diff --git a/src/server/cl-engine/src/file_manager.hpp b/src/server/cl-engine/src/file_manager.hpp new file mode 100644 index 000000000..8928c34a4 --- /dev/null +++ b/src/server/cl-engine/src/file_manager.hpp @@ -0,0 +1,29 @@ +#ifndef FILE_MANAGER_HPP +#define FILE_MANAGER_HPP +#include "client_manager.hpp" + +inline void CapioFileManager::set_committed(const std::filesystem::path &path) { + START_LOG(gettid(), "call(path=%s)", path.c_str()); + auto metadata_path = path.string() + ".capio"; + LOG("Creating token %s", metadata_path.c_str()); + auto fd = creat(metadata_path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + LOG("Token fd = %d", fd); + close(fd); + client_manager->unlock_thread_awaiting_data(path); + client_manager->unlock_thread_awaiting_creation(path); +} + +inline void CapioFileManager::set_committed(long tid) { + START_LOG(gettid(), "call(tid=%d)", tid); + auto files = client_manager->get_produced_files(tid); + for (const auto &file : *files) { + LOG("Committing file %s", file.c_str()); + CapioFileManager::set_committed(file); + } +} + +inline bool CapioFileManager::is_committed(const std::filesystem::path &path) { + return std::filesystem::exists(path / ".capio"); +} + +#endif // FILE_MANAGER_HPP diff --git a/src/server/cl-engine/src/file_manager_header.hpp b/src/server/cl-engine/src/file_manager_header.hpp new file mode 100644 index 000000000..6a049335c --- /dev/null +++ b/src/server/cl-engine/src/file_manager_header.hpp @@ -0,0 +1,11 @@ +#ifndef FILE_MANAGER_HEADER_HPP +#define FILE_MANAGER_HEADER_HPP + +class CapioFileManager { + public: + static void set_committed(const std::filesystem::path& path); + static void set_committed(long tid); + static bool is_committed(const std::filesystem::path &path); +}; + +#endif // FILE_MANAGER_HEADER_HPP diff --git a/src/server/cl-engine/src/handlers.hpp b/src/server/cl-engine/src/handlers.hpp index 1e3d32fa6..f7c6349d8 100644 --- a/src/server/cl-engine/src/handlers.hpp +++ b/src/server/cl-engine/src/handlers.hpp @@ -9,7 +9,6 @@ #include "handlers/open.hpp" #include "handlers/read.hpp" #include "handlers/rename.hpp" -#include "handlers/seek.hpp" #include "handlers/write.hpp" #endif // HANDLERS_HPP diff --git a/src/server/cl-engine/src/handlers/close.hpp b/src/server/cl-engine/src/handlers/close.hpp index 38210e6f3..85f2c807b 100644 --- a/src/server/cl-engine/src/handlers/close.hpp +++ b/src/server/cl-engine/src/handlers/close.hpp @@ -7,13 +7,15 @@ inline void close_handler(const char *const str) { char path[PATH_MAX]; sscanf(str, "%d %s", &tid, path); + START_LOG(gettid(), "call(tid=%d, path=%s)", tid, path); + std::filesystem::path filename(path); if (path == get_capio_dir() || !capio_configuration->file_to_be_handled(filename)) { return; } - // TODO: gestire le CoC + CapioFileManager::set_committed(path); } #endif // CAPIO_CLOSE_HPP diff --git a/src/server/cl-engine/src/handlers/consent.hpp b/src/server/cl-engine/src/handlers/consent.hpp index 3cc8eb164..e0d7ac8ca 100644 --- a/src/server/cl-engine/src/handlers/consent.hpp +++ b/src/server/cl-engine/src/handlers/consent.hpp @@ -29,7 +29,7 @@ inline void consent_to_proceed_handler(const char *const str) { return; } - if (std::filesystem::exists(path)) { + if (std::filesystem::exists(path) || CapioFileManager::is_committed(path)) { client_manager->reply_to_client(tid, 1); } else { client_manager->add_thread_awaiting_creation(path, tid); diff --git a/src/server/cl-engine/src/handlers/create.hpp b/src/server/cl-engine/src/handlers/create.hpp index b6e998c4f..20dabbc89 100644 --- a/src/server/cl-engine/src/handlers/create.hpp +++ b/src/server/cl-engine/src/handlers/create.hpp @@ -7,7 +7,7 @@ inline void create_handler(const char *const str) { int tid; char path[PATH_MAX]; sscanf(str, "%d %s", &tid, path); - + START_LOG(gettid(), "call(tid=%d, path=%s)", tid, path); client_manager->unlock_thread_awaiting_creation(path); } diff --git a/src/server/cl-engine/src/handlers/exit.hpp b/src/server/cl-engine/src/handlers/exit.hpp index 89d0e5540..7e39aa669 100644 --- a/src/server/cl-engine/src/handlers/exit.hpp +++ b/src/server/cl-engine/src/handlers/exit.hpp @@ -5,10 +5,10 @@ inline void exit_handler(const char *const str) { // TODO: register files open for each tid ti register a close int tid; - char path[PATH_MAX]; sscanf(str, "%d", &tid); - - // TODO: handle commits on termination + START_LOG(gettid(), "call(tid=%d)", tid); + CapioFileManager::set_committed(tid); + client_manager->remove_client(tid); } #endif // CAPIO_EXIT_HPP diff --git a/src/server/cl-engine/src/handlers/read.hpp b/src/server/cl-engine/src/handlers/read.hpp index 0020cc20f..53d92eadb 100644 --- a/src/server/cl-engine/src/handlers/read.hpp +++ b/src/server/cl-engine/src/handlers/read.hpp @@ -24,7 +24,7 @@ inline void read_handler(const char *const str) { return; } - if (std::filesystem::file_size(path) >= end_of_read) { + if (std::filesystem::file_size(path) >= end_of_read || CapioFileManager::is_committed(path)) { client_manager->reply_to_client(tid, 1); } else { client_manager->add_thread_awaiting_data(path, tid, end_of_read); diff --git a/src/server/cl-engine/src/handlers/rename.hpp b/src/server/cl-engine/src/handlers/rename.hpp index 5a47da0fe..e06465736 100644 --- a/src/server/cl-engine/src/handlers/rename.hpp +++ b/src/server/cl-engine/src/handlers/rename.hpp @@ -6,7 +6,7 @@ inline void rename_handler(const char *const str) { int tid; char old_path[PATH_MAX], new_path[PATH_MAX]; sscanf(str, "%d %s %s", &tid, old_path, new_path); - + START_LOG(gettid(), "call(tid=%d, old=%s, new=%s)", tid, old_path, new_path); client_manager->unlock_thread_awaiting_creation(new_path); // TODO: gestire le rename? } diff --git a/src/server/cl-engine/src/handlers/seek.hpp b/src/server/cl-engine/src/handlers/seek.hpp deleted file mode 100644 index 4d837f081..000000000 --- a/src/server/cl-engine/src/handlers/seek.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef CAPIO_SEEK_HPP -#define CAPIO_SEEK_HPP - -inline void seek_handler(const char *const str) { - long tid, target_offset; - int whence, fd; - char path[PATH_MAX]; - sscanf(str, "%ld %s %ld %d %d", &tid, path, &target_offset, &fd, &whence); - START_LOG(gettid(), "call()"); - std::filesystem::path filename(path); - - // TODO: MIGHT NOT BE NEEDED - if (path == get_capio_dir() || !capio_configuration->file_to_be_handled(filename)) { - return; - } - auto path_size = std::filesystem::file_size(path); - if (path_size >= target_offset) { - client_manager->reply_to_client(tid, path_size); - } else { - } -} - -#endif // CAPIO_SEEK_HPP diff --git a/src/server/cl-engine/src/handlers/write.hpp b/src/server/cl-engine/src/handlers/write.hpp index 0f162481d..879d2bae7 100644 --- a/src/server/cl-engine/src/handlers/write.hpp +++ b/src/server/cl-engine/src/handlers/write.hpp @@ -5,13 +5,14 @@ inline void write_handler(const char *const str) { int tid, count, fd; char path[PATH_MAX]; sscanf(str, "%ld %ld %s %ld", &tid, &fd, path, &count); - + START_LOG(gettid(), "call(tid=%d, fd=%d, path=%s, count=%d)", tid, fd, path, count); std::filesystem::path filename(path); if (path == get_capio_dir() || !capio_configuration->file_to_be_handled(filename)) { return; } + LOG("File needs to be handled"); client_manager->unlock_thread_awaiting_data(path); } From 18ebd6faaa2c54ef53b9272f8c50791384de3e7e Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Wed, 31 Jul 2024 17:33:06 +0200 Subject: [PATCH 005/151] Implemented write cache for write requests Added optimization in read operations --- .github/workflows/ci-tests.yaml | 106 +++++++++--------- CMakeLists.txt | 2 +- src/common/capio/constants.hpp | 38 ++++--- src/common/capio/queue.hpp | 12 +- src/posix/handlers/access.hpp | 7 +- src/posix/handlers/chdir.hpp | 2 +- src/posix/handlers/close.hpp | 2 +- src/posix/handlers/dup.hpp | 12 +- src/posix/handlers/execve.hpp | 2 +- src/posix/handlers/exit_group.hpp | 5 +- src/posix/handlers/fchmod.hpp | 2 +- src/posix/handlers/fchown.hpp | 2 +- src/posix/handlers/fcntl.hpp | 2 +- src/posix/handlers/fgetxattr.hpp | 2 +- src/posix/handlers/getdents.hpp | 2 +- src/posix/handlers/ioctl.hpp | 2 +- src/posix/handlers/lseek.hpp | 6 +- src/posix/handlers/mkdir.hpp | 10 +- src/posix/handlers/open.hpp | 6 +- src/posix/handlers/read.hpp | 27 +++-- src/posix/handlers/rename.hpp | 2 +- src/posix/handlers/stat.hpp | 18 ++- src/posix/handlers/statfs.hpp | 7 +- src/posix/handlers/statx.hpp | 4 +- src/posix/handlers/unlink.hpp | 52 +-------- src/posix/handlers/write.hpp | 24 ++-- src/posix/utils/cache.hpp | 102 +++++++++++++++++ src/posix/utils/clone.hpp | 24 ++-- src/posix/utils/common.hpp | 2 +- src/posix/utils/env.hpp | 2 +- src/posix/utils/filesystem.hpp | 18 +-- src/posix/utils/requests.hpp | 37 ++---- src/server/cl-engine/src/client_manager.hpp | 34 +++--- src/server/cl-engine/src/file_manager.hpp | 2 +- .../cl-engine/src/file_manager_header.hpp | 4 +- src/server/cl-engine/src/handlers/close.hpp | 2 +- src/server/cl-engine/src/handlers/consent.hpp | 4 +- src/server/cl-engine/src/handlers/create.hpp | 2 +- src/server/cl-engine/src/handlers/exit.hpp | 2 +- .../cl-engine/src/handlers/handshake.hpp | 4 +- src/server/cl-engine/src/handlers/open.hpp | 5 +- src/server/cl-engine/src/handlers/read.hpp | 18 ++- src/server/cl-engine/src/handlers/rename.hpp | 2 +- src/server/cl-engine/src/handlers/write.hpp | 9 +- src/server/cl-engine/src/json_parser.hpp | 4 - src/server/utils/common.hpp | 20 ---- src/server/utils/types.hpp | 2 +- 47 files changed, 346 insertions(+), 309 deletions(-) create mode 100644 src/posix/utils/cache.hpp diff --git a/.github/workflows/ci-tests.yaml b/.github/workflows/ci-tests.yaml index c351985ad..69a573644 100644 --- a/.github/workflows/ci-tests.yaml +++ b/.github/workflows/ci-tests.yaml @@ -17,59 +17,59 @@ jobs: - uses: actions/checkout@v4 - name: "Run codespell" uses: codespell-project/actions-codespell@v2 - docker-check: - name: "Check Docker image" - runs-on: ubuntu-22.04 - strategy: - matrix: - cmake-build-type: - - "Debug" - - "Release" - steps: - - uses: actions/checkout@v4 - - uses: docker/setup-qemu-action@v3 - - uses: docker/setup-buildx-action@v3 - - name: "Build Docker image" - uses: docker/build-push-action@v5 - with: - build-args: | - CAPIO_BUILD_TESTS=ON - CAPIO_LOG=${{ matrix.cmake-build-type == 'Debug' && 'ON' || 'OFF' }} - CMAKE_BUILD_TYPE=${{ matrix.cmake-build-type }} - load: true - tags: alphaunito/capio:latest - - name: "Run unit tests with Docker" - run: | - echo "Run CAPIO POSIX Unit tests" - docker run --rm \ - --env CAPIO_DIR=/tmp \ - --env CAPIO_LOG_LEVEL=-1 \ - --name capio-docker \ - alphaunito/capio:latest \ - capio_posix_unit_tests \ - --gtest_break_on_failure \ - --gtest_print_time=1 - - echo "Run CAPIO server Unit tests" - docker run --rm \ - --env CAPIO_DIR=/tmp \ - --env CAPIO_LOG_LEVEL=-1 \ - --name capio-docker \ - alphaunito/capio:latest \ - capio_server_unit_tests \ - --gtest_break_on_failure \ - --gtest_print_time=1 - - echo "Run CAPIO integration tests" - docker run --rm \ - --env CAPIO_DIR=/tmp \ - --env CAPIO_LOG_LEVEL=-1 \ - --env LD_PRELOAD=libcapio_posix.so \ - --name capio-docker \ - alphaunito/capio:latest \ - capio_integration_tests \ - --gtest_break_on_failure \ - --gtest_print_time=1 +# docker-check: +# name: "Check Docker image" +# runs-on: ubuntu-22.04 +# strategy: +# matrix: +# cmake-build-type: +# - "Debug" +# - "Release" +# steps: +# - uses: actions/checkout@v4 +# - uses: docker/setup-qemu-action@v3 +# - uses: docker/setup-buildx-action@v3 +# - name: "Build Docker image" +# uses: docker/build-push-action@v5 +# with: +# build-args: | +# CAPIO_BUILD_TESTS=ON +# CAPIO_LOG=${{ matrix.cmake-build-type == 'Debug' && 'ON' || 'OFF' }} +# CMAKE_BUILD_TYPE=${{ matrix.cmake-build-type }} +# load: true +# tags: alphaunito/capio:latest +# - name: "Run unit tests with Docker" +# run: | +# echo "Run CAPIO POSIX Unit tests" +# docker run --rm \ +# --env CAPIO_DIR=/tmp \ +# --env CAPIO_LOG_LEVEL=-1 \ +# --name capio-docker \ +# alphaunito/capio:latest \ +# capio_posix_unit_tests \ +# --gtest_break_on_failure \ +# --gtest_print_time=1 +# +# echo "Run CAPIO server Unit tests" +# docker run --rm \ +# --env CAPIO_DIR=/tmp \ +# --env CAPIO_LOG_LEVEL=-1 \ +# --name capio-docker \ +# alphaunito/capio:latest \ +# capio_server_unit_tests \ +# --gtest_break_on_failure \ +# --gtest_print_time=1 +# +# echo "Run CAPIO integration tests" +# docker run --rm \ +# --env CAPIO_DIR=/tmp \ +# --env CAPIO_LOG_LEVEL=-1 \ +# --env LD_PRELOAD=libcapio_posix.so \ +# --name capio-docker \ +# alphaunito/capio:latest \ +# capio_integration_tests \ +# --gtest_break_on_failure \ +# --gtest_print_time=1 format-check: name: "Check ${{ matrix.path }} clang-format conformance" diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f8f488c8..5f0905a03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.15) # Define project name and description -project(capio +project(capio-fs LANGUAGES CXX DESCRIPTION "Cross-Application Programmable I/O" VERSION 1.0.0 diff --git a/src/common/capio/constants.hpp b/src/common/capio/constants.hpp index 96714ed12..50100cca2 100644 --- a/src/common/capio/constants.hpp +++ b/src/common/capio/constants.hpp @@ -7,6 +7,9 @@ #include +// 64bit unsigned long int type for file offsets +typedef unsigned long long int capio_off64_t; + // CAPIO files constants constexpr size_t CAPIO_DEFAULT_DIR_INITIAL_SIZE = 1024L * 1024 * 1024; constexpr off64_t CAPIO_DEFAULT_FILE_INITIAL_SIZE = 1024L * 1024 * 1024 * 4; @@ -85,18 +88,24 @@ constexpr char CAPIO_LOG_POSIX_SYSCALL_END[] = "~~~~~~~~~ END SYSCA constexpr char CAPIO_SERVER_DEFAULT_LOG_FILE_PREFIX[] = "server_thread_\0"; constexpr char CAPIO_LOG_SERVER_BANNER[] = "\n\n " - "\033[1;34m /$$$$$$ /$$$$$$ /$$$$$$$\033[0;96m /$$$$$$ /$$$$$$ \n" - "\033[1;34m /$$__ $$ /$$__ $$| $$__ $$\033[0;96m|_ $$_/ /$$__ $$\n" - "\033[1;34m| $$ \\__/| $$ \\ $$| $$ \\ $$ \033[0;96m | $$ | $$ \\ " - "$$\n" - "\033[1;34m| $$ | $$$$$$$$| $$$$$$$/ \033[0;96m| $$ | $$ | $$\n" - "\033[1;34m| $$ | $$__ $$| $$____/ \033[0;96m| $$ | $$ | $$\n" - "\033[1;34m| $$ $$| $$ | $$| $$ \033[0;96m| $$ | $$ | $$\n" - "\033[1;34m| $$$$$$/| $$ | $$| $$ \033[0;96m/$$$$$$| $$$$$$/\n" - "\033[1;34m \\______/ |__/ |__/|__/ \033[0;96m|______/ " - "\\______/\n\n" - "\033[0m CAPIO - Cross Application Programmable IO \n" - " V. " CAPIO_VERSION "\n\n"; + "\033[1;34m /$$$$$$ /$$$$$$ /$$$$$$$\033[0;96m /$$$$$$ /$$$$$$ \033[1;33m " + "$$$$$$$$\\ $$$$$$\\ \n" + "\033[1;34m /$$__ $$ /$$__ $$| $$__ $$\033[0;96m|_ $$_/ /$$__ $$\033[1;33m $$ " + "_____|$$ __$$\\ \n" + "\033[1;34m| $$ \\__/| $$ \\ $$| $$ \\ $$ \033[0;96m | $$ | $$ \\ $$\033[1;33m $$ " + "| $$ / \\__|\n" + "\033[1;34m| $$ | $$$$$$$$| $$$$$$$/ \033[0;96m| $$ | $$ | $$\033[1;33m $$$$$$\\ " + "$$$$$\\ \\$$$$$$\\ \n" + "\033[1;34m| $$ | $$__ $$| $$____/ \033[0;96m| $$ | $$ | $$\033[1;33m \\______|$$ " + "__| \\____$$\\ \n" + "\033[1;34m| $$ $$| $$ | $$| $$ \033[0;96m| $$ | $$ | $$\033[1;33m $$ | " + " $$\\ $$ |\n" + "\033[1;34m| $$$$$$/| $$ | $$| $$ \033[0;96m/$$$$$$| $$$$$$/\033[1;33m $$ | " + " \\$$$$$$ |\n" + "\033[1;34m \\______/ |__/ |__/|__/ \033[0;96m|______/ \\______/\033[1;33m " + "\\__| \\______/ \n\n" + "\033[0m CAPIO - Cross Application Programmable IO - File System edition \n" + " V. " CAPIO_VERSION "\n\n"; constexpr char CAPIO_LOG_SERVER_CLI_LEVEL_INFO[] = "[ \033[1;32m SERVER \033[0m ] "; constexpr char CAPIO_LOG_SERVER_CLI_LEVEL_WARNING[] = "[ \033[1;33m SERVER \033[0m ] "; constexpr char CAPIO_LOG_SERVER_CLI_LEVEL_ERROR[] = "[ \033[1;31m SERVER \033[0m ] "; @@ -121,9 +130,8 @@ constexpr char CAPIO_LOG_SERVER_REQUEST_END[] = "~~~~~~~~~ END REQUEST ~~~~~~~ // CAPIO server argument parser constexpr char CAPIO_SERVER_ARG_PARSER_PRE[] = - "Cross Application Programmable IO application. developed by Alberto " - "Riccardo Martinelli (UniTO), Massimo Torquati(UniPI), Marco Aldinucci (UniTO), Iacopo " - "Colonnelli(UniTO) and Marco Edoardo Santimaria (UniTO)."; + "Cross Application Programmable IO application. developed by Marco Edoardo Santimaria (UniTO), " + "Iacopo Colonnelli(UniTO), Massimo Torquati(UniPI) and Marco Aldinucci (UniTO), "; constexpr char CAPIO_SERVER_ARG_PARSER_EPILOGUE[] = "For further help, a full list of the available ENVIRONMENT VARIABLES," " and a guide on config JSON file structure, please visit " diff --git a/src/common/capio/queue.hpp b/src/common/capio/queue.hpp index bb90cb82a..912fbfcc2 100644 --- a/src/common/capio/queue.hpp +++ b/src/common/capio/queue.hpp @@ -21,7 +21,7 @@ template class Queue { Mutex _mutex; NamedSemaphore _sem_num_elems, _sem_num_empty; - inline void _read(T *buff_recv, int num_bytes) { + inline void _read(T *buff_recv, unsigned long long int num_bytes) { _sem_num_elems.lock(); std::lock_guard lg(_mutex); @@ -32,7 +32,7 @@ template class Queue { _sem_num_empty.unlock(); } - inline void _write(const T *data, int num_bytes) { + inline void _write(const T *data, unsigned long long int num_bytes) { _sem_num_empty.lock(); std::lock_guard lg(_mutex); @@ -95,8 +95,8 @@ template class Queue { inline auto get_name() { return this->_shm_name; } - inline void read(T *buff_rcv, long int num_bytes) { - START_LOG(capio_syscall(SYS_gettid), "call(buff_rcv=0x%08x, num_bytes=%ld)", buff_rcv, + inline void read(T *buff_rcv, unsigned long long int num_bytes) { + START_LOG(capio_syscall(SYS_gettid), "call(buff_rcv=0x%08x, num_bytes=%llu)", buff_rcv, num_bytes); off64_t n_reads = num_bytes / _elem_size; @@ -129,8 +129,8 @@ template class Queue { return segment; } - inline void write(const T *data, long int num_bytes) { - START_LOG(capio_syscall(SYS_gettid), "call(data=0x%08x, num_bytes=%ld)", data, num_bytes); + inline void write(const T *data, unsigned long long int num_bytes) { + START_LOG(capio_syscall(SYS_gettid), "call(data=0x%08x, num_bytes=%llu)", data, num_bytes); off64_t n_writes = num_bytes / _elem_size; size_t r = num_bytes % _elem_size; diff --git a/src/posix/handlers/access.hpp b/src/posix/handlers/access.hpp index cf5d65850..566069e77 100644 --- a/src/posix/handlers/access.hpp +++ b/src/posix/handlers/access.hpp @@ -8,8 +8,7 @@ int access_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { const std::string_view pathname(reinterpret_cast(arg0)); - auto mode = static_cast(arg1); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call()"); if (is_forbidden_path(pathname)) { LOG("Path %s is forbidden: skip", pathname.data()); @@ -29,9 +28,7 @@ int faccessat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, lon long *result) { auto dirfd = static_cast(arg0); const std::string_view pathname(reinterpret_cast(arg1)); - auto mode = static_cast(arg2); - auto flags = static_cast(arg3); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call()"); if (is_forbidden_path(pathname)) { diff --git a/src/posix/handlers/chdir.hpp b/src/posix/handlers/chdir.hpp index 9191e043a..62eda0b8f 100644 --- a/src/posix/handlers/chdir.hpp +++ b/src/posix/handlers/chdir.hpp @@ -8,7 +8,7 @@ int chdir_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { const std::string_view pathname(reinterpret_cast(arg0)); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call(path=%s)", pathname.data()); diff --git a/src/posix/handlers/close.hpp b/src/posix/handlers/close.hpp index c32a661b0..f909b407f 100644 --- a/src/posix/handlers/close.hpp +++ b/src/posix/handlers/close.hpp @@ -7,7 +7,7 @@ int close_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { int fd = static_cast(arg0); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call(fd=%ld)", fd); if (exists_capio_fd(fd)) { diff --git a/src/posix/handlers/dup.hpp b/src/posix/handlers/dup.hpp index ff494f52c..c8dfea117 100644 --- a/src/posix/handlers/dup.hpp +++ b/src/posix/handlers/dup.hpp @@ -7,7 +7,7 @@ #include "utils/requests.hpp" int dup_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); int fd = static_cast(arg0); START_LOG(tid, "call(fd=%d)", fd); @@ -27,14 +27,14 @@ int dup_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5 } int dup2_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - long tid = syscall_no_intercept(SYS_gettid); - int fd = static_cast(arg0); - int fd2 = static_cast(arg1); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); + auto fd = static_cast(arg0); + auto fd2 = static_cast(arg1); START_LOG(tid, "call(fd=%d, fd2=%d)", fd, fd2); if (exists_capio_fd(fd)) { - int res = static_cast(syscall_no_intercept(SYS_dup2, fd, fd2)); + auto res = static_cast(syscall_no_intercept(SYS_dup2, fd, fd2)); if (res == -1) { *result = -errno; return CAPIO_POSIX_SYSCALL_SUCCESS; @@ -49,7 +49,7 @@ int dup2_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg } int dup3_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); int fd = static_cast(arg0); int fd2 = static_cast(arg1); int flags = static_cast(arg2); diff --git a/src/posix/handlers/execve.hpp b/src/posix/handlers/execve.hpp index 865003365..b26d97224 100644 --- a/src/posix/handlers/execve.hpp +++ b/src/posix/handlers/execve.hpp @@ -6,7 +6,7 @@ #include "utils/snapshot.hpp" int execve_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call()"); create_snapshot(tid); diff --git a/src/posix/handlers/exit_group.hpp b/src/posix/handlers/exit_group.hpp index b6a720970..ad58a1cb1 100644 --- a/src/posix/handlers/exit_group.hpp +++ b/src/posix/handlers/exit_group.hpp @@ -15,7 +15,7 @@ */ int exit_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call()"); if (is_capio_tid(tid)) { @@ -25,6 +25,9 @@ int exit_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg remove_capio_tid(tid); } + delete write_request_cache; + delete read_request_cache; + return CAPIO_POSIX_SYSCALL_SKIP; } diff --git a/src/posix/handlers/fchmod.hpp b/src/posix/handlers/fchmod.hpp index 22ffa5c3d..6981918ca 100644 --- a/src/posix/handlers/fchmod.hpp +++ b/src/posix/handlers/fchmod.hpp @@ -5,7 +5,7 @@ int fchmod_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { int fd = static_cast(arg0); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(syscall_no_intercept(SYS_gettid), "call(fd=%d)", fd); if (!exists_capio_fd(fd)) { diff --git a/src/posix/handlers/fchown.hpp b/src/posix/handlers/fchown.hpp index 5d5982d07..0e8955f60 100644 --- a/src/posix/handlers/fchown.hpp +++ b/src/posix/handlers/fchown.hpp @@ -6,7 +6,7 @@ int fchown_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { int fd = static_cast(arg0); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(syscall_no_intercept(SYS_gettid), "call(fd=%d)", fd); if (!exists_capio_fd(fd)) { diff --git a/src/posix/handlers/fcntl.hpp b/src/posix/handlers/fcntl.hpp index aa77c5455..3af9a39bf 100644 --- a/src/posix/handlers/fcntl.hpp +++ b/src/posix/handlers/fcntl.hpp @@ -9,7 +9,7 @@ int fcntl_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long ar auto fd = static_cast(arg0); auto cmd = static_cast(arg1); auto arg = static_cast(arg2); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call(fd=%d, cmd=%d, arg=%d)", fd, cmd, arg); diff --git a/src/posix/handlers/fgetxattr.hpp b/src/posix/handlers/fgetxattr.hpp index adefb117a..ee5c6d3fc 100644 --- a/src/posix/handlers/fgetxattr.hpp +++ b/src/posix/handlers/fgetxattr.hpp @@ -8,7 +8,7 @@ int fgetxattr_handler(long arg0, long arg1, long arg2, long arg3, long arg4, lon std::string name(reinterpret_cast(arg1)); auto *value = reinterpret_cast(arg2); auto size = static_cast(arg3); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); auto fd = static_cast(arg0); START_LOG(tid, "call(name=%s, value=0x%08x, size=%ld)", name.c_str(), value, size); diff --git a/src/posix/handlers/getdents.hpp b/src/posix/handlers/getdents.hpp index 9bcdd6ce6..9429084f2 100644 --- a/src/posix/handlers/getdents.hpp +++ b/src/posix/handlers/getdents.hpp @@ -8,7 +8,7 @@ inline int getdents_handler_impl(long arg0, long arg1, long arg2, long *result, auto fd = static_cast(arg0); auto *buffer = reinterpret_cast(arg1); auto count = static_cast(arg2); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call(fd=%d, dirp=0x%08x, count=%ld, is64bit=%s)", fd, buffer, count, is64bit ? "true" : "false"); diff --git a/src/posix/handlers/ioctl.hpp b/src/posix/handlers/ioctl.hpp index 98afe9226..9d36cf0bb 100644 --- a/src/posix/handlers/ioctl.hpp +++ b/src/posix/handlers/ioctl.hpp @@ -6,7 +6,7 @@ int ioctl_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { auto fd = static_cast(arg0); auto request = static_cast(arg1); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call(fd=%d, request=%ld)", fd, request, tid); if (exists_capio_fd(fd)) { diff --git a/src/posix/handlers/lseek.hpp b/src/posix/handlers/lseek.hpp index 2a04498d7..31c4484f4 100644 --- a/src/posix/handlers/lseek.hpp +++ b/src/posix/handlers/lseek.hpp @@ -7,13 +7,13 @@ int lseek_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { int fd = static_cast(arg0); - auto offset = static_cast(arg1); + auto offset = static_cast(arg1); int whence = static_cast(arg2); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call(fd=%d, offset=%ld, whence=%d)", fd, offset, whence); if (exists_capio_fd(fd)) { - off64_t computed_offset = 0; + capio_off64_t computed_offset = 0; if (whence == SEEK_CUR) { computed_offset = get_capio_fd_offset(fd) + offset; diff --git a/src/posix/handlers/mkdir.hpp b/src/posix/handlers/mkdir.hpp index 825e85ed0..6f7b84bc3 100644 --- a/src/posix/handlers/mkdir.hpp +++ b/src/posix/handlers/mkdir.hpp @@ -6,7 +6,7 @@ #include "utils/common.hpp" #include "utils/filesystem.hpp" -inline off64_t capio_mkdirat(int dirfd, const std::string_view &pathname, mode_t mode, long tid) { +inline off64_t capio_mkdirat(int dirfd, const std::string_view &pathname, mode_t mode, pid_t tid) { START_LOG(tid, "call(dirfd=%d, pathname=%s, mode=%o)", dirfd, pathname.data(), mode); if (is_forbidden_path(pathname)) { @@ -40,7 +40,7 @@ inline off64_t capio_mkdirat(int dirfd, const std::string_view &pathname, mode_t return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } -inline off64_t capio_rmdir(const std::string_view &pathname, long tid) { +inline off64_t capio_rmdir(const std::string_view &pathname, pid_t tid) { START_LOG(tid, "call(pathname=%s)", pathname.data()); return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; @@ -49,7 +49,7 @@ inline off64_t capio_rmdir(const std::string_view &pathname, long tid) { int mkdir_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { const std::string_view pathname(reinterpret_cast(arg0)); auto mode = static_cast(arg1); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); return posix_return_value(capio_mkdirat(AT_FDCWD, pathname, mode, tid), result); } @@ -59,14 +59,14 @@ int mkdirat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long int dirfd = static_cast(arg0); const std::string_view pathname(reinterpret_cast(arg1)); auto mode = static_cast(arg2); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); return posix_return_value(capio_mkdirat(dirfd, pathname, mode, tid), result); } int rmdir_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { const std::string_view pathname(reinterpret_cast(arg0)); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); return posix_return_value(capio_rmdir(pathname, tid), result); } diff --git a/src/posix/handlers/open.hpp b/src/posix/handlers/open.hpp index 5428d3c7c..c5f5731aa 100644 --- a/src/posix/handlers/open.hpp +++ b/src/posix/handlers/open.hpp @@ -36,7 +36,7 @@ std::string compute_abs_path(char *pathname, int dirfd) { int creat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { std::string pathname(reinterpret_cast(arg0)); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); int flags = O_CREAT | O_WRONLY | O_TRUNC; mode_t mode = static_cast(arg2); START_LOG(tid, "call(path=%s, flags=%d, mode=%d)", pathname.data(), flags, mode); @@ -66,7 +66,7 @@ int open_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg std::string pathname(reinterpret_cast(arg0)); int flags = static_cast(arg1); mode_t mode = static_cast(arg2); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call(path=%s, flags=%d, mode=%d)", pathname.data(), flags, mode); std::string path = compute_abs_path(pathname.data(), -1); @@ -98,7 +98,7 @@ int openat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long a std::string pathname(reinterpret_cast(arg1)); int flags = static_cast(arg2); mode_t mode = static_cast(arg3); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call(path=%s, flags=%d, mode=%d)", pathname.data(), flags, mode); std::string path = compute_abs_path(pathname.data(), dirfd); diff --git a/src/posix/handlers/read.hpp b/src/posix/handlers/read.hpp index cee2e6814..39976b9db 100644 --- a/src/posix/handlers/read.hpp +++ b/src/posix/handlers/read.hpp @@ -4,28 +4,35 @@ #if defined(SYS_read) || defined(SYS_readv) int read_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - int fd = static_cast(arg0); - void *buffer = reinterpret_cast(arg1); - auto count = static_cast(arg2); - long tid = syscall_no_intercept(SYS_gettid); + int fd = static_cast(arg0); + auto count = static_cast(arg2); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); + + START_LOG(capio_syscall(SYS_gettid), "call(fd=%d, tid=%d, count=%ld)", fd, tid, count); if (exists_capio_fd(fd)) { auto computed_offset = get_capio_fd_offset(fd) + count; - read_request(get_capio_fd_path(fd), computed_offset , tid, fd); + + LOG("Handling read on file %s up to byte %ld", get_capio_fd_path(fd).c_str(), + computed_offset); + + read_request_cache->read_request(get_capio_fd_path(fd), computed_offset, tid, fd); + set_capio_fd_offset(fd, computed_offset); } return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } int readv_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - auto fd = static_cast(arg0); - const auto *iov = reinterpret_cast(arg1); - auto iovcnt = static_cast(arg2); - long tid = syscall_no_intercept(SYS_gettid); + auto fd = static_cast(arg0); + auto iovcnt = static_cast(arg2); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); if (exists_capio_fd(fd)) { auto computed_offset = get_capio_fd_offset(fd) + iovcnt * sizeof(iovec); - read_request(get_capio_fd_path(fd), computed_offset, tid, fd); + + read_request_cache->read_request(get_capio_fd_path(fd), computed_offset, tid, fd); + set_capio_fd_offset(fd, computed_offset); } return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; diff --git a/src/posix/handlers/rename.hpp b/src/posix/handlers/rename.hpp index 42dc1f092..96dad6ec0 100644 --- a/src/posix/handlers/rename.hpp +++ b/src/posix/handlers/rename.hpp @@ -8,7 +8,7 @@ int rename_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { const std::filesystem::path oldpath(reinterpret_cast(arg0)); const std::filesystem::path newpath(reinterpret_cast(arg1)); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call(oldpath=%s, newpath=%s)", oldpath.c_str(), newpath.c_str()); auto oldpath_abs = capio_absolute(oldpath); diff --git a/src/posix/handlers/stat.hpp b/src/posix/handlers/stat.hpp index faa97580d..ad903ae4e 100644 --- a/src/posix/handlers/stat.hpp +++ b/src/posix/handlers/stat.hpp @@ -11,11 +11,7 @@ #include "utils/filesystem.hpp" #include "utils/requests.hpp" -inline blkcnt_t get_nblocks(off64_t file_size) { - return (file_size % 4096 == 0) ? (file_size / 512) : (file_size / 512 + 8); -} - -inline int capio_fstat(int fd, struct stat *statbuf, long tid) { +inline int capio_fstat(int fd, struct stat *statbuf, pid_t tid) { START_LOG(tid, "call(fd=%d, statbuf=0x%08x)", fd, statbuf); if (exists_capio_fd(fd)) { @@ -24,7 +20,7 @@ inline int capio_fstat(int fd, struct stat *statbuf, long tid) { return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } -inline int capio_lstat(const std::string_view &pathname, struct stat *statbuf, long tid) { +inline int capio_lstat(const std::string_view &pathname, struct stat *statbuf, pid_t tid) { START_LOG(tid, "call(absolute_path=%s, statbuf=0x%08x)", pathname.data(), statbuf); if (is_forbidden_path(pathname)) { @@ -39,7 +35,7 @@ inline int capio_lstat(const std::string_view &pathname, struct stat *statbuf, l return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } -inline int capio_lstat_wrapper(const std::string_view &pathname, struct stat *statbuf, long tid) { +inline int capio_lstat_wrapper(const std::string_view &pathname, struct stat *statbuf, pid_t tid) { START_LOG(tid, "call(path=%s, buf=0x%08x)", pathname.data(), statbuf); if (is_forbidden_path(pathname)) { @@ -56,7 +52,7 @@ inline int capio_lstat_wrapper(const std::string_view &pathname, struct stat *st } inline int capio_fstatat(int dirfd, const std::string_view &pathname, struct stat *statbuf, - int flags, long tid) { + int flags, pid_t tid) { START_LOG(tid, "call(dirfd=%ld, pathname=%s, statbuf=0x%08x, flags=%X)", dirfd, pathname.data(), statbuf, flags); @@ -96,7 +92,7 @@ inline int capio_fstatat(int dirfd, const std::string_view &pathname, struct sta int fstat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { auto fd = static_cast(arg0); auto *buf = reinterpret_cast(arg1); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); return posix_return_value(capio_fstat(fd, buf, tid), result); } @@ -107,7 +103,7 @@ int fstatat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long const std::string_view pathname(reinterpret_cast(arg1)); auto *statbuf = reinterpret_cast(arg2); auto flags = static_cast(arg3); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); return posix_return_value(capio_fstatat(dirfd, pathname, statbuf, flags, tid), result); } @@ -115,7 +111,7 @@ int fstatat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long int lstat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { const std::string_view pathname(reinterpret_cast(arg0)); auto *buf = reinterpret_cast(arg1); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); return posix_return_value(capio_lstat_wrapper(pathname, buf, tid), result); } diff --git a/src/posix/handlers/statfs.hpp b/src/posix/handlers/statfs.hpp index 42c630f00..4b12ff0b0 100644 --- a/src/posix/handlers/statfs.hpp +++ b/src/posix/handlers/statfs.hpp @@ -5,11 +5,10 @@ int fstatfs_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - auto fd = static_cast(arg0); - auto *buf = reinterpret_cast(arg1); - long tid = syscall_no_intercept(SYS_gettid); + auto fd = static_cast(arg0); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); - START_LOG(tid, "call(fd=%d, buf=0x%08x)", fd, buf); + START_LOG(tid, "call(fd=%d)", fd); if (exists_capio_fd(fd)) { consent_to_proceed_request(get_capio_fd_path(fd), tid); diff --git a/src/posix/handlers/statx.hpp b/src/posix/handlers/statx.hpp index 18484ebf7..1bcc5fc6a 100644 --- a/src/posix/handlers/statx.hpp +++ b/src/posix/handlers/statx.hpp @@ -6,7 +6,7 @@ #include "utils/common.hpp" inline int capio_statx(int dirfd, const std::string_view &pathname, int flags, int mask, - struct statx *statxbuf, long tid) { + struct statx *statxbuf, pid_t tid) { START_LOG(tid, "call(dirfd=%d, pathname=%s, flags=%d, mask=%d, statxbuf=0x%08x)", dirfd, pathname.data(), flags, mask, statxbuf); @@ -29,7 +29,7 @@ int statx_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long ar auto flags = static_cast(arg2); auto mask = static_cast(arg3); auto *buf = reinterpret_cast(arg4); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); return posix_return_value(capio_statx(dirfd, pathname, flags, mask, buf, tid), result); } diff --git a/src/posix/handlers/unlink.hpp b/src/posix/handlers/unlink.hpp index 4ad36234e..e7e03bc22 100644 --- a/src/posix/handlers/unlink.hpp +++ b/src/posix/handlers/unlink.hpp @@ -5,55 +5,9 @@ #include "utils/common.hpp" -off64_t capio_unlink_abs(const std::filesystem::path &abs_path, long tid, bool is_dir) { - START_LOG(tid, "call(abs_path=%s, is_dir=%s)", abs_path.c_str(), is_dir ? "true" : "false"); - - if (is_capio_path(abs_path)) { - LOG("Removing %s from capio_files_path", abs_path.c_str()); - delete_capio_path(abs_path); - } - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; -} - -inline off64_t capio_unlinkat(int dirfd, const std::string_view &pathname, int flags, long tid) { - START_LOG(tid, "call(dirfd=%d, pathname=%s, flags=%X)", dirfd, pathname.data(), flags); - - if (is_forbidden_path(pathname)) { - LOG("Path %s is forbidden: skip", pathname.data()); - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; - } - - off64_t res; - bool is_dir = flags & AT_REMOVEDIR; - std::filesystem::path path(pathname); - if (path.is_relative()) { - if (dirfd == AT_FDCWD) { - path = capio_posix_realpath(path); - if (path.empty()) { - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; - } - res = capio_unlink_abs(path, tid, is_dir); - } else { - if (!is_directory(dirfd)) { - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; - } - const std::filesystem::path dir_path = get_dir_path(dirfd); - if (dir_path.empty()) { - return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; - } - path = (dir_path / path).lexically_normal(); - res = capio_unlink_abs(path, tid, is_dir); - } - } else { - res = capio_unlink_abs(path, tid, is_dir); - } - - return res; -} - int unlink_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { std::string_view pathname(reinterpret_cast(arg0)); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call(path=%s)", pathname.data()); @@ -67,10 +21,8 @@ int unlink_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long a int unlinkat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - int dirfd = static_cast(arg0); const std::string_view pathname(reinterpret_cast(arg1)); - int flags = static_cast(arg2); - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call(path=%s)", pathname.data()); auto path = capio_posix_realpath(pathname); diff --git a/src/posix/handlers/write.hpp b/src/posix/handlers/write.hpp index 734361b27..1dc89a488 100644 --- a/src/posix/handlers/write.hpp +++ b/src/posix/handlers/write.hpp @@ -6,31 +6,29 @@ #include "utils/common.hpp" #include "utils/requests.hpp" -inline off64_t capio_write(int fd, const void *buffer, off64_t count, long tid) { - START_LOG(tid, "call(fd=%d, buf=0x%08x, count=%ld)", fd, buffer, count); +inline off64_t capio_write(int fd, capio_off64_t count, pid_t tid) { + START_LOG(tid, "call(fd=%d, count=%ld)", fd, count); if (exists_capio_fd(fd)) { - write_request(get_capio_fd_path(fd), count, tid, fd); + write_request_cache->write_request(get_capio_fd_path(fd), count, tid, fd); } return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } int write_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - auto fd = static_cast(arg0); - const auto *buf = reinterpret_cast(arg1); - auto count = static_cast(arg2); - long tid = syscall_no_intercept(SYS_gettid); + auto fd = static_cast(arg0); + auto count = static_cast(arg2); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); - return posix_return_value(capio_write(fd, buf, count, tid), result); + return posix_return_value(capio_write(fd, count, tid), result); } int writev_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - auto fd = static_cast(arg0); - const auto *iov = reinterpret_cast(arg1); - auto iovcnt = static_cast(arg2); - long tid = syscall_no_intercept(SYS_gettid); + auto fd = static_cast(arg0); + auto iovcnt = static_cast(arg2); + long tid = syscall_no_intercept(SYS_gettid); - return posix_return_value(capio_write(fd, iov, iovcnt, tid), result); + return posix_return_value(capio_write(fd, iovcnt, tid), result); } #endif // SYS_write || SYS_writev diff --git a/src/posix/utils/cache.hpp b/src/posix/utils/cache.hpp new file mode 100644 index 000000000..3404de028 --- /dev/null +++ b/src/posix/utils/cache.hpp @@ -0,0 +1,102 @@ +#ifndef CAPIO_CACHE_HPP +#define CAPIO_CACHE_HPP + +class WriteRequestCache { + + int current_fd = -1; + long long current_size = 0; + + const long long _max_size; + + std::filesystem::path current_path; + + // non-blocking as write is not in the pre port of CAPIO semantics + inline void _write_request(const off64_t count, const long tid, const long fd) { + START_LOG(capio_syscall(SYS_gettid), "call(path=%s, count=%ld, tid=%ld)", + current_path.c_str(), count, tid); + char req[CAPIO_REQ_MAX_SIZE]; + sprintf(req, "%04d %ld %ld %s %ld", CAPIO_REQUEST_WRITE, tid, fd, current_path.c_str(), + count); + buf_requests->write(req, CAPIO_REQ_MAX_SIZE); + } + + public: + explicit WriteRequestCache(long long max_size) : _max_size(max_size) {} + + ~WriteRequestCache() { this->flush(capio_syscall(SYS_gettid)); } + + void write_request(std::filesystem::path path, int tid, int fd, long count) { + + if (fd != current_fd) { + this->flush(tid); + current_path = std::move(path); + current_fd = fd; + } + current_size += count; + + if (current_size > _max_size) { + this->flush(tid); + } + }; + + void flush(int tid) { + if (current_fd != -1 && current_size > 0) { + _write_request(tid, current_fd, current_size); + } + current_size = 0; + } +}; + +class ReadRequestCache { + int current_fd = -1; + long long max_read = 0; + bool is_committed = false; + + std::filesystem::path current_path; + + // return amount of readable bytes + inline off64_t _read_request(const std::filesystem::path &path, const off64_t end_of_Read, + const long tid, const long fd) { + START_LOG(capio_syscall(SYS_gettid), "call(path=%s, end_of_Read=%ld, tid=%ld, fd=%ld)", + path.c_str(), end_of_Read, tid, fd); + char req[CAPIO_REQ_MAX_SIZE]; + sprintf(req, "%04d %s %ld %ld %ld", CAPIO_REQUEST_READ, path.c_str(), tid, fd, end_of_Read); + LOG("Sending read request %s", req); + buf_requests->write(req, CAPIO_REQ_MAX_SIZE); + off64_t res; + bufs_response->at(tid)->read(&res); + LOG("Response to request is %ld", res); + return res; + } + + public: + explicit ReadRequestCache() = default; + + ~ReadRequestCache() = default; + + void read_request(std::filesystem::path path, long end_of_read, int tid, int fd) { + + if (fd != current_fd) { + current_path = std::move(path); + current_fd = fd; + max_read = 0; + is_committed = false; + } + + if (is_committed) { + return; + } + + if (end_of_read > max_read) { + max_read = _read_request(current_path, end_of_read, tid, fd); + if (max_read == -1) { + is_committed = true; + } + } + }; +}; + +thread_local ReadRequestCache *read_request_cache; +thread_local WriteRequestCache *write_request_cache; + +#endif // CAPIO_CACHE_HPP diff --git a/src/posix/utils/clone.hpp b/src/posix/utils/clone.hpp index c2ded01e4..42cf697cf 100644 --- a/src/posix/utils/clone.hpp +++ b/src/posix/utils/clone.hpp @@ -8,28 +8,28 @@ #include "requests.hpp" -std::mutex clone_mutex; -std::condition_variable clone_cv; -std::unordered_set *tids; +inline std::mutex clone_mutex; +inline std::condition_variable clone_cv; +inline std::unordered_set *tids; -inline bool is_capio_tid(const long tid) { +inline bool is_capio_tid(const pid_t tid) { const std::lock_guard lg(clone_mutex); return tids->find(tid) != tids->end(); } -inline void register_capio_tid(const long tid) { +inline void register_capio_tid(const pid_t tid) { const std::lock_guard lg(clone_mutex); tids->insert(tid); } -inline void remove_capio_tid(const long tid) { +inline void remove_capio_tid(const pid_t tid) { const std::lock_guard lg(clone_mutex); tids->erase(tid); } -void init_threading_support() { tids = new std::unordered_set{}; } +void init_threading_support() { tids = new std::unordered_set{}; } -void init_process(long tid) { +void init_process(pid_t tid) { START_LOG(syscall_no_intercept(SYS_gettid), "call(tid=%ld)", tid); syscall_no_intercept_flag = true; @@ -37,7 +37,7 @@ void init_process(long tid) { register_listener(tid); const char *capio_app_name = get_capio_app_name(); - long pid = syscall_no_intercept(SYS_getpid); + auto pid = static_cast(syscall_no_intercept(SYS_gettid)); if (capio_app_name == nullptr) { handshake_anonymous_request(tid, pid); } else { @@ -48,7 +48,7 @@ void init_process(long tid) { } void hook_clone_child() { - long tid = syscall_no_intercept(SYS_gettid); + auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call()"); std::unique_lock lock(clone_mutex); @@ -64,11 +64,13 @@ void hook_clone_child() { lock.unlock(); LOG("Starting child thread %d", tid); + write_request_cache = new WriteRequestCache(8192); + read_request_cache = new ReadRequestCache(); } void hook_clone_parent(long child_tid) { SUSPEND_SYSCALL_LOGGING(); - long parent_tid = syscall_no_intercept(SYS_gettid); + auto parent_tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(parent_tid, "call(parent_tid=%d, child_tid=%d)", parent_tid, child_tid); if (child_tid < 0) { diff --git a/src/posix/utils/common.hpp b/src/posix/utils/common.hpp index 9727b334f..fbf303175 100644 --- a/src/posix/utils/common.hpp +++ b/src/posix/utils/common.hpp @@ -3,7 +3,7 @@ #ifndef CAPIO_FUNCTIONS_H #define CAPIO_FUNCTIONS_H -int posix_return_value(long res, long *result) { +inline int posix_return_value(long res, long *result) { START_LOG(capio_syscall(SYS_gettid), "cal(res=%ld)", res); if (res != CAPIO_POSIX_SYSCALL_REQUEST_SKIP) { *result = (res < 0 ? -errno : res); diff --git a/src/posix/utils/env.hpp b/src/posix/utils/env.hpp index 54cab6275..704639c90 100644 --- a/src/posix/utils/env.hpp +++ b/src/posix/utils/env.hpp @@ -6,7 +6,7 @@ #include "capio/logger.hpp" -const char *get_capio_app_name() { +inline const char *get_capio_app_name() { static char *capio_app_name = std::getenv("CAPIO_APP_NAME"); if (capio_app_name == nullptr) { diff --git a/src/posix/utils/filesystem.hpp b/src/posix/utils/filesystem.hpp index a613dbb18..a28367dc3 100644 --- a/src/posix/utils/filesystem.hpp +++ b/src/posix/utils/filesystem.hpp @@ -17,10 +17,10 @@ #include "types.hpp" -CPFileDescriptors_t *capio_files_descriptors; -CPFilesPaths_t *capio_files_paths; -std::unique_ptr current_dir; -CPFiles_t *files; +inline CPFileDescriptors_t *capio_files_descriptors; +inline CPFilesPaths_t *capio_files_paths; +inline std::unique_ptr current_dir; +inline CPFiles_t *files; /** * Set the CLOEXEC property of a file descriptor in metadata structures @@ -53,8 +53,8 @@ inline void add_capio_path(const std::string &path) { * @param fd * @return */ -inline void add_capio_fd(long tid, const std::string &path, int fd, off64_t offset, - off64_t init_size, int flags, bool is_cloexec) { +inline void add_capio_fd(pid_t tid, const std::string &path, int fd, capio_off64_t offset, + capio_off64_t init_size, int flags, bool is_cloexec) { START_LOG(tid, "call(path=%s, fd=%d)", path.c_str(), fd); add_capio_path(path); LOG("Added capio path %s", path.c_str()); @@ -114,7 +114,7 @@ inline std::filesystem::path capio_absolute(const std::filesystem::path &path) { * @param fd * @return */ -inline void delete_capio_fd(int fd) { +inline void delete_capio_fd(pid_t fd) { START_LOG(syscall_no_intercept(SYS_gettid), "call(fd=%d)", fd); auto &path = capio_files_descriptors->at(fd); capio_files_paths->at(path).erase(fd); @@ -158,7 +158,7 @@ inline void destroy_filesystem() { * @param newfd * @return */ -inline void dup_capio_fd(long tid, int oldfd, int newfd, bool is_cloexec) { +inline void dup_capio_fd(pid_t tid, int oldfd, int newfd, bool is_cloexec) { const std::string &path = capio_files_descriptors->at(oldfd); capio_files_paths->at(path).insert(newfd); files->insert({newfd, files->at(oldfd)}); @@ -171,7 +171,7 @@ inline void dup_capio_fd(long tid, int oldfd, int newfd, bool is_cloexec) { * @param fd * @return if the file descriptor exists */ -inline bool exists_capio_fd(int fd) { +inline bool exists_capio_fd(pid_t fd) { START_LOG(capio_syscall(SYS_gettid), "call(fd=%d)", fd); return files->find(fd) != files->end(); } diff --git a/src/posix/utils/requests.hpp b/src/posix/utils/requests.hpp index f1b1e068b..65f1c2775 100644 --- a/src/posix/utils/requests.hpp +++ b/src/posix/utils/requests.hpp @@ -1,6 +1,8 @@ #ifndef CAPIO_POSIX_UTILS_REQUESTS_HPP #define CAPIO_POSIX_UTILS_REQUESTS_HPP +#include + #include "capio/requests.hpp" #include "env.hpp" @@ -10,6 +12,8 @@ inline CircularBuffer *buf_requests; inline CPBufResponse_t *bufs_response; +#include "cache.hpp" + /** * Initialize request and response buffers * @return @@ -19,6 +23,11 @@ inline void init_client() { buf_requests = new CircularBuffer(SHM_COMM_CHAN_NAME, CAPIO_REQ_BUFF_CNT, CAPIO_REQ_MAX_SIZE); bufs_response = new CPBufResponse_t(); + + // TODO: use var to set cache size + // TODO: also enable multithreading + write_request_cache = new WriteRequestCache(8192); + read_request_cache = new ReadRequestCache(); } /** @@ -45,6 +54,7 @@ inline void consent_to_proceed_request(const std::filesystem::path &path, const // non blocking inline void close_request(const std::filesystem::path &path, const long tid) { START_LOG(capio_syscall(SYS_gettid), "call(path=%s, tid=%ld)", path.c_str(), tid); + write_request_cache->flush(tid); char req[CAPIO_REQ_MAX_SIZE]; sprintf(req, "%04d %ld %s", CAPIO_REQUEST_CLOSE, tid, path.c_str()); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); @@ -61,6 +71,7 @@ inline void create_request(const int fd, const std::filesystem::path &path, cons // non blocking inline void exit_group_request(const long tid) { START_LOG(capio_syscall(SYS_gettid), "call(tid=%ld)", tid); + write_request_cache->flush(tid); char req[CAPIO_REQ_MAX_SIZE]; sprintf(req, "%04d %ld", CAPIO_REQUEST_EXIT_GROUP, tid); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); @@ -86,6 +97,7 @@ inline void handshake_named_request(const long tid, const long pid, const std::s // block until open is possible inline void open_request(const int fd, const std::filesystem::path &path, const long tid) { START_LOG(capio_syscall(SYS_gettid), "call(fd=%ld, path=%s, tid=%ld)", fd, path.c_str(), tid); + write_request_cache->flush(tid); char req[CAPIO_REQ_MAX_SIZE]; sprintf(req, "%04d %ld %d %s", CAPIO_REQUEST_OPEN, tid, fd, path.c_str()); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); @@ -93,21 +105,6 @@ inline void open_request(const int fd, const std::filesystem::path &path, const bufs_response->at(tid)->read(&res); } -// return amount of readable bytes -inline off64_t read_request(const std::filesystem::path &path, const off64_t end_of_Read, - const long tid, const long fd) { - START_LOG(capio_syscall(SYS_gettid), "call(path=%s, end_of_Read=%ld, tid=%ld, fd=%ld)", - path.c_str(), end_of_Read, tid, fd); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %s %ld %ld %ld", CAPIO_REQUEST_READ, path.c_str(), tid, fd, end_of_Read); - LOG("Sending read request %s", req); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res; - bufs_response->at(tid)->read(&res); - LOG("Response to request is %ld", res); - return res; -} - // non blocking inline void rename_request(const std::filesystem::path &old_path, const std::filesystem::path &new_path, const long tid) { @@ -118,14 +115,4 @@ inline void rename_request(const std::filesystem::path &old_path, buf_requests->write(req, CAPIO_REQ_MAX_SIZE); } -// non blocking as write is not in the pre port of capio semantics -inline void write_request(const std::filesystem::path &path, const off64_t count, const long tid, - const long fd) { - START_LOG(capio_syscall(SYS_gettid), "call(path=%s, count=%ld, tid=%ld)", path.c_str(), count, - tid); - char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %ld %s %ld", CAPIO_REQUEST_WRITE, tid, fd, path.c_str(), count); - buf_requests->write(req, CAPIO_REQ_MAX_SIZE); -} - #endif // CAPIO_POSIX_UTILS_REQUESTS_HPP diff --git a/src/server/cl-engine/src/client_manager.hpp b/src/server/cl-engine/src/client_manager.hpp index 5237fd330..4b6e21e35 100644 --- a/src/server/cl-engine/src/client_manager.hpp +++ b/src/server/cl-engine/src/client_manager.hpp @@ -6,22 +6,22 @@ class ClientManager { CSBufResponse_t *bufs_response; std::unordered_map *app_names; - std::unordered_map *> *thread_awaiting_file_creation; - std::unordered_map *> *thread_awaiting_data; + std::unordered_map *> *thread_awaiting_file_creation; + std::unordered_map *> + *thread_awaiting_data; // TODO: this is an approx. here opnly the creator will commit the file. // TODO: more complex checks needs to be done but this is a temporary fix - std::unordered_map *> *files_to_be_committed_by_tid; + std::unordered_map *> *files_to_be_committed_by_tid; public: ClientManager() { bufs_response = new CSBufResponse_t(); app_names = new std::unordered_map; - thread_awaiting_file_creation = new std::unordered_map *>; + thread_awaiting_file_creation = new std::unordered_map *>; thread_awaiting_data = - new std::unordered_map *>; - - files_to_be_committed_by_tid = new std::unordered_map *>; + new std::unordered_map *>; + files_to_be_committed_by_tid = new std::unordered_map *>; } ~ClientManager() { @@ -39,11 +39,11 @@ class ClientManager { * @param tid * @return */ - inline void register_new_client(long tid, const std::string &app_name) const { + inline void register_new_client(pid_t tid, const std::string &app_name) const { // TODO: replace numbers with constexpr auto *p_buf_response = - new CircularBuffer(SHM_COMM_CHAN_NAME_RESP + std::to_string(tid), - CAPIO_REQ_BUFF_CNT, sizeof(off_t), workflow_name); + new CircularBuffer(SHM_COMM_CHAN_NAME_RESP + std::to_string(tid), + CAPIO_REQ_BUFF_CNT, sizeof(off_t), workflow_name); bufs_response->insert(std::make_pair(tid, p_buf_response)); app_names->emplace(tid, app_name); files_to_be_committed_by_tid->emplace(tid, new std::vector); @@ -54,7 +54,7 @@ class ClientManager { * @param tid * @return */ - inline void remove_client(int tid) { + inline void remove_client(pid_t tid) { auto it_resp = bufs_response->find(tid); if (it_resp != bufs_response->end()) { delete it_resp->second; @@ -69,13 +69,13 @@ class ClientManager { * @param offset * @return */ - inline void reply_to_client(long tid, off64_t offset) { + inline void reply_to_client(pid_t tid, capio_off64_t offset) { START_LOG(gettid(), "call(tid=%ld, offset=%ld)", tid, offset); return bufs_response->at(tid)->write(&offset); } - void add_thread_awaiting_creation(std::string path, int tid) { + void add_thread_awaiting_creation(std::string path, pid_t tid) { if (thread_awaiting_file_creation->find(path) == thread_awaiting_file_creation->end()) { thread_awaiting_file_creation->emplace(path, new std::vector); } @@ -94,7 +94,7 @@ class ClientManager { // register tid to wait for file size of certain size void add_thread_awaiting_data(std::string path, int tid, size_t expected_size) { if (thread_awaiting_data->find(path) == thread_awaiting_data->end()) { - thread_awaiting_data->emplace(path, new std::unordered_map); + thread_awaiting_data->emplace(path, new std::unordered_map); } thread_awaiting_data->at(path)->emplace(tid, expected_size); } @@ -106,7 +106,7 @@ class ClientManager { if (thread_awaiting_data->find(path) != thread_awaiting_data->end()) { LOG("Path has thread awaiting"); auto th = thread_awaiting_data->at(path); - std::vector item_to_delete; + std::vector item_to_delete; for (auto item : *th) { LOG("Handling thread"); @@ -125,11 +125,11 @@ class ClientManager { } } - void add_producer_file_path(int tid, std::string &path) const { + void add_producer_file_path(pid_t tid, std::string &path) const { files_to_be_committed_by_tid->at(tid)->emplace_back(path); } - [[nodiscard]] auto get_produced_files(int tid) const { + [[nodiscard]] auto get_produced_files(pid_t tid) const { return files_to_be_committed_by_tid->at(tid); } }; diff --git a/src/server/cl-engine/src/file_manager.hpp b/src/server/cl-engine/src/file_manager.hpp index 8928c34a4..abb17814b 100644 --- a/src/server/cl-engine/src/file_manager.hpp +++ b/src/server/cl-engine/src/file_manager.hpp @@ -13,7 +13,7 @@ inline void CapioFileManager::set_committed(const std::filesystem::path &path) { client_manager->unlock_thread_awaiting_creation(path); } -inline void CapioFileManager::set_committed(long tid) { +inline void CapioFileManager::set_committed(pid_t tid) { START_LOG(gettid(), "call(tid=%d)", tid); auto files = client_manager->get_produced_files(tid); for (const auto &file : *files) { diff --git a/src/server/cl-engine/src/file_manager_header.hpp b/src/server/cl-engine/src/file_manager_header.hpp index 6a049335c..f4125c9d8 100644 --- a/src/server/cl-engine/src/file_manager_header.hpp +++ b/src/server/cl-engine/src/file_manager_header.hpp @@ -3,8 +3,8 @@ class CapioFileManager { public: - static void set_committed(const std::filesystem::path& path); - static void set_committed(long tid); + static void set_committed(const std::filesystem::path &path); + static void set_committed(pid_t tid); static bool is_committed(const std::filesystem::path &path); }; diff --git a/src/server/cl-engine/src/handlers/close.hpp b/src/server/cl-engine/src/handlers/close.hpp index 85f2c807b..3d415f33e 100644 --- a/src/server/cl-engine/src/handlers/close.hpp +++ b/src/server/cl-engine/src/handlers/close.hpp @@ -3,7 +3,7 @@ #include inline void close_handler(const char *const str) { - int tid; + pid_t tid; char path[PATH_MAX]; sscanf(str, "%d %s", &tid, path); diff --git a/src/server/cl-engine/src/handlers/consent.hpp b/src/server/cl-engine/src/handlers/consent.hpp index e0d7ac8ca..b2020eee9 100644 --- a/src/server/cl-engine/src/handlers/consent.hpp +++ b/src/server/cl-engine/src/handlers/consent.hpp @@ -8,7 +8,7 @@ This handler only checks if the client is allowed to continue */ inline void consent_to_proceed_handler(const char *const str) { - int tid; + pid_t tid; char path[1024]; sscanf(str, "%d %s", &tid, path); START_LOG(gettid(), "call(tid=%d, path=%s)", tid, path); @@ -24,7 +24,7 @@ inline void consent_to_proceed_handler(const char *const str) { } if (!capio_configuration->file_to_be_handled(path_fs)) { - LOG("Ignore calls as fiel should not be treated by CAPIO"); + LOG("Ignore calls as file should not be treated by CAPIO"); client_manager->reply_to_client(tid, 1); return; } diff --git a/src/server/cl-engine/src/handlers/create.hpp b/src/server/cl-engine/src/handlers/create.hpp index 20dabbc89..fa766a2d3 100644 --- a/src/server/cl-engine/src/handlers/create.hpp +++ b/src/server/cl-engine/src/handlers/create.hpp @@ -4,7 +4,7 @@ #include inline void create_handler(const char *const str) { - int tid; + pid_t tid; char path[PATH_MAX]; sscanf(str, "%d %s", &tid, path); START_LOG(gettid(), "call(tid=%d, path=%s)", tid, path); diff --git a/src/server/cl-engine/src/handlers/exit.hpp b/src/server/cl-engine/src/handlers/exit.hpp index 7e39aa669..07469f6e6 100644 --- a/src/server/cl-engine/src/handlers/exit.hpp +++ b/src/server/cl-engine/src/handlers/exit.hpp @@ -4,7 +4,7 @@ inline void exit_handler(const char *const str) { // TODO: register files open for each tid ti register a close - int tid; + pid_t tid; sscanf(str, "%d", &tid); START_LOG(gettid(), "call(tid=%d)", tid); CapioFileManager::set_committed(tid); diff --git a/src/server/cl-engine/src/handlers/handshake.hpp b/src/server/cl-engine/src/handlers/handshake.hpp index 1f645c483..944e1fcfb 100644 --- a/src/server/cl-engine/src/handlers/handshake.hpp +++ b/src/server/cl-engine/src/handlers/handshake.hpp @@ -4,14 +4,14 @@ #include "capio/constants.hpp" inline void handshake_anonymous_handler(const char *const str) { - int tid, pid; + pid_t tid, pid; sscanf(str, "%d %d", &tid, &pid); START_LOG(gettid(), "call(tid=%ld, pid=%ld)", tid, pid); client_manager->register_new_client(tid, CAPIO_DEFAULT_APP_NAME); } inline void handshake_named_handler(const char *const str) { - int tid, pid; + pid_t tid, pid; char app_name[1024]; sscanf(str, "%d %d %s", &tid, &pid, app_name); START_LOG(gettid(), "call(tid=%ld, pid=%ld, app_name=%s)", tid, pid, app_name); diff --git a/src/server/cl-engine/src/handlers/open.hpp b/src/server/cl-engine/src/handlers/open.hpp index 3ece4ca48..684290fd3 100644 --- a/src/server/cl-engine/src/handlers/open.hpp +++ b/src/server/cl-engine/src/handlers/open.hpp @@ -1,9 +1,10 @@ #ifndef OPEN_HPP #define OPEN_HPP inline void open_handler(const char *const str) { - int tid, fd; + pid_t tid; + int fd; char path[PATH_MAX]; - sscanf(str, "%ld %d %s", &tid, &fd, path); + sscanf(str, "%d %d %s", &tid, &fd, path); START_LOG(gettid(), "call(tid=%d, fd=%d, path=%s", tid, fd, path); if (std::filesystem::exists(path)) { diff --git a/src/server/cl-engine/src/handlers/read.hpp b/src/server/cl-engine/src/handlers/read.hpp index 53d92eadb..11925c736 100644 --- a/src/server/cl-engine/src/handlers/read.hpp +++ b/src/server/cl-engine/src/handlers/read.hpp @@ -2,12 +2,13 @@ #define READ_HPP inline void read_handler(const char *const str) { - - long tid, end_of_read, fd; + pid_t tid; + int fd; + capio_off64_t end_of_read; char path[PATH_MAX]; - sscanf(str, "%s %ld %ld %ld", path, &tid, &fd, &end_of_read); - START_LOG(gettid(), "call(path=%s, tid=%ld, count=%ld)", path, tid, end_of_read); + sscanf(str, "%s %d %d %llu", path, &tid, &fd, &end_of_read); + START_LOG(gettid(), "call(path=%s, tid=%ld, end_of_read=%llu)", path, tid, end_of_read); std::filesystem::path path_fs(path); // Skip operations on CAPIO_DIR @@ -24,8 +25,13 @@ inline void read_handler(const char *const str) { return; } - if (std::filesystem::file_size(path) >= end_of_read || CapioFileManager::is_committed(path)) { - client_manager->reply_to_client(tid, 1); + auto is_committed = CapioFileManager::is_committed(path); + auto file_size = std::filesystem::file_size(path); + + // return ULLONG_MAX to signal client cache that file is committed and no more requests are + // required + if (file_size >= end_of_read || is_committed) { + client_manager->reply_to_client(tid, is_committed ? ULLONG_MAX : file_size); } else { client_manager->add_thread_awaiting_data(path, tid, end_of_read); } diff --git a/src/server/cl-engine/src/handlers/rename.hpp b/src/server/cl-engine/src/handlers/rename.hpp index e06465736..6d8bd9aa5 100644 --- a/src/server/cl-engine/src/handlers/rename.hpp +++ b/src/server/cl-engine/src/handlers/rename.hpp @@ -3,7 +3,7 @@ #include inline void rename_handler(const char *const str) { - int tid; + pid_t tid; char old_path[PATH_MAX], new_path[PATH_MAX]; sscanf(str, "%d %s %s", &tid, old_path, new_path); START_LOG(gettid(), "call(tid=%d, old=%s, new=%s)", tid, old_path, new_path); diff --git a/src/server/cl-engine/src/handlers/write.hpp b/src/server/cl-engine/src/handlers/write.hpp index 879d2bae7..55a6f0451 100644 --- a/src/server/cl-engine/src/handlers/write.hpp +++ b/src/server/cl-engine/src/handlers/write.hpp @@ -2,10 +2,13 @@ #define WRITE_HPP inline void write_handler(const char *const str) { - int tid, count, fd; + pid_t tid; + int fd; + capio_off64_t write_size; + char path[PATH_MAX]; - sscanf(str, "%ld %ld %s %ld", &tid, &fd, path, &count); - START_LOG(gettid(), "call(tid=%d, fd=%d, path=%s, count=%d)", tid, fd, path, count); + sscanf(str, "%d %d %s %llu", &tid, &fd, path, &write_size); + START_LOG(gettid(), "call(tid=%d, fd=%d, path=%s, count=%llu)", tid, fd, path, write_size); std::filesystem::path filename(path); if (path == get_capio_dir() || !capio_configuration->file_to_be_handled(filename)) { diff --git a/src/server/cl-engine/src/json_parser.hpp b/src/server/cl-engine/src/json_parser.hpp index 1d2e67864..79d547fad 100644 --- a/src/server/cl-engine/src/json_parser.hpp +++ b/src/server/cl-engine/src/json_parser.hpp @@ -170,8 +170,6 @@ class JsonParser { } LOG("path: %s", path.c_str()); - std::size_t pos = path.native().find('*'); - // TODO: check for globs std::string commit(commit_rule), firerule(mode); if (n_files != -1) { @@ -194,7 +192,6 @@ class JsonParser { } // END OF APP MAIN LOOPS LOG("Completed parsing of io_graph app main loops"); - long int batch_size = 0; if (entries["permanent"].get_array().get(permanent_files)) { // PARSING PERMANENT FILES std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "No permanent section found for workflow: " << workflow_name << std::endl; @@ -216,7 +213,6 @@ class JsonParser { // NOTE: here there was a copy of the previous structured block. // pretty much sure it is a bug, but it might be wanted... - std::size_t pos = path.native().find('*'); // TODO: check for globs locations->setPermanent(name.data(), true); diff --git a/src/server/utils/common.hpp b/src/server/utils/common.hpp index 988c986c0..d66c85bff 100644 --- a/src/server/utils/common.hpp +++ b/src/server/utils/common.hpp @@ -17,24 +17,4 @@ inline bool is_int(const std::string &s) { return res; } -inline int find_batch_size(const std::string &glob, CSMetadataConfGlobs_t &metadata_conf_globs) { - START_LOG(gettid(), "call(%s)", glob.c_str()); - bool found = false; - int nfiles; - std::size_t i = 0; - - while (!found && i < metadata_conf_globs.size()) { - found = glob == std::get<0>(metadata_conf_globs[i]); - ++i; - } - - if (found) { - nfiles = std::get<5>(metadata_conf_globs[i - 1]); - } else { - nfiles = -1; - } - - return nfiles; -} - #endif // CAPIO_SERVER_UTILS_COMMON_HPP diff --git a/src/server/utils/types.hpp b/src/server/utils/types.hpp index 002f958e4..e11af4c58 100644 --- a/src/server/utils/types.hpp +++ b/src/server/utils/types.hpp @@ -25,7 +25,7 @@ typedef std::unordered_map *, Semaphore *>>> CSClientsRemotePendingNFilesMap_t; -typedef std::unordered_map *> CSBufResponse_t; +typedef std::unordered_map *> CSBufResponse_t; typedef CircularBuffer CSBufRequest_t; typedef void (*CSHandler_t)(const char *const); From c9ea4f97dada11780d136c1ef664b43b334c0b80 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Fri, 2 Aug 2024 12:02:17 +0200 Subject: [PATCH 006/151] Filesystem Monitor thread Added file system monitor thread WARNING: no mutually exclusive access is guaranteed and probably race conditions exsists in the FileSystemMonitor class, but it is good enough for now --- src/posix/utils/clone.hpp | 2 +- src/server/cl-engine/cl_engine.hpp | 5 ++ .../cl-engine/src/capio_cl_configuration.hpp | 3 +- src/server/cl-engine/src/client_manager.hpp | 16 ++++++ .../cl-engine/src/file_system_monitor.hpp | 52 +++++++++++++++++++ 5 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 src/server/cl-engine/src/file_system_monitor.hpp diff --git a/src/posix/utils/clone.hpp b/src/posix/utils/clone.hpp index 42cf697cf..3fa764cdc 100644 --- a/src/posix/utils/clone.hpp +++ b/src/posix/utils/clone.hpp @@ -65,7 +65,7 @@ void hook_clone_child() { lock.unlock(); LOG("Starting child thread %d", tid); write_request_cache = new WriteRequestCache(8192); - read_request_cache = new ReadRequestCache(); + read_request_cache = new ReadRequestCache(); } void hook_clone_parent(long child_tid) { diff --git a/src/server/cl-engine/cl_engine.hpp b/src/server/cl-engine/cl_engine.hpp index 25fc1aecc..29c58d843 100644 --- a/src/server/cl-engine/cl_engine.hpp +++ b/src/server/cl-engine/cl_engine.hpp @@ -11,6 +11,8 @@ #include "src/handlers.hpp" +#include "src/file_system_monitor.hpp" + class ClEngine { private: //// Variables @@ -72,6 +74,8 @@ class ClEngine { capio_configuration->print(); + fs_monitor = new FileSystemMonitor(); + std::cout << CAPIO_SERVER_CLI_LOG_SERVER << " CL-Engine initialization completed. ready to listen for incoming requests" << std::endl; @@ -79,6 +83,7 @@ class ClEngine { ~ClEngine() { delete buf_requests; + delete fs_monitor; std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_requests cleanup completed" << std::endl; } diff --git a/src/server/cl-engine/src/capio_cl_configuration.hpp b/src/server/cl-engine/src/capio_cl_configuration.hpp index fa7f95f6c..844e3e392 100644 --- a/src/server/cl-engine/src/capio_cl_configuration.hpp +++ b/src/server/cl-engine/src/capio_cl_configuration.hpp @@ -48,8 +48,7 @@ class CapioCLConfiguration { std::string name_trunc = truncate_last_n(itm.first, 12); auto kind = std::get<5>(itm.second) ? "F" : "D"; - std::cout << "| " << kind << " " - << "| " << name_trunc << std::setfill(' ') + std::cout << "| " << kind << " " << "| " << name_trunc << std::setfill(' ') << std::setw(20 - name_trunc.length()) << "| "; auto producers = std::get<0>(itm.second); diff --git a/src/server/cl-engine/src/client_manager.hpp b/src/server/cl-engine/src/client_manager.hpp index 4b6e21e35..88cb2000b 100644 --- a/src/server/cl-engine/src/client_manager.hpp +++ b/src/server/cl-engine/src/client_manager.hpp @@ -132,6 +132,22 @@ class ClientManager { [[nodiscard]] auto get_produced_files(pid_t tid) const { return files_to_be_committed_by_tid->at(tid); } + + auto get_file_awaiting_creation() { + std::vector keys; + for (auto itm : *thread_awaiting_file_creation) { + keys.emplace_back(itm.first); + } + return keys; + } + + auto get_file_awaiting_data() { + std::vector keys; + for (auto itm : *thread_awaiting_data) { + keys.emplace_back(itm.first); + } + return keys; + } }; inline ClientManager *client_manager; diff --git a/src/server/cl-engine/src/file_system_monitor.hpp b/src/server/cl-engine/src/file_system_monitor.hpp new file mode 100644 index 000000000..79b125a8f --- /dev/null +++ b/src/server/cl-engine/src/file_system_monitor.hpp @@ -0,0 +1,52 @@ +#ifndef CAPIO_FS_FILE_SYSTEM_MONITOR_HPP +#define CAPIO_FS_FILE_SYSTEM_MONITOR_HPP + +#include + +class FileSystemMonitor { + std::thread *th; + + bool *continue_execution = new bool; + + public: + static void _main(const bool *continue_execution) { + timespec sleep{}; + sleep.tv_nsec = 300; // sleep 0.3 seconds + while (*continue_execution) { + + auto files_awaiting_creation = client_manager->get_file_awaiting_creation(); + + for (const auto &file : files_awaiting_creation) { + if (std::filesystem::exists(file)) { + client_manager->unlock_thread_awaiting_creation(file); + } + } + + auto files_awaiting_data = client_manager->get_file_awaiting_data(); + + for (auto file : files_awaiting_data) { + if (std::filesystem::exists(file)) { + client_manager->unlock_thread_awaiting_data(file); + } + } + + nanosleep(&sleep, nullptr); + } + } + + explicit FileSystemMonitor() { + *continue_execution = true; + th = new std::thread(_main, std::ref(continue_execution)); + } + + ~FileSystemMonitor() { + *continue_execution = false; + th->join(); + delete th; + delete continue_execution; + } +}; + +FileSystemMonitor *fs_monitor; + +#endif // CAPIO_FS_FILE_SYSTEM_MONITOR_HPP From 4f209b1aedeb433b3e7b7fe17c589cd6797924ad Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Fri, 2 Aug 2024 19:37:34 +0200 Subject: [PATCH 007/151] Improved read request cache --- src/common/capio/constants.hpp | 7 +---- src/posix/utils/cache.hpp | 39 +++++++++++++++--------- src/posix/utils/clone.hpp | 2 +- src/posix/utils/env.hpp | 9 ++++++ src/posix/utils/requests.hpp | 2 +- src/server/cl-engine/src/json_parser.hpp | 2 +- 6 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/common/capio/constants.hpp b/src/common/capio/constants.hpp index 50100cca2..f429b0c88 100644 --- a/src/common/capio/constants.hpp +++ b/src/common/capio/constants.hpp @@ -29,8 +29,7 @@ constexpr char CAPIO_SHM_CANARY_ERROR[] = // CAPIO communication constants constexpr int CAPIO_REQ_BUFF_CNT = 512; // Max number of elements inside buffers constexpr int CAPIO_CACHE_LINES_DEFAULT = 10; -constexpr int CAPIO_CACHE_LINE_SIZE_DEFAULT = 256 * 1024; -constexpr size_t CAPIO_SERVER_REQUEST_MAX_SIZE = sizeof(char) * (PATH_MAX + 81920); +constexpr int CAPIO_CACHE_LINE_SIZE_DEFAULT = 4096; constexpr size_t CAPIO_REQ_MAX_SIZE = 256 * sizeof(char); constexpr char CAPIO_SERVER_CLI_LOG_SERVER[] = "[ \033[1;32m SERVER \033[0m ] "; constexpr char CAPIO_SERVER_CLI_LOG_SERVER_WARNING[] = "[ \033[1;33m SERVER \033[0m ] "; @@ -40,10 +39,6 @@ constexpr char LOG_CAPIO_END_REQUEST[] = "----------- END SYSCALL constexpr char CAPIO_SERVER_LOG_START_REQUEST_MSG[] = "+++++++++++++++++REQUEST+++++++++++++++++"; constexpr char CAPIO_SERVER_LOG_END_REQUEST_MSG[] = "~~~~~~~~~~~~~~~END REQUEST~~~~~~~~~~~~~~~"; constexpr int CAPIO_LOG_MAX_MSG_LEN = 2048; -constexpr int CAPIO_SEM_RETRIES = 100; -constexpr int THEORETICAL_SIZE_DIRENT64 = sizeof(ino64_t) + sizeof(off64_t) + - sizeof(unsigned short) + sizeof(unsigned char) + - sizeof(char) * NAME_MAX; // CAPIO streaming semantics constexpr char CAPIO_FILE_MODE_NO_UPDATE[] = "no_update"; diff --git a/src/posix/utils/cache.hpp b/src/posix/utils/cache.hpp index 3404de028..b917d9629 100644 --- a/src/posix/utils/cache.hpp +++ b/src/posix/utils/cache.hpp @@ -6,7 +6,7 @@ class WriteRequestCache { int current_fd = -1; long long current_size = 0; - const long long _max_size; + const capio_off64_t _max_size; std::filesystem::path current_path; @@ -21,7 +21,7 @@ class WriteRequestCache { } public: - explicit WriteRequestCache(long long max_size) : _max_size(max_size) {} + explicit WriteRequestCache() : _max_size(get_capio_write_cache_size()) {} ~WriteRequestCache() { this->flush(capio_syscall(SYS_gettid)); } @@ -48,15 +48,15 @@ class WriteRequestCache { }; class ReadRequestCache { - int current_fd = -1; - long long max_read = 0; - bool is_committed = false; + int current_fd = -1; + capio_off64_t max_read = 0; + std::unordered_map *available_read_cache; std::filesystem::path current_path; // return amount of readable bytes - inline off64_t _read_request(const std::filesystem::path &path, const off64_t end_of_Read, - const long tid, const long fd) { + static inline off64_t _read_request(const std::filesystem::path &path, + const off64_t end_of_Read, const long tid, const long fd) { START_LOG(capio_syscall(SYS_gettid), "call(path=%s, end_of_Read=%ld, tid=%ld, fd=%ld)", path.c_str(), end_of_Read, tid, fd); char req[CAPIO_REQ_MAX_SIZE]; @@ -70,27 +70,38 @@ class ReadRequestCache { } public: - explicit ReadRequestCache() = default; + explicit ReadRequestCache() { + available_read_cache = new std::unordered_map; + }; - ~ReadRequestCache() = default; + ~ReadRequestCache() { delete available_read_cache; }; void read_request(std::filesystem::path path, long end_of_read, int tid, int fd) { if (fd != current_fd) { current_path = std::move(path); current_fd = fd; - max_read = 0; - is_committed = false; + + auto entry = available_read_cache->find(path); + if (entry != available_read_cache->end()) { + max_read = entry->second; + } else { + max_read = 0; + available_read_cache->emplace(path, max_read); + } } - if (is_committed) { + // File is committed if server reports its size to be ULLONG_MAX + if (max_read == ULLONG_MAX) { return; } if (end_of_read > max_read) { max_read = _read_request(current_path, end_of_read, tid, fd); - if (max_read == -1) { - is_committed = true; + if (available_read_cache->find(path) == available_read_cache->end()) { + available_read_cache->emplace(path, max_read); + } else { + available_read_cache->at(path) = max_read; } } }; diff --git a/src/posix/utils/clone.hpp b/src/posix/utils/clone.hpp index 3fa764cdc..68460acd3 100644 --- a/src/posix/utils/clone.hpp +++ b/src/posix/utils/clone.hpp @@ -64,7 +64,7 @@ void hook_clone_child() { lock.unlock(); LOG("Starting child thread %d", tid); - write_request_cache = new WriteRequestCache(8192); + write_request_cache = new WriteRequestCache(); read_request_cache = new ReadRequestCache(); } diff --git a/src/posix/utils/env.hpp b/src/posix/utils/env.hpp index 704639c90..0702a5801 100644 --- a/src/posix/utils/env.hpp +++ b/src/posix/utils/env.hpp @@ -15,4 +15,13 @@ inline const char *get_capio_app_name() { return capio_app_name; } +inline capio_off64_t get_capio_write_cache_size() { + static char *cache_size_str = std::getenv("CAPIO_WRITER_CACHE_SIZE"); + + static capio_off64_t cache_size = cache_size_str == nullptr + ? CAPIO_CACHE_LINE_SIZE_DEFAULT + : std::strtoull(cache_size_str, nullptr, 10); + return cache_size; +} + #endif // CAPIO_POSIX_UTILS_ENV_HPP diff --git a/src/posix/utils/requests.hpp b/src/posix/utils/requests.hpp index 65f1c2775..ff04b6276 100644 --- a/src/posix/utils/requests.hpp +++ b/src/posix/utils/requests.hpp @@ -26,7 +26,7 @@ inline void init_client() { // TODO: use var to set cache size // TODO: also enable multithreading - write_request_cache = new WriteRequestCache(8192); + write_request_cache = new WriteRequestCache(); read_request_cache = new ReadRequestCache(); } diff --git a/src/server/cl-engine/src/json_parser.hpp b/src/server/cl-engine/src/json_parser.hpp index 79d547fad..4fc8ffe11 100644 --- a/src/server/cl-engine/src/json_parser.hpp +++ b/src/server/cl-engine/src/json_parser.hpp @@ -82,7 +82,7 @@ class JsonParser { std::filesystem::path file(itm.get_string().take_value()); std::string appname(app_name); locations->newFile(file); - locations->addConsumer(file, appname); + locations->addProducer(file, appname); } std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Completed output_stream parsing for app: " << app_name << std::endl; From 61d23b060bd925893c5ae7db44ba8aab4ed9f76b Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Sat, 10 Aug 2024 17:49:17 +0200 Subject: [PATCH 008/151] Capioctl --- CMakeLists.txt | 1 + src/capioctl/CMakeLists.txt | 72 ++++++++++++++ src/capioctl/capioctl.cpp | 97 +++++++++++++++++++ src/common/capio/queue.hpp | 20 ++-- src/server/capio_server.cpp | 9 +- src/server/cl-engine/cl_engine.hpp | 1 + .../cl-engine/src/file_system_monitor.hpp | 1 + src/server/ctl-engine/capio_ctl.hpp | 49 ++++++++++ src/server/utils/signals.hpp | 19 +--- 9 files changed, 242 insertions(+), 27 deletions(-) create mode 100644 src/capioctl/CMakeLists.txt create mode 100644 src/capioctl/capioctl.cpp create mode 100644 src/server/ctl-engine/capio_ctl.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f0905a03..4cb784934 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,7 @@ ENDIF (CAPIO_LOG AND CMAKE_BUILD_TYPE STREQUAL "Debug") ##################################### add_subdirectory(src/posix) add_subdirectory(src/server) +add_subdirectory(src/capioctl) IF (CAPIO_BUILD_TESTS) message(STATUS "Building CAPIO test suite") diff --git a/src/capioctl/CMakeLists.txt b/src/capioctl/CMakeLists.txt new file mode 100644 index 000000000..ae88810e9 --- /dev/null +++ b/src/capioctl/CMakeLists.txt @@ -0,0 +1,72 @@ +##################################### +# Target information +##################################### +set(TARGET_NAME capioctl) +set(TARGET_INCLUDE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}) +set(TARGET_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/capioctl.cpp +) + +##################################### +# External projects +##################################### +FetchContent_Declare( + args + GIT_REPOSITORY https://github.com/Taywee/args.git + GIT_TAG 6.4.6 +) +FetchContent_Declare( + simdjson + GIT_REPOSITORY https://github.com/simdjson/simdjson.git + GIT_TAG v3.3.0 +) +FetchContent_MakeAvailable(args simdjson) + +##################################### +# Target definition +##################################### +add_executable(${TARGET_NAME} ${TARGET_SOURCES} ${simdjson_SOURCE_DIR}/singleheader/simdjson.cpp) + +##################################### +# Include files and directories +##################################### +file(GLOB_RECURSE CAPIO_SERVER_HEADERS "*.hpp") +target_sources(${TARGET_NAME} PRIVATE + "${CAPIO_COMMON_HEADERS}" + "${CAPIO_SERVER_HEADERS}" +) +target_include_directories(${TARGET_NAME} PRIVATE + ${TARGET_INCLUDE_FOLDER} + ${MPI_INCLUDE_PATH} + ${args_SOURCE_DIR} + ${simdjson_SOURCE_DIR} +) + + +##################################### +# Link libraries +##################################### +target_link_libraries(${TARGET_NAME} PRIVATE ${MPI_LIBRARIES} pthread rt stdc++fs) + +##################################### +# Install rules +##################################### +install(TARGETS ${TARGET_NAME} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +##################################### +# Code coverage +##################################### +IF (ENABLE_COVERAGE) + IF (CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_definitions(${TARGET_NAME} PRIVATE CAPIO_COVERAGE) + target_compile_options(${TARGET_NAME} PRIVATE --coverage -fprofile-arcs -ftest-coverage) + target_link_options(${TARGET_NAME} PRIVATE --coverage -fprofile-arcs -ftest-coverage) + IF (CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") + target_link_libraries(${TARGET_NAME} PRIVATE gcov) + ENDIF (CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") + ELSE (CMAKE_BUILD_TYPE STREQUAL "Debug") + message(WARNING "Code coverage is disabled in release mode.") + ENDIF (CMAKE_BUILD_TYPE STREQUAL "Debug") +ENDIF (ENABLE_COVERAGE) \ No newline at end of file diff --git a/src/capioctl/capioctl.cpp b/src/capioctl/capioctl.cpp new file mode 100644 index 000000000..f0bd9e1f7 --- /dev/null +++ b/src/capioctl/capioctl.cpp @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "args.hxx" + +#include "capio/queue.hpp" + +int main(int argc, char *argv[]) { + + auto workflow_name = get_capio_workflow_name(); + + // check canary of capio server + + if (shm_open(get_capio_workflow_name().c_str(), O_RDONLY, 0) == -1) { + std::cout << "No capio instances found for workflow " << get_capio_workflow_name() + << "! Aborting" << std::endl; + exit(EXIT_FAILURE); + } + + auto tx = new CircularBuffer("RX", CAPIO_REQ_BUFF_CNT, CAPIO_REQ_MAX_SIZE, workflow_name, + false); + + auto rx = new CircularBuffer("TX", CAPIO_REQ_BUFF_CNT, CAPIO_REQ_MAX_SIZE, workflow_name, + false); + + args::ArgumentParser parser("Parser for capioctl"); + args::Group commands(parser, "commands"); + args::Command get(commands, "get", "Retrive informations on object"); + args::Command set(commands, "set", "Configure the CAPIO server"); + + args::ValueFlag apps( + get, "apps", "[ all , ... ]List the currently registered apps with capio", {"apps"}); + args::ValueFlag config( + get, "config", "[ all, ... ] Retrieve the currently loaded CAPIO-CL configuration", + {"config"}); + + args::HelpFlag h(commands, "help", "help", {'h', "help"}); + args::HelpFlag h_get(get, "help", "help", {'h', "help"}); + + try { + parser.ParseCLI(argc, argv); + + if (get) { + if (apps) { + auto option = apps.Get(); + + if (option == "all") { + std::cout << "Fetching all applications registered with server" << std::endl; + } else { + // Fetch for given app + std::cout << "Fetching application registered with server with name " << option + << std::endl; + } + } + + if (config) { + } + } + + } catch (args::Help) { + std::cout << parser; + } catch (args::Error &e) { + std::cerr << e.what() << std::endl << parser; + return 1; + } + + tx->write("CIAO PIPPO"); + + char result[256]; + + rx->read(result); + + std::cout << result << std::endl; + + delete tx; + delete rx; + + return 0; +} \ No newline at end of file diff --git a/src/common/capio/queue.hpp b/src/common/capio/queue.hpp index 912fbfcc2..8a8fcc0d6 100644 --- a/src/common/capio/queue.hpp +++ b/src/common/capio/queue.hpp @@ -18,6 +18,7 @@ template class Queue { long int _buff_size; // buffer size in bytes long int *_first_elem = nullptr, *_last_elem = nullptr; const std::string _shm_name, _first_elem_name, _last_elem_name; + bool require_cleanup; Mutex _mutex; NamedSemaphore _sem_num_elems, _sem_num_empty; @@ -45,18 +46,20 @@ template class Queue { public: Queue(const std::string &shm_name, const long int max_num_elems, const long int elem_size, - const std::string &workflow_name = get_capio_workflow_name()) + const std::string &workflow_name = get_capio_workflow_name(), bool cleanup = true) : _max_num_elems(max_num_elems), _elem_size(elem_size), _buff_size(_max_num_elems * _elem_size), _shm_name(workflow_name + "_" + shm_name), _first_elem_name(workflow_name + SHM_FIRST_ELEM + shm_name), _last_elem_name(workflow_name + SHM_LAST_ELEM + shm_name), _mutex(workflow_name + SHM_MUTEX_PREFIX + shm_name, 1), _sem_num_elems(workflow_name + SHM_SEM_ELEMS + shm_name, 0), - _sem_num_empty(workflow_name + SHM_SEM_EMPTY + shm_name, max_num_elems) { + _sem_num_empty(workflow_name + SHM_SEM_EMPTY + shm_name, max_num_elems), + require_cleanup(cleanup) { START_LOG(capio_syscall(SYS_gettid), "call(shm_name=%s, _max_num_elems=%ld, elem_size=%ld, " - "workflow_name=%s)", - shm_name.data(), max_num_elems, elem_size, workflow_name.data()); + "workflow_name=%s, cleanup=%s)", + shm_name.data(), max_num_elems, elem_size, workflow_name.data(), + cleanup ? "yes" : "no"); _first_elem = (long int *) create_shm(_first_elem_name, sizeof(long int)); _last_elem = (long int *) create_shm(_last_elem_name, sizeof(long int)); @@ -74,9 +77,12 @@ template class Queue { START_LOG(capio_syscall(SYS_gettid), "call(_shm_name=%s, _first_elem_name=%s, _last_elem_name=%s)", _shm_name.c_str(), _first_elem_name.c_str(), _last_elem_name.c_str()); - SHM_DESTROY_CHECK(_shm_name.c_str()); - SHM_DESTROY_CHECK(_first_elem_name.c_str()); - SHM_DESTROY_CHECK(_last_elem_name.c_str()); + if (require_cleanup) { + LOG("Performing cleanup of allocated resources"); + SHM_DESTROY_CHECK(_shm_name.c_str()); + SHM_DESTROY_CHECK(_first_elem_name.c_str()); + SHM_DESTROY_CHECK(_last_elem_name.c_str()); + } } inline T *fetch() { diff --git a/src/server/capio_server.cpp b/src/server/capio_server.cpp index 675b340df..47d9e6203 100644 --- a/src/server/capio_server.cpp +++ b/src/server/capio_server.cpp @@ -25,17 +25,15 @@ char node_name[HOST_NAME_MAX]; #include "utils/types.hpp" -// tid -> (client_to_server_data_buf, server_to_client_data_buf) -CSDataBufferMap_t data_buffers; - #include "capio/env.hpp" #include "capio/logger.hpp" #include "capio/semaphore.hpp" #include "utils/common.hpp" #include "utils/env.hpp" -#include "utils/signals.hpp" #include "cl-engine/cl_engine.hpp" +#include "ctl-engine/capio_ctl.hpp" +#include "utils/signals.hpp" std::string parseCLI(int argc, char **argv) { Logger *log; @@ -182,7 +180,8 @@ int main(int argc, char **argv) { setup_signal_handlers(); - cl_engine = new ClEngine(config_path); + ctl_engine = new CapioCtlEngine(); + cl_engine = new ClEngine(config_path); cl_engine->start(); return 0; diff --git a/src/server/cl-engine/cl_engine.hpp b/src/server/cl-engine/cl_engine.hpp index 29c58d843..7b20a3a84 100644 --- a/src/server/cl-engine/cl_engine.hpp +++ b/src/server/cl-engine/cl_engine.hpp @@ -82,6 +82,7 @@ class ClEngine { } ~ClEngine() { + START_LOG(gettid(), "call()"); delete buf_requests; delete fs_monitor; std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_requests cleanup completed" diff --git a/src/server/cl-engine/src/file_system_monitor.hpp b/src/server/cl-engine/src/file_system_monitor.hpp index 79b125a8f..67f644002 100644 --- a/src/server/cl-engine/src/file_system_monitor.hpp +++ b/src/server/cl-engine/src/file_system_monitor.hpp @@ -41,6 +41,7 @@ class FileSystemMonitor { ~FileSystemMonitor() { *continue_execution = false; + pthread_cancel(th->native_handle()); th->join(); delete th; delete continue_execution; diff --git a/src/server/ctl-engine/capio_ctl.hpp b/src/server/ctl-engine/capio_ctl.hpp new file mode 100644 index 000000000..e8c2ba2c5 --- /dev/null +++ b/src/server/ctl-engine/capio_ctl.hpp @@ -0,0 +1,49 @@ +#ifndef CAPIO_FS_CAPIO_CTL_HPP +#define CAPIO_FS_CAPIO_CTL_HPP + +class CapioCtlEngine { + + std::thread *th; + + bool *continue_execution = new bool; + CSBufRequest_t *readQueue; + CSBufRequest_t *writeQueue; + + static void _main(const bool *continue_execution, CSBufRequest_t *readQueue, + CSBufRequest_t *writeQueue) { + START_LOG(gettid(), "call()"); + + char request[CAPIO_REQ_MAX_SIZE]; + + while (*continue_execution) { + LOG("Reading incoming request"); + readQueue->read(request); + LOG("Recived request %s", request); + + writeQueue->write("CIAO PLUTO"); + } + } + + public: + CapioCtlEngine() { + readQueue = new CSBufRequest_t("RX", CAPIO_REQ_BUFF_CNT, CAPIO_REQ_MAX_SIZE, workflow_name); + writeQueue = + new CSBufRequest_t("TX", CAPIO_REQ_BUFF_CNT, CAPIO_REQ_MAX_SIZE, workflow_name); + + *continue_execution = true; + th = new std::thread(_main, continue_execution, readQueue, writeQueue); + } + + ~CapioCtlEngine() { + *continue_execution = false; + pthread_cancel(th->native_handle()); + th->join(); + delete continue_execution; + delete readQueue; + delete writeQueue; + } +}; + +CapioCtlEngine *ctl_engine; + +#endif // CAPIO_FS_CAPIO_CTL_HPP diff --git a/src/server/utils/signals.hpp b/src/server/utils/signals.hpp index 1286adedf..06594da90 100644 --- a/src/server/utils/signals.hpp +++ b/src/server/utils/signals.hpp @@ -19,28 +19,17 @@ void sig_term_handler(int signum, siginfo_t *info, void *ptr) { std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << "Segfault detected!" << std::endl; } - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "shm cleanup completed" << std::endl; - - for (auto &p : data_buffers) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "Deleting data buffer for " - << p.second.first->get_name() << std::endl; - delete p.second.first; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "Deleting data buffer for " - << p.second.second->get_name() << std::endl; - delete p.second.second; - } - - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "data_buffers cleanup completed" - << std::endl; - #ifdef CAPIO_COVERAGE __gcov_dump(); #endif delete cl_engine; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "cl_engine cleanup completed" << std::endl; + delete ctl_engine; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "ctl_engine cleanup completed" << std::endl; delete shm_canary; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << "shutdown completed" << std::endl; + exit(EXIT_SUCCESS); } From 5627e72693cb2616a62feb9aea3b78b00f3b5a36 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Thu, 5 Sep 2024 10:20:21 +0200 Subject: [PATCH 009/151] Refactor code to reflect choosen architecture --- src/capioctl/capioctl.cpp | 4 +- src/common/capio/constants.hpp | 2 +- src/common/capio/logger.hpp | 1 + .../capio_cl_engine.hpp} | 7 +- .../src => capio-cl-engine}/json_parser.hpp | 4 +- src/server/capio_server.cpp | 23 +-- src/server/cl-engine/src/client_manager.hpp | 155 ------------------ src/server/cl-engine/src/file_manager.hpp | 29 ---- .../cl-engine/src/file_manager_header.hpp | 11 -- .../cl-engine/src/file_system_monitor.hpp | 53 ------ src/server/cl-engine/src/handlers.hpp | 14 -- src/server/client-manager/client_manager.hpp | 79 +++++++++ .../src => client-manager}/handlers/close.hpp | 5 +- .../handlers/consent.hpp | 7 +- .../handlers/create.hpp | 4 +- .../src => client-manager}/handlers/exit.hpp | 3 +- .../handlers/handshake.hpp | 0 .../src => client-manager}/handlers/open.hpp | 2 +- .../src => client-manager}/handlers/read.hpp | 4 +- .../handlers/rename.hpp | 3 +- .../src => client-manager}/handlers/write.hpp | 5 +- .../request_handler_engine.hpp} | 64 ++++---- .../{ctl-engine => ctl-module}/capio_ctl.hpp | 13 +- src/server/file-manager/file_manager.hpp | 39 +++++ src/server/file-manager/file_manager_impl.hpp | 103 ++++++++++++ src/server/file-manager/fs_monitor.hpp | 68 ++++++++ src/server/utils/signals.hpp | 12 +- 27 files changed, 368 insertions(+), 346 deletions(-) rename src/server/{cl-engine/src/capio_cl_configuration.hpp => capio-cl-engine/capio_cl_engine.hpp} (98%) rename src/server/{cl-engine/src => capio-cl-engine}/json_parser.hpp (98%) delete mode 100644 src/server/cl-engine/src/client_manager.hpp delete mode 100644 src/server/cl-engine/src/file_manager.hpp delete mode 100644 src/server/cl-engine/src/file_manager_header.hpp delete mode 100644 src/server/cl-engine/src/file_system_monitor.hpp delete mode 100644 src/server/cl-engine/src/handlers.hpp create mode 100644 src/server/client-manager/client_manager.hpp rename src/server/{cl-engine/src => client-manager}/handlers/close.hpp (66%) rename src/server/{cl-engine/src => client-manager}/handlers/consent.hpp (84%) rename src/server/{cl-engine/src => client-manager}/handlers/create.hpp (74%) rename src/server/{cl-engine/src => client-manager}/handlers/exit.hpp (80%) rename src/server/{cl-engine/src => client-manager}/handlers/handshake.hpp (100%) rename src/server/{cl-engine/src => client-manager}/handlers/open.hpp (85%) rename src/server/{cl-engine/src => client-manager}/handlers/read.hpp (89%) rename src/server/{cl-engine/src => client-manager}/handlers/rename.hpp (78%) rename src/server/{cl-engine/src => client-manager}/handlers/write.hpp (70%) rename src/server/{cl-engine/cl_engine.hpp => client-manager/request_handler_engine.hpp} (74%) rename src/server/{ctl-engine => ctl-module}/capio_ctl.hpp (83%) create mode 100644 src/server/file-manager/file_manager.hpp create mode 100644 src/server/file-manager/file_manager_impl.hpp create mode 100644 src/server/file-manager/fs_monitor.hpp diff --git a/src/capioctl/capioctl.cpp b/src/capioctl/capioctl.cpp index f0bd9e1f7..5dad115b9 100644 --- a/src/capioctl/capioctl.cpp +++ b/src/capioctl/capioctl.cpp @@ -43,11 +43,11 @@ int main(int argc, char *argv[]) { args::ArgumentParser parser("Parser for capioctl"); args::Group commands(parser, "commands"); - args::Command get(commands, "get", "Retrive informations on object"); + args::Command get(commands, "get", "Retrieve information's on object"); args::Command set(commands, "set", "Configure the CAPIO server"); args::ValueFlag apps( - get, "apps", "[ all , ... ]List the currently registered apps with capio", {"apps"}); + get, "apps", "[ all , ... ]List the currently registered apps with CAPIO", {"apps"}); args::ValueFlag config( get, "config", "[ all, ... ] Retrieve the currently loaded CAPIO-CL configuration", {"config"}); diff --git a/src/common/capio/constants.hpp b/src/common/capio/constants.hpp index f429b0c88..6e36ad818 100644 --- a/src/common/capio/constants.hpp +++ b/src/common/capio/constants.hpp @@ -120,7 +120,7 @@ constexpr char CAPIO_LOG_SERVER_CLI_LOGGING_ENABLED_WARNING[] = "|==================================================================|\n"; constexpr char CAPIO_LOG_SERVER_CLI_LOGGING_NOT_AVAILABLE[] = "CAPIO_LOG set but log support was not compiled into CAPIO!"; -constexpr char CAPIO_LOG_SERVER_REQUEST_START[] = "\n+++++++++++ REQUEST +++++++++++"; +constexpr char CAPIO_LOG_SERVER_REQUEST_START[] = "+++++++++++ REQUEST +++++++++++"; constexpr char CAPIO_LOG_SERVER_REQUEST_END[] = "~~~~~~~~~ END REQUEST ~~~~~~~~~\n"; // CAPIO server argument parser diff --git a/src/common/capio/logger.hpp b/src/common/capio/logger.hpp index b171c1409..d7d4a0c18 100644 --- a/src/common/capio/logger.hpp +++ b/src/common/capio/logger.hpp @@ -167,6 +167,7 @@ class Logger { #else if (!logfileOpen) { setup_posix_log_filename(); + current_log_level = 0; // reset after clone log level, so to not inherit it capio_syscall(SYS_mkdir, get_log_dir(), 0755); capio_syscall(SYS_mkdir, get_posix_log_dir(), 0755); diff --git a/src/server/cl-engine/src/capio_cl_configuration.hpp b/src/server/capio-cl-engine/capio_cl_engine.hpp similarity index 98% rename from src/server/cl-engine/src/capio_cl_configuration.hpp rename to src/server/capio-cl-engine/capio_cl_engine.hpp index 844e3e392..89d3d03de 100644 --- a/src/server/cl-engine/src/capio_cl_configuration.hpp +++ b/src/server/capio-cl-engine/capio_cl_engine.hpp @@ -1,7 +1,7 @@ #ifndef CAPIO_ENGINE_HPP #define CAPIO_ENGINE_HPP -class CapioCLConfiguration { +class CapioCLEngine { private: std::unordered_map, // Vector for producers @@ -48,7 +48,8 @@ class CapioCLConfiguration { std::string name_trunc = truncate_last_n(itm.first, 12); auto kind = std::get<5>(itm.second) ? "F" : "D"; - std::cout << "| " << kind << " " << "| " << name_trunc << std::setfill(' ') + std::cout << "| " << kind << " " + << "| " << name_trunc << std::setfill(' ') << std::setw(20 - name_trunc.length()) << "| "; auto producers = std::get<0>(itm.second); @@ -211,5 +212,5 @@ class CapioCLConfiguration { inline auto consumers(const std::string &path) { return std::get<1>(_locations.at(path)); } }; -inline CapioCLConfiguration *capio_configuration; +inline CapioCLEngine *capio_cl_engine; #endif // CAPIO_ENGINE_HPP diff --git a/src/server/cl-engine/src/json_parser.hpp b/src/server/capio-cl-engine/json_parser.hpp similarity index 98% rename from src/server/cl-engine/src/json_parser.hpp rename to src/server/capio-cl-engine/json_parser.hpp index 4fc8ffe11..784f15a20 100644 --- a/src/server/cl-engine/src/json_parser.hpp +++ b/src/server/capio-cl-engine/json_parser.hpp @@ -4,8 +4,8 @@ class JsonParser { public: - static CapioCLConfiguration *parse(const std::filesystem::path &source) { - auto locations = new CapioCLConfiguration(); + static CapioCLEngine *parse(const std::filesystem::path &source) { + auto locations = new CapioCLEngine(); auto capio_dir = get_capio_dir(); START_LOG(gettid(), "call(config_file='%s', capio_dir='%s')", source.c_str(), diff --git a/src/server/capio_server.cpp b/src/server/capio_server.cpp index 47d9e6203..ce1a3fb3e 100644 --- a/src/server/capio_server.cpp +++ b/src/server/capio_server.cpp @@ -31,10 +31,12 @@ char node_name[HOST_NAME_MAX]; #include "utils/common.hpp" #include "utils/env.hpp" -#include "cl-engine/cl_engine.hpp" -#include "ctl-engine/capio_ctl.hpp" +#include "client-manager/request_handler_engine.hpp" +#include "ctl-module/capio_ctl.hpp" #include "utils/signals.hpp" +#include "file-manager/file_manager.hpp" + std::string parseCLI(int argc, char **argv) { Logger *log; @@ -169,20 +171,21 @@ std::string parseCLI(int argc, char **argv) { int main(int argc, char **argv) { std::cout << CAPIO_LOG_SERVER_BANNER; - gethostname(node_name, HOST_NAME_MAX); - const std::string config_path = parseCLI(argc, argv); START_LOG(gettid(), "call()"); - - shm_canary = new CapioShmCanary(workflow_name); - setup_signal_handlers(); - ctl_engine = new CapioCtlEngine(); - cl_engine = new ClEngine(config_path); - cl_engine->start(); + capio_cl_engine = JsonParser::parse(config_path); + shm_canary = new CapioShmCanary(workflow_name); + file_manager = new CapioFileManager(); + fs_monitor = new FileSystemMonitor(); + ctl_module = new CapioCTLModule(); + request_handlers_engine = new RequestHandlerEngine(); + + capio_cl_engine->print(); + request_handlers_engine->start(); return 0; } \ No newline at end of file diff --git a/src/server/cl-engine/src/client_manager.hpp b/src/server/cl-engine/src/client_manager.hpp deleted file mode 100644 index 88cb2000b..000000000 --- a/src/server/cl-engine/src/client_manager.hpp +++ /dev/null @@ -1,155 +0,0 @@ -#ifndef CLIENT_MANAGER_HPP -#define CLIENT_MANAGER_HPP - -class ClientManager { - private: - CSBufResponse_t *bufs_response; - std::unordered_map *app_names; - - std::unordered_map *> *thread_awaiting_file_creation; - std::unordered_map *> - *thread_awaiting_data; - - // TODO: this is an approx. here opnly the creator will commit the file. - // TODO: more complex checks needs to be done but this is a temporary fix - std::unordered_map *> *files_to_be_committed_by_tid; - - public: - ClientManager() { - bufs_response = new CSBufResponse_t(); - app_names = new std::unordered_map; - thread_awaiting_file_creation = new std::unordered_map *>; - thread_awaiting_data = - new std::unordered_map *>; - files_to_be_committed_by_tid = new std::unordered_map *>; - } - - ~ClientManager() { - delete bufs_response; - delete app_names; - delete thread_awaiting_file_creation; - delete thread_awaiting_data; - delete files_to_be_committed_by_tid; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_response cleanup completed" - << std::endl; - } - - /** - * Add a new response buffer for thread @param tid - * @param tid - * @return - */ - inline void register_new_client(pid_t tid, const std::string &app_name) const { - // TODO: replace numbers with constexpr - auto *p_buf_response = - new CircularBuffer(SHM_COMM_CHAN_NAME_RESP + std::to_string(tid), - CAPIO_REQ_BUFF_CNT, sizeof(off_t), workflow_name); - bufs_response->insert(std::make_pair(tid, p_buf_response)); - app_names->emplace(tid, app_name); - files_to_be_committed_by_tid->emplace(tid, new std::vector); - } - - /** - * Delete the response buffer associated with thread @param tid - * @param tid - * @return - */ - inline void remove_client(pid_t tid) { - auto it_resp = bufs_response->find(tid); - if (it_resp != bufs_response->end()) { - delete it_resp->second; - bufs_response->erase(it_resp); - } - files_to_be_committed_by_tid->erase(tid); - } - - /** - * Write offset to response buffer of process @param tid - * @param tid - * @param offset - * @return - */ - inline void reply_to_client(pid_t tid, capio_off64_t offset) { - START_LOG(gettid(), "call(tid=%ld, offset=%ld)", tid, offset); - - return bufs_response->at(tid)->write(&offset); - } - - void add_thread_awaiting_creation(std::string path, pid_t tid) { - if (thread_awaiting_file_creation->find(path) == thread_awaiting_file_creation->end()) { - thread_awaiting_file_creation->emplace(path, new std::vector); - } - thread_awaiting_file_creation->at(path)->emplace_back(tid); - } - - void unlock_thread_awaiting_creation(std::string path) { - if (thread_awaiting_file_creation->find(path) != thread_awaiting_file_creation->end()) { - auto th = thread_awaiting_file_creation->at(path); - for (auto tid : *th) { - reply_to_client(tid, 1); - } - } - } - - // register tid to wait for file size of certain size - void add_thread_awaiting_data(std::string path, int tid, size_t expected_size) { - if (thread_awaiting_data->find(path) == thread_awaiting_data->end()) { - thread_awaiting_data->emplace(path, new std::unordered_map); - } - thread_awaiting_data->at(path)->emplace(tid, expected_size); - } - - void unlock_thread_awaiting_data(std::string path) { - START_LOG(gettid(), "call(path=%s)", path.c_str()); - auto path_size = std::filesystem::file_size(path); - - if (thread_awaiting_data->find(path) != thread_awaiting_data->end()) { - LOG("Path has thread awaiting"); - auto th = thread_awaiting_data->at(path); - std::vector item_to_delete; - - for (auto item : *th) { - LOG("Handling thread"); - if (CapioFileManager::is_committed(path) || - item.second >= std::filesystem::file_size(path)) { - LOG("Thread %ld can be unlocked", item.first); - reply_to_client(item.first, path_size); - item_to_delete.emplace_back(item.first); - } - } - // cleanup of served clients - for (auto itm : item_to_delete) { - LOG("Cleanup of thread %ld", itm); - th->erase(itm); - } - } - } - - void add_producer_file_path(pid_t tid, std::string &path) const { - files_to_be_committed_by_tid->at(tid)->emplace_back(path); - } - - [[nodiscard]] auto get_produced_files(pid_t tid) const { - return files_to_be_committed_by_tid->at(tid); - } - - auto get_file_awaiting_creation() { - std::vector keys; - for (auto itm : *thread_awaiting_file_creation) { - keys.emplace_back(itm.first); - } - return keys; - } - - auto get_file_awaiting_data() { - std::vector keys; - for (auto itm : *thread_awaiting_data) { - keys.emplace_back(itm.first); - } - return keys; - } -}; - -inline ClientManager *client_manager; - -#endif // CLIENT_MANAGER_HPP diff --git a/src/server/cl-engine/src/file_manager.hpp b/src/server/cl-engine/src/file_manager.hpp deleted file mode 100644 index abb17814b..000000000 --- a/src/server/cl-engine/src/file_manager.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef FILE_MANAGER_HPP -#define FILE_MANAGER_HPP -#include "client_manager.hpp" - -inline void CapioFileManager::set_committed(const std::filesystem::path &path) { - START_LOG(gettid(), "call(path=%s)", path.c_str()); - auto metadata_path = path.string() + ".capio"; - LOG("Creating token %s", metadata_path.c_str()); - auto fd = creat(metadata_path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - LOG("Token fd = %d", fd); - close(fd); - client_manager->unlock_thread_awaiting_data(path); - client_manager->unlock_thread_awaiting_creation(path); -} - -inline void CapioFileManager::set_committed(pid_t tid) { - START_LOG(gettid(), "call(tid=%d)", tid); - auto files = client_manager->get_produced_files(tid); - for (const auto &file : *files) { - LOG("Committing file %s", file.c_str()); - CapioFileManager::set_committed(file); - } -} - -inline bool CapioFileManager::is_committed(const std::filesystem::path &path) { - return std::filesystem::exists(path / ".capio"); -} - -#endif // FILE_MANAGER_HPP diff --git a/src/server/cl-engine/src/file_manager_header.hpp b/src/server/cl-engine/src/file_manager_header.hpp deleted file mode 100644 index f4125c9d8..000000000 --- a/src/server/cl-engine/src/file_manager_header.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef FILE_MANAGER_HEADER_HPP -#define FILE_MANAGER_HEADER_HPP - -class CapioFileManager { - public: - static void set_committed(const std::filesystem::path &path); - static void set_committed(pid_t tid); - static bool is_committed(const std::filesystem::path &path); -}; - -#endif // FILE_MANAGER_HEADER_HPP diff --git a/src/server/cl-engine/src/file_system_monitor.hpp b/src/server/cl-engine/src/file_system_monitor.hpp deleted file mode 100644 index 67f644002..000000000 --- a/src/server/cl-engine/src/file_system_monitor.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef CAPIO_FS_FILE_SYSTEM_MONITOR_HPP -#define CAPIO_FS_FILE_SYSTEM_MONITOR_HPP - -#include - -class FileSystemMonitor { - std::thread *th; - - bool *continue_execution = new bool; - - public: - static void _main(const bool *continue_execution) { - timespec sleep{}; - sleep.tv_nsec = 300; // sleep 0.3 seconds - while (*continue_execution) { - - auto files_awaiting_creation = client_manager->get_file_awaiting_creation(); - - for (const auto &file : files_awaiting_creation) { - if (std::filesystem::exists(file)) { - client_manager->unlock_thread_awaiting_creation(file); - } - } - - auto files_awaiting_data = client_manager->get_file_awaiting_data(); - - for (auto file : files_awaiting_data) { - if (std::filesystem::exists(file)) { - client_manager->unlock_thread_awaiting_data(file); - } - } - - nanosleep(&sleep, nullptr); - } - } - - explicit FileSystemMonitor() { - *continue_execution = true; - th = new std::thread(_main, std::ref(continue_execution)); - } - - ~FileSystemMonitor() { - *continue_execution = false; - pthread_cancel(th->native_handle()); - th->join(); - delete th; - delete continue_execution; - } -}; - -FileSystemMonitor *fs_monitor; - -#endif // CAPIO_FS_FILE_SYSTEM_MONITOR_HPP diff --git a/src/server/cl-engine/src/handlers.hpp b/src/server/cl-engine/src/handlers.hpp deleted file mode 100644 index f7c6349d8..000000000 --- a/src/server/cl-engine/src/handlers.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef HANDLERS_HPP -#define HANDLERS_HPP - -#include "handlers/close.hpp" -#include "handlers/consent.hpp" -#include "handlers/create.hpp" -#include "handlers/exit.hpp" -#include "handlers/handshake.hpp" -#include "handlers/open.hpp" -#include "handlers/read.hpp" -#include "handlers/rename.hpp" -#include "handlers/write.hpp" - -#endif // HANDLERS_HPP diff --git a/src/server/client-manager/client_manager.hpp b/src/server/client-manager/client_manager.hpp new file mode 100644 index 000000000..e51822ad9 --- /dev/null +++ b/src/server/client-manager/client_manager.hpp @@ -0,0 +1,79 @@ +#ifndef CLIENT_MANAGER_HPP +#define CLIENT_MANAGER_HPP + +class ClientManager { + CSBufResponse_t *bufs_response; + std::unordered_map *app_names; + + // TODO: this is an approx. here only the creator will commit the file. + // TODO: more complex checks needs to be done but this is a temporary fix + std::unordered_map *> *files_to_be_committed_by_tid; + + public: + ClientManager() { + bufs_response = new CSBufResponse_t(); + app_names = new std::unordered_map; + files_to_be_committed_by_tid = new std::unordered_map *>; + } + + ~ClientManager() { + delete bufs_response; + delete app_names; + delete files_to_be_committed_by_tid; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_response cleanup completed" + << std::endl; + } + + /** + * Add a new response buffer for thread @param tid + * @param tid + * @return + */ + inline void register_new_client(pid_t tid, const std::string &app_name) const { + // TODO: replace numbers with constexpr + auto *p_buf_response = + new CircularBuffer(SHM_COMM_CHAN_NAME_RESP + std::to_string(tid), + CAPIO_REQ_BUFF_CNT, sizeof(off_t), workflow_name); + bufs_response->insert(std::make_pair(tid, p_buf_response)); + app_names->emplace(tid, app_name); + files_to_be_committed_by_tid->emplace(tid, new std::vector); + } + + /** + * Delete the response buffer associated with thread @param tid + * @param tid + * @return + */ + inline void remove_client(pid_t tid) { + auto it_resp = bufs_response->find(tid); + if (it_resp != bufs_response->end()) { + delete it_resp->second; + bufs_response->erase(it_resp); + } + files_to_be_committed_by_tid->erase(tid); + } + + /** + * Write offset to response buffer of process @param tid + * @param tid + * @param offset + * @return + */ + inline void reply_to_client(pid_t tid, capio_off64_t offset) { + START_LOG(gettid(), "call(tid=%ld, offset=%ld)", tid, offset); + + return bufs_response->at(tid)->write(&offset); + } + + void add_producer_file_path(pid_t tid, std::string &path) const { + files_to_be_committed_by_tid->at(tid)->emplace_back(path); + } + + [[nodiscard]] auto get_produced_files(pid_t tid) const { + return files_to_be_committed_by_tid->at(tid); + } +}; + +inline ClientManager *client_manager; + +#endif // CLIENT_MANAGER_HPP diff --git a/src/server/cl-engine/src/handlers/close.hpp b/src/server/client-manager/handlers/close.hpp similarity index 66% rename from src/server/cl-engine/src/handlers/close.hpp rename to src/server/client-manager/handlers/close.hpp index 3d415f33e..0b145c705 100644 --- a/src/server/cl-engine/src/handlers/close.hpp +++ b/src/server/client-manager/handlers/close.hpp @@ -1,6 +1,5 @@ #ifndef CAPIO_CLOSE_HPP #define CAPIO_CLOSE_HPP -#include inline void close_handler(const char *const str) { pid_t tid; @@ -11,11 +10,11 @@ inline void close_handler(const char *const str) { std::filesystem::path filename(path); - if (path == get_capio_dir() || !capio_configuration->file_to_be_handled(filename)) { + if (path == get_capio_dir() || !capio_cl_engine->file_to_be_handled(filename)) { return; } - CapioFileManager::set_committed(path); + file_manager->set_committed(path); } #endif // CAPIO_CLOSE_HPP diff --git a/src/server/cl-engine/src/handlers/consent.hpp b/src/server/client-manager/handlers/consent.hpp similarity index 84% rename from src/server/cl-engine/src/handlers/consent.hpp rename to src/server/client-manager/handlers/consent.hpp index b2020eee9..e7e571d26 100644 --- a/src/server/cl-engine/src/handlers/consent.hpp +++ b/src/server/client-manager/handlers/consent.hpp @@ -1,8 +1,5 @@ #ifndef CONSENT_HPP #define CONSENT_HPP - -#include - /* This handler only checks if the client is allowed to continue */ @@ -23,7 +20,7 @@ inline void consent_to_proceed_handler(const char *const str) { return; } - if (!capio_configuration->file_to_be_handled(path_fs)) { + if (!capio_cl_engine->file_to_be_handled(path_fs)) { LOG("Ignore calls as file should not be treated by CAPIO"); client_manager->reply_to_client(tid, 1); return; @@ -32,7 +29,7 @@ inline void consent_to_proceed_handler(const char *const str) { if (std::filesystem::exists(path) || CapioFileManager::is_committed(path)) { client_manager->reply_to_client(tid, 1); } else { - client_manager->add_thread_awaiting_creation(path, tid); + file_manager->add_thread_awaiting_creation(path, tid); } } diff --git a/src/server/cl-engine/src/handlers/create.hpp b/src/server/client-manager/handlers/create.hpp similarity index 74% rename from src/server/cl-engine/src/handlers/create.hpp rename to src/server/client-manager/handlers/create.hpp index fa766a2d3..40861562e 100644 --- a/src/server/cl-engine/src/handlers/create.hpp +++ b/src/server/client-manager/handlers/create.hpp @@ -1,14 +1,12 @@ #ifndef CAPIO_CREATE_HPP #define CAPIO_CREATE_HPP -#include - inline void create_handler(const char *const str) { pid_t tid; char path[PATH_MAX]; sscanf(str, "%d %s", &tid, path); START_LOG(gettid(), "call(tid=%d, path=%s)", tid, path); - client_manager->unlock_thread_awaiting_creation(path); + file_manager->unlock_thread_awaiting_creation(path); } #endif // CAPIO_CREATE_HPP diff --git a/src/server/cl-engine/src/handlers/exit.hpp b/src/server/client-manager/handlers/exit.hpp similarity index 80% rename from src/server/cl-engine/src/handlers/exit.hpp rename to src/server/client-manager/handlers/exit.hpp index 07469f6e6..365bce71d 100644 --- a/src/server/cl-engine/src/handlers/exit.hpp +++ b/src/server/client-manager/handlers/exit.hpp @@ -1,13 +1,12 @@ #ifndef CAPIO_EXIT_HPP #define CAPIO_EXIT_HPP -#include inline void exit_handler(const char *const str) { // TODO: register files open for each tid ti register a close pid_t tid; sscanf(str, "%d", &tid); START_LOG(gettid(), "call(tid=%d)", tid); - CapioFileManager::set_committed(tid); + file_manager->set_committed(tid); client_manager->remove_client(tid); } diff --git a/src/server/cl-engine/src/handlers/handshake.hpp b/src/server/client-manager/handlers/handshake.hpp similarity index 100% rename from src/server/cl-engine/src/handlers/handshake.hpp rename to src/server/client-manager/handlers/handshake.hpp diff --git a/src/server/cl-engine/src/handlers/open.hpp b/src/server/client-manager/handlers/open.hpp similarity index 85% rename from src/server/cl-engine/src/handlers/open.hpp rename to src/server/client-manager/handlers/open.hpp index 684290fd3..7501a5414 100644 --- a/src/server/cl-engine/src/handlers/open.hpp +++ b/src/server/client-manager/handlers/open.hpp @@ -10,7 +10,7 @@ inline void open_handler(const char *const str) { if (std::filesystem::exists(path)) { client_manager->reply_to_client(tid, 1); } else { - client_manager->add_thread_awaiting_creation(path, tid); + file_manager->add_thread_awaiting_creation(path, tid); } } #endif // OPEN_HPP diff --git a/src/server/cl-engine/src/handlers/read.hpp b/src/server/client-manager/handlers/read.hpp similarity index 89% rename from src/server/cl-engine/src/handlers/read.hpp rename to src/server/client-manager/handlers/read.hpp index 11925c736..8dba63680 100644 --- a/src/server/cl-engine/src/handlers/read.hpp +++ b/src/server/client-manager/handlers/read.hpp @@ -19,7 +19,7 @@ inline void read_handler(const char *const str) { return; } - if (!capio_configuration->file_to_be_handled(path_fs)) { + if (!capio_cl_engine->file_to_be_handled(path_fs)) { LOG("Ignore calls as file should not be treated by CAPIO"); client_manager->reply_to_client(tid, 1); return; @@ -33,7 +33,7 @@ inline void read_handler(const char *const str) { if (file_size >= end_of_read || is_committed) { client_manager->reply_to_client(tid, is_committed ? ULLONG_MAX : file_size); } else { - client_manager->add_thread_awaiting_data(path, tid, end_of_read); + file_manager->add_thread_awaiting_data(path, tid, end_of_read); } } diff --git a/src/server/cl-engine/src/handlers/rename.hpp b/src/server/client-manager/handlers/rename.hpp similarity index 78% rename from src/server/cl-engine/src/handlers/rename.hpp rename to src/server/client-manager/handlers/rename.hpp index 6d8bd9aa5..3131853e2 100644 --- a/src/server/cl-engine/src/handlers/rename.hpp +++ b/src/server/client-manager/handlers/rename.hpp @@ -1,13 +1,12 @@ #ifndef CAPIO_RENAME_HPP #define CAPIO_RENAME_HPP -#include inline void rename_handler(const char *const str) { pid_t tid; char old_path[PATH_MAX], new_path[PATH_MAX]; sscanf(str, "%d %s %s", &tid, old_path, new_path); START_LOG(gettid(), "call(tid=%d, old=%s, new=%s)", tid, old_path, new_path); - client_manager->unlock_thread_awaiting_creation(new_path); + file_manager->unlock_thread_awaiting_creation(new_path); // TODO: gestire le rename? } diff --git a/src/server/cl-engine/src/handlers/write.hpp b/src/server/client-manager/handlers/write.hpp similarity index 70% rename from src/server/cl-engine/src/handlers/write.hpp rename to src/server/client-manager/handlers/write.hpp index 55a6f0451..a38629b89 100644 --- a/src/server/cl-engine/src/handlers/write.hpp +++ b/src/server/client-manager/handlers/write.hpp @@ -1,5 +1,6 @@ #ifndef WRITE_HPP #define WRITE_HPP +#include "capio-cl-engine/capio_cl_engine.hpp" inline void write_handler(const char *const str) { pid_t tid; @@ -11,12 +12,12 @@ inline void write_handler(const char *const str) { START_LOG(gettid(), "call(tid=%d, fd=%d, path=%s, count=%llu)", tid, fd, path, write_size); std::filesystem::path filename(path); - if (path == get_capio_dir() || !capio_configuration->file_to_be_handled(filename)) { + if (path == get_capio_dir() || !capio_cl_engine->file_to_be_handled(filename)) { return; } LOG("File needs to be handled"); - client_manager->unlock_thread_awaiting_data(path); + file_manager->check_and_unlock_thread_awaiting_data(path); } #endif // WRITE_HPP diff --git a/src/server/cl-engine/cl_engine.hpp b/src/server/client-manager/request_handler_engine.hpp similarity index 74% rename from src/server/cl-engine/cl_engine.hpp rename to src/server/client-manager/request_handler_engine.hpp index 7b20a3a84..c80315a10 100644 --- a/src/server/cl-engine/cl_engine.hpp +++ b/src/server/client-manager/request_handler_engine.hpp @@ -1,27 +1,29 @@ #ifndef CAPIO_CL_ENGINE_MAIN_HPP #define CAPIO_CL_ENGINE_MAIN_HPP -#include "src/file_manager_header.hpp" - +#include "capio-cl-engine/capio_cl_engine.hpp" +#include "capio-cl-engine/json_parser.hpp" #include "capio/requests.hpp" -#include "src/capio_cl_configuration.hpp" -#include "src/client_manager.hpp" -#include "src/file_manager.hpp" -#include "src/json_parser.hpp" - -#include "src/handlers.hpp" - -#include "src/file_system_monitor.hpp" - -class ClEngine { - private: - //// Variables - std::array request_handlers; - +#include "client_manager.hpp" +#include "file-manager/file_manager.hpp" + +/* + * REQUESTS handlers + */ +#include "handlers/close.hpp" +#include "handlers/consent.hpp" +#include "handlers/create.hpp" +#include "handlers/exit.hpp" +#include "handlers/handshake.hpp" +#include "handlers/open.hpp" +#include "handlers/read.hpp" +#include "handlers/rename.hpp" +#include "handlers/write.hpp" + +class RequestHandlerEngine { + std::array request_handlers{}; CSBufRequest_t *buf_requests; - //// Class methods - static constexpr std::array build_request_handlers_table() { std::array _request_handlers{0}; @@ -45,7 +47,7 @@ class ClEngine { * @param str * @return request code */ - inline auto read_next_request(char *str) { + inline auto read_next_request(char *str) const { char req[CAPIO_REQ_MAX_SIZE]; buf_requests->read(req); START_LOG(gettid(), "call(req=%s)", req); @@ -62,29 +64,23 @@ class ClEngine { } public: - explicit ClEngine(const std::filesystem::path &json_path) { - START_LOG(gettid(), "call(path=%s)", json_path.c_str()); - - client_manager = new ClientManager(); - capio_configuration = JsonParser::parse(json_path); - request_handlers = build_request_handlers_table(); - - buf_requests = new CSBufRequest_t(SHM_COMM_CHAN_NAME, CAPIO_REQ_BUFF_CNT, - CAPIO_REQ_MAX_SIZE, workflow_name); - - capio_configuration->print(); + explicit RequestHandlerEngine() { + START_LOG(gettid(), "call()"); - fs_monitor = new FileSystemMonitor(); + client_manager = new ClientManager(); + request_handlers = build_request_handlers_table(); + buf_requests = new CSBufRequest_t(SHM_COMM_CHAN_NAME, CAPIO_REQ_BUFF_CNT, + CAPIO_REQ_MAX_SIZE, workflow_name); std::cout << CAPIO_SERVER_CLI_LOG_SERVER << " CL-Engine initialization completed. ready to listen for incoming requests" << std::endl; } - ~ClEngine() { + ~RequestHandlerEngine() { START_LOG(gettid(), "call()"); delete buf_requests; - delete fs_monitor; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_requests cleanup completed" << std::endl; } @@ -108,6 +104,6 @@ class ClEngine { } }; -inline ClEngine *cl_engine; +inline RequestHandlerEngine *request_handlers_engine; #endif // CAPIO_CL_ENGINE_MAIN_HPP diff --git a/src/server/ctl-engine/capio_ctl.hpp b/src/server/ctl-module/capio_ctl.hpp similarity index 83% rename from src/server/ctl-engine/capio_ctl.hpp rename to src/server/ctl-module/capio_ctl.hpp index e8c2ba2c5..8ac598597 100644 --- a/src/server/ctl-engine/capio_ctl.hpp +++ b/src/server/ctl-module/capio_ctl.hpp @@ -1,7 +1,7 @@ #ifndef CAPIO_FS_CAPIO_CTL_HPP #define CAPIO_FS_CAPIO_CTL_HPP -class CapioCtlEngine { +class CapioCTLModule { std::thread *th; @@ -11,21 +11,20 @@ class CapioCtlEngine { static void _main(const bool *continue_execution, CSBufRequest_t *readQueue, CSBufRequest_t *writeQueue) { - START_LOG(gettid(), "call()"); + START_LOG(gettid(), "INFO: instance of FileSystemMonitor"); char request[CAPIO_REQ_MAX_SIZE]; while (*continue_execution) { LOG("Reading incoming request"); readQueue->read(request); - LOG("Recived request %s", request); - + LOG("Received request %s", request); writeQueue->write("CIAO PLUTO"); } } public: - CapioCtlEngine() { + CapioCTLModule() { readQueue = new CSBufRequest_t("RX", CAPIO_REQ_BUFF_CNT, CAPIO_REQ_MAX_SIZE, workflow_name); writeQueue = new CSBufRequest_t("TX", CAPIO_REQ_BUFF_CNT, CAPIO_REQ_MAX_SIZE, workflow_name); @@ -34,7 +33,7 @@ class CapioCtlEngine { th = new std::thread(_main, continue_execution, readQueue, writeQueue); } - ~CapioCtlEngine() { + ~CapioCTLModule() { *continue_execution = false; pthread_cancel(th->native_handle()); th->join(); @@ -44,6 +43,6 @@ class CapioCtlEngine { } }; -CapioCtlEngine *ctl_engine; +inline CapioCTLModule *ctl_module; #endif // CAPIO_FS_CAPIO_CTL_HPP diff --git a/src/server/file-manager/file_manager.hpp b/src/server/file-manager/file_manager.hpp new file mode 100644 index 000000000..4576984d5 --- /dev/null +++ b/src/server/file-manager/file_manager.hpp @@ -0,0 +1,39 @@ +#ifndef FILE_MANAGER_HEADER_HPP +#define FILE_MANAGER_HEADER_HPP + +class CapioFileManager { + private: + std::unordered_map *> *thread_awaiting_file_creation; + std::unordered_map *> + *thread_awaiting_data; + + public: + CapioFileManager() { + thread_awaiting_file_creation = new std::unordered_map *>; + thread_awaiting_data = + new std::unordered_map *>; + } + ~CapioFileManager() { + delete thread_awaiting_file_creation; + delete thread_awaiting_data; + } + + void set_committed(const std::filesystem::path &path) const; + void set_committed(pid_t tid) const; + static bool is_committed(const std::filesystem::path &path); + void check_and_unlock_thread_awaiting_data(std::string path) const; + void add_thread_awaiting_data(std::string path, int tid, size_t expected_size) const; + void unlock_thread_awaiting_creation(std::string path) const; + void add_thread_awaiting_creation(std::string path, pid_t tid) const; + void delete_file_awaiting_creation(std::string path) const; + [[nodiscard]] std::vector get_file_awaiting_creation() const; + [[nodiscard]] std::vector get_file_awaiting_data() const; +}; + +CapioFileManager *file_manager; + +#include "fs_monitor.hpp" + +#include "file_manager_impl.hpp" + +#endif // FILE_MANAGER_HEADER_HPP diff --git a/src/server/file-manager/file_manager_impl.hpp b/src/server/file-manager/file_manager_impl.hpp new file mode 100644 index 000000000..90cfb8fa6 --- /dev/null +++ b/src/server/file-manager/file_manager_impl.hpp @@ -0,0 +1,103 @@ +#ifndef FILE_MANAGER_HPP +#define FILE_MANAGER_HPP +#include "client-manager/client_manager.hpp" + +inline void CapioFileManager::add_thread_awaiting_creation(std::string path, pid_t tid) const { + if (thread_awaiting_file_creation->find(path) == thread_awaiting_file_creation->end()) { + thread_awaiting_file_creation->emplace(path, new std::vector); + } + thread_awaiting_file_creation->at(path)->emplace_back(tid); +} + +inline void CapioFileManager::unlock_thread_awaiting_creation(std::string path) const { + if (thread_awaiting_file_creation->find(path) != thread_awaiting_file_creation->end()) { + auto th = thread_awaiting_file_creation->at(path); + for (auto tid : *th) { + client_manager->reply_to_client(tid, 1); + } + thread_awaiting_file_creation->erase(path); + } +} + +inline void CapioFileManager::delete_file_awaiting_creation(std::string path) const { + thread_awaiting_file_creation->erase(path); +} + +// register tid to wait for file size of certain size +inline void CapioFileManager::add_thread_awaiting_data(std::string path, int tid, + size_t expected_size) const { + if (thread_awaiting_data->find(path) == thread_awaiting_data->end()) { + thread_awaiting_data->emplace(path, new std::unordered_map); + } + thread_awaiting_data->at(path)->emplace(tid, expected_size); +} + +inline void CapioFileManager::check_and_unlock_thread_awaiting_data(std::string path) const { + START_LOG(gettid(), "call(path=%s)", path.c_str()); + auto path_size = std::filesystem::file_size(path); + + if (thread_awaiting_data->find(path) != thread_awaiting_data->end()) { + LOG("Path has thread awaiting"); + auto threads = thread_awaiting_data->at(path); + + for (auto item = threads->begin(); item != threads->end();) { + LOG("Handling thread"); + if (is_committed(path) || item->first >= std::filesystem::file_size(path)) { + LOG("Thread %ld can be unlocked", item->first); + client_manager->reply_to_client(item->first, path_size); + // remove thread from map + LOG("Removing thread %ld from threads awaiting on data", item->first); + item = threads->erase(item); + } else { + ++item; + } + } + + if (threads->empty()) { + LOG("There are no threads waiting for path %s. cleaning up map", path.c_str()); + thread_awaiting_data->erase(path); + } + } +} + +inline void CapioFileManager::set_committed(const std::filesystem::path &path) const { + START_LOG(gettid(), "call(path=%s)", path.c_str()); + auto metadata_path = path.string() + ".capio"; + LOG("Creating token %s", metadata_path.c_str()); + auto fd = creat(metadata_path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + LOG("Token fd = %d", fd); + close(fd); + check_and_unlock_thread_awaiting_data(path); + unlock_thread_awaiting_creation(path); +} + +inline void CapioFileManager::set_committed(pid_t tid) const { + START_LOG(gettid(), "call(tid=%d)", tid); + auto files = client_manager->get_produced_files(tid); + for (const auto &file : *files) { + LOG("Committing file %s", file.c_str()); + CapioFileManager::set_committed(file); + } +} + +inline bool CapioFileManager::is_committed(const std::filesystem::path &path) { + return std::filesystem::exists(path / ".capio"); +} + +inline std::vector CapioFileManager::get_file_awaiting_creation() const { + std::vector keys; + for (auto itm : *thread_awaiting_file_creation) { + keys.emplace_back(itm.first); + } + return keys; +} + +inline std::vector CapioFileManager::get_file_awaiting_data() const { + std::vector keys; + for (auto itm : *thread_awaiting_data) { + keys.emplace_back(itm.first); + } + return keys; +} + +#endif // FILE_MANAGER_HPP diff --git a/src/server/file-manager/fs_monitor.hpp b/src/server/file-manager/fs_monitor.hpp new file mode 100644 index 000000000..a19563cc4 --- /dev/null +++ b/src/server/file-manager/fs_monitor.hpp @@ -0,0 +1,68 @@ +#ifndef CAPIO_FS_FILE_SYSTEM_MONITOR_HPP +#define CAPIO_FS_FILE_SYSTEM_MONITOR_HPP +#include + +class FileSystemMonitor { + std::thread *th; + + bool *continue_execution = new bool; + + public: + static void _main(const bool *continue_execution) { + + START_LOG(gettid(), "INFO: instance of FileSystemMonitor"); + + timespec sleep{}; + sleep.tv_nsec = 300; // sleep 0.3 seconds + while (*continue_execution) { + + /** + * Main idea is to check whether the files exists on the file system. + * Then if they exists, wake both thread waiting for file existence + * and files waiting for data, as the check on the file size (ie. if + * there is enough data) is carried out by the CapioFileManager class + * and not by the file_system monitor component itself + */ + std::vector paths_to_check; + + auto wait_exists = file_manager->get_file_awaiting_creation(); + auto wait_data = file_manager->get_file_awaiting_data(); + + paths_to_check.reserve((wait_data.size() + wait_data.size())); + paths_to_check.insert(paths_to_check.end(), wait_exists.begin(), wait_exists.end()); + paths_to_check.insert(paths_to_check.end(), wait_data.begin(), wait_data.end()); + + for (const auto &file : paths_to_check) { + if (std::filesystem::exists(file)) { + LOG("File %s exists. Unlocking thread awaiting for creation", file.c_str()); + file_manager->unlock_thread_awaiting_creation(file); + file_manager->delete_file_awaiting_creation(file); + LOG("File %s exists. Checking if enough data is available", file.c_str()); + // actual update, end eventual removal from map is handled by the + // CapioFileManager class and not by the FileSystemMonitor class + file_manager->check_and_unlock_thread_awaiting_data(file); + LOG("Completed handling.\n\n"); + } + } + + nanosleep(&sleep, nullptr); + } + } + + explicit FileSystemMonitor() { + *continue_execution = true; + th = new std::thread(_main, std::ref(continue_execution)); + } + + ~FileSystemMonitor() { + *continue_execution = false; + pthread_cancel(th->native_handle()); + th->join(); + delete th; + delete continue_execution; + } +}; + +FileSystemMonitor *fs_monitor; + +#endif // CAPIO_FS_FILE_SYSTEM_MONITOR_HPP diff --git a/src/server/utils/signals.hpp b/src/server/utils/signals.hpp index 06594da90..c22faa82f 100644 --- a/src/server/utils/signals.hpp +++ b/src/server/utils/signals.hpp @@ -1,7 +1,6 @@ #ifndef CAPIO_SERVER_HANDLERS_SIGNALS_HPP #define CAPIO_SERVER_HANDLERS_SIGNALS_HPP -#include #include #ifdef CAPIO_COVERAGE @@ -23,10 +22,13 @@ void sig_term_handler(int signum, siginfo_t *info, void *ptr) { __gcov_dump(); #endif - delete cl_engine; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "cl_engine cleanup completed" << std::endl; - delete ctl_engine; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "ctl_engine cleanup completed" << std::endl; + delete request_handlers_engine; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "request_handlers_engine cleanup completed" + << std::endl; + delete ctl_module; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "ctl_module cleanup completed" << std::endl; + delete fs_monitor; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "fs_monitor cleanup completed" << std::endl; delete shm_canary; std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << "shutdown completed" << std::endl; From 468f583486a0df9b6ce6662baa982bb3b10209c3 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Thu, 5 Sep 2024 15:40:07 +0200 Subject: [PATCH 010/151] JSON parser --- CMakeLists.txt | 1 - src/server/CMakeLists.txt | 11 -- .../capio-cl-engine/capio_cl_engine.hpp | 90 ++++++------- src/server/capio-cl-engine/json_parser.hpp | 120 ++++++++++++++---- src/server/client-manager/client_manager.hpp | 2 + .../client-manager/request_handler_engine.hpp | 3 +- src/server/ctl-module/capio_ctl.hpp | 4 +- src/server/file-manager/file_manager.hpp | 3 +- src/server/file-manager/fs_monitor.hpp | 2 + src/server/utils/common.hpp | 42 ++++++ 10 files changed, 194 insertions(+), 84 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4cb784934..94d307574 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,6 @@ include(GNUInstallDirs) ##################################### # Dependencies ##################################### -find_package(MPI REQUIRED) find_package(Threads REQUIRED) ##################################### diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 4c51715e0..1ef4c7266 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -42,17 +42,6 @@ target_include_directories(${TARGET_NAME} PRIVATE ${simdjson_SOURCE_DIR} ) -##################################### -# Target properties -##################################### -IF (MPI_COMPILE_FLAGS) - set_target_properties(${TARGET_NAME} PROPERTIES COMPILE_FLAGS "${MPI_COMPILE_FLAGS}") -ENDIF (MPI_COMPILE_FLAGS) - -IF (MPI_LINK_FLAGS) - set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS "${MPI_LINK_FLAGS}") -ENDIF (MPI_LINK_FLAGS) - ##################################### # Link libraries ##################################### diff --git a/src/server/capio-cl-engine/capio_cl_engine.hpp b/src/server/capio-cl-engine/capio_cl_engine.hpp index 89d3d03de..ee5a65f5c 100644 --- a/src/server/capio-cl-engine/capio_cl_engine.hpp +++ b/src/server/capio-cl-engine/capio_cl_engine.hpp @@ -1,18 +1,20 @@ #ifndef CAPIO_ENGINE_HPP #define CAPIO_ENGINE_HPP +#include "utils/common.hpp" + class CapioCLEngine { private: std::unordered_map, // Vector for producers - std::vector, // Vector for consumers - std::string, // commit rule - std::string, // fire_rule - bool, // permanent - bool, // exclude - bool, // is_file (if true yes otherwise it is a directory) - int, // commit on file number - long> // directory file count + std::tuple, // Vector for producers [0] + std::vector, // Vector for consumers [1] + std::string, // commit rule [2] + std::string, // fire_rule [3] + bool, // permanent [4] + bool, // exclude [5] + bool, // is_file (if true yes otherwise it is a directory) [6] + int, // commit on file number [7] + long> // directory file count [8] > _locations; @@ -46,7 +48,7 @@ class CapioCLEngine { << std::endl; for (auto itm : _locations) { std::string name_trunc = truncate_last_n(itm.first, 12); - auto kind = std::get<5>(itm.second) ? "F" : "D"; + auto kind = std::get<6>(itm.second) ? "F" : "D"; std::cout << "| " << kind << " " << "| " << name_trunc << std::setfill(' ') @@ -104,20 +106,18 @@ class CapioCLEngine { }; // TODO: might need to be improved - bool file_to_be_handled(std::filesystem::path::iterator::reference path) { + bool file_to_be_handled(std::filesystem::path::iterator::reference path) const { for (const auto &entry : _locations) { auto capio_path = entry.first; if (path == capio_path) { return true; } - if (capio_path.find('*') != std::string::npos) { // check for globs if (capio_path.find(path) == 0) { // if path and capio_path begins in the same way return true; } } } - return false; }; @@ -130,77 +130,79 @@ class CapioCLEngine { inline void newFile(const std::string &path) { if (_locations.find(path) == _locations.end()) { - _locations.emplace( - path, std::make_tuple(std::vector(), std::vector(), - CAPIO_FILE_COMMITTED_ON_TERMINATION, CAPIO_FILE_MODE_UPDATE, - false, false, true, -1, -1)); + std::string commit = CAPIO_FILE_COMMITTED_ON_TERMINATION; + std::string fire = CAPIO_FILE_MODE_UPDATE; + + /* + * Inherit commit and fire rules from LPM directory + * matchSize is used to compute LPM + */ + size_t matchSize = 0; + for (const auto &[filename, data] : _locations) { + if (match_globs(filename, path) && this->isDirectory(filename) && + filename.length() > matchSize) { + matchSize = filename.length(); + commit = this->getCommitRule(filename); + fire = this->getFireRule(filename); + } + } + + _locations.emplace(path, std::make_tuple(std::vector(), + std::vector(), commit, fire, + false, false, true, -1, -1)); } } inline void addProducer(const std::string &path, std::string &producer) { - this->newFile(path); producer.erase(remove_if(producer.begin(), producer.end(), isspace), producer.end()); std::get<0>(_locations.at(path)).emplace_back(producer); } inline void addConsumer(const std::string &path, std::string &consumer) { - this->newFile(path); consumer.erase(remove_if(consumer.begin(), consumer.end(), isspace), consumer.end()); std::get<1>(_locations.at(path)).emplace_back(consumer); } inline void setCommitRule(const std::string &path, const std::string &commit_rule) { - this->newFile(path); std::get<2>(_locations.at(path)) = commit_rule; } - inline auto getCommitRule(const std::string &path) { - this->newFile(path); + inline std::string getCommitRule(const std::string &path) { return std::get<2>(_locations.at(path)); } inline void setFireRule(const std::string &path, const std::string &fire_rule) { - this->newFile(path); std::get<3>(_locations.at(path)) = fire_rule; } - inline auto getFireRule(const std::string &path) { - this->newFile(path); + inline std::string getFireRule(const std::string &path) { return std::get<3>(_locations.at(path)); } inline void setPermanent(const std::string &path, bool value) { - this->newFile(path); - std::get<5>(_locations.at(path)) = value; - } - - inline void setExclude(const std::string &path, bool value) { - this->newFile(path); std::get<4>(_locations.at(path)) = value; } - inline void setDirectory(const std::string &path) { - this->newFile(path); - std::get<5>(_locations.at(path)) = false; + inline void setExclude(const std::string &path, const bool value) { + std::get<5>(_locations.at(path)) = value; } - inline bool isDirectory(const std::string &path) { return !std::get<5>(_locations.at(path)); } + inline void setDirectory(const std::string &path) { std::get<6>(_locations.at(path)) = false; } - inline void setFile(const std::string &path) { - this->newFile(path); - std::get<5>(_locations.at(path)) = true; + inline bool isDirectory(const std::string &path) const { + return !std::get<6>(_locations.at(path)); } - inline bool isFile(const std::string &path) { return std::get<5>(_locations.at(path)); } + inline void setFile(const std::string &path) { std::get<6>(_locations.at(path)) = true; } - inline void setCommitedNumber(const std::string &path, int num) { - this->newFile(path); - std::get<6>(_locations.at(path)) = num; + inline bool isFile(const std::string &path) const { return std::get<6>(_locations.at(path)); } + + inline void setCommitedNumber(const std::string &path, const int num) { + std::get<7>(_locations.at(path)) = num; } inline void setDirectoryFileCount(const std::string &path, long num) { - this->newFile(path); - std::get<7>(_locations.at(path)) = num; + std::get<8>(_locations.at(path)) = num; } inline void remove(const std::string &path) { _locations.erase(path); } diff --git a/src/server/capio-cl-engine/json_parser.hpp b/src/server/capio-cl-engine/json_parser.hpp index 784f15a20..3f03f6284 100644 --- a/src/server/capio-cl-engine/json_parser.hpp +++ b/src/server/capio-cl-engine/json_parser.hpp @@ -1,8 +1,10 @@ #ifndef JSON_PARSER_HPP #define JSON_PARSER_HPP +#include "utils/common.hpp" #include class JsonParser { + public: static CapioCLEngine *parse(const std::filesystem::path &source) { auto locations = new CapioCLEngine(); @@ -19,7 +21,8 @@ class JsonParser { simdjson::ondemand::parser parser; simdjson::padded_string json; simdjson::ondemand::document entries; - simdjson::ondemand::array input_stream, output_stream, streaming, permanent_files; + simdjson::ondemand::array input_stream, output_stream, streaming, permanent_files, + exclude_files; simdjson::error_code error; try { @@ -42,6 +45,8 @@ class JsonParser { << "Parsing configuration for workflow: " << workflow_name << std::endl; LOG("Parsing configuration for workflow: %s", std::string(workflow_name).c_str()); + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << std::endl; + auto io_graph = entries["IO_Graph"]; for (auto app : io_graph) { @@ -62,9 +67,18 @@ class JsonParser { } else { for (auto itm : input_stream) { std::filesystem::path file(itm.get_string().take_value()); - std::string appname(app_name); - locations->newFile(file); - locations->addConsumer(file, appname); + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Found file : " << file + << std::endl; + if (file.is_relative() || first_is_subpath_of_second(file, get_capio_dir())) { + std::string appname(app_name); + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "File : " << file + << " added to app: " << app_name << std::endl; + locations->newFile(file); + locations->addConsumer(file, appname); + } else { + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "File : " << file + << " is not relative to CAPIO_DIR. Ignoring..." << std::endl; + } } std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON @@ -80,9 +94,13 @@ class JsonParser { } else { for (auto itm : output_stream) { std::filesystem::path file(itm.get_string().take_value()); - std::string appname(app_name); - locations->newFile(file); - locations->addProducer(file, appname); + if (file.is_relative() || first_is_subpath_of_second(file, get_capio_dir())) { + std::string appname(app_name); + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Adding file: " << file + << " to app: " << app_name << std::endl; + locations->newFile(file); + locations->addProducer(file, appname); + } } std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Completed output_stream parsing for app: " << app_name << std::endl; @@ -101,17 +119,32 @@ class JsonParser { std::vector streaming_names; long int n_close = -1; long n_files, batch_size; - - simdjson::ondemand::array name = file["name"].get_array(); - if (name.is_empty()) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR - << "error: name for application is mandatory" << std::endl; - ERR_EXIT("error: name for application is mandatory"); + bool is_file = true; + + simdjson::ondemand::array name; + error = file["name"].get_array().get(name); + if (error || name.is_empty()) { + error = file["dirname"].get_array().get(name); + if (error || name.is_empty()) { + std::cout + << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + << "error: either name or dirname in streaming section is required" + << std::endl; + ERR_EXIT( + "error: either name or dirname in streaming section is required"); + } + is_file = false; } + for (auto item : name) { std::string_view elem = item.get_string().value(); - streaming_names.emplace_back(elem); LOG("Found name: %s", std::string(elem).c_str()); + std::filesystem::path file_fs(elem); + if (file_fs.is_relative() || + first_is_subpath_of_second(file_fs, get_capio_dir())) { + LOG("Saving file %s to locations", elem.data()); + streaming_names.emplace_back(elem); + } } // PARSING COMMITTED @@ -173,9 +206,17 @@ class JsonParser { // TODO: check for globs std::string commit(commit_rule), firerule(mode); if (n_files != -1) { - locations->setDirectory(path); + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + << "Setting path: " << path << " to be a directory" + << std::endl; locations->setDirectoryFileCount(path, n_files); + } else { + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + << "Setting path: " << path << " to be a file" << std::endl; + locations->setFile(path); } + + is_file ? locations->setFile(path) : locations->setDirectory(path); locations->setCommitRule(path, commit); locations->setFireRule(path, firerule); locations->setCommitedNumber(path, n_close); @@ -188,9 +229,12 @@ class JsonParser { LOG("completed parsing of streaming section for app: %s", std::string(app_name).c_str()); } // END PARSING STREAMING FILES - + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << std::endl; } // END OF APP MAIN LOOPS LOG("Completed parsing of io_graph app main loops"); + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Completed parsing of io_graph" + << std::endl; + LOG("Completed parsing of io_graph"); if (entries["permanent"].get_array().get(permanent_files)) { // PARSING PERMANENT FILES std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING @@ -210,21 +254,47 @@ class JsonParser { if (path.is_relative()) { path = (capio_dir / path).lexically_normal(); } - // NOTE: here there was a copy of the previous structured block. - // pretty much sure it is a bug, but it might be wanted... - // TODO: check for globs - - locations->setPermanent(name.data(), true); + if (first_is_subpath_of_second(path, get_capio_dir())) { + locations->setPermanent(name.data(), true); + } } std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Completed parsing of permanent files" << std::endl; LOG("Completed parsing of permanent files"); } // END PARSING PERMANENT FILES - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Completed parsing of io_graph" - << std::endl; - LOG("Completed parsing of io_graph"); + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << std::endl; + + if (entries["exclude"].get_array().get(exclude_files)) { // PARSING PERMANENT FILES + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING + << "No exclude section found for workflow: " << workflow_name << std::endl; + LOG("No exclude section found for workflow: %s", std::string(workflow_name).c_str()); + } else { + for (auto file : exclude_files) { + std::string_view name; + error = file.get_string().get(name); + if (error) { + ERR_EXIT("error name for exclude section is mandatory"); + } + LOG("exclude name: %s", std::string(name).c_str()); + + std::filesystem::path path(name); + + if (path.is_relative()) { + path = (capio_dir / path).lexically_normal(); + } + // TODO: check for globs + if (first_is_subpath_of_second(path, get_capio_dir())) { + locations->setExclude(name.data(), true); + } + } + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Completed parsing of exclude files" + << std::endl; + LOG("Completed parsing of exclude files"); + } // END PARSING PERMANENT FILES + + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << std::endl; auto home_node_policies = entries["home_node_policy"].error(); if (!home_node_policies) { @@ -233,6 +303,8 @@ class JsonParser { << std::endl; } + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << std::endl; + return locations; } }; diff --git a/src/server/client-manager/client_manager.hpp b/src/server/client-manager/client_manager.hpp index e51822ad9..08d711a9f 100644 --- a/src/server/client-manager/client_manager.hpp +++ b/src/server/client-manager/client_manager.hpp @@ -14,6 +14,8 @@ class ClientManager { bufs_response = new CSBufResponse_t(); app_names = new std::unordered_map; files_to_be_committed_by_tid = new std::unordered_map *>; + std::cout << CAPIO_SERVER_CLI_LOG_SERVER << "ClientManager initialization completed." + << std::endl; } ~ClientManager() { diff --git a/src/server/client-manager/request_handler_engine.hpp b/src/server/client-manager/request_handler_engine.hpp index c80315a10..efdc8cbcf 100644 --- a/src/server/client-manager/request_handler_engine.hpp +++ b/src/server/client-manager/request_handler_engine.hpp @@ -72,8 +72,7 @@ class RequestHandlerEngine { buf_requests = new CSBufRequest_t(SHM_COMM_CHAN_NAME, CAPIO_REQ_BUFF_CNT, CAPIO_REQ_MAX_SIZE, workflow_name); - std::cout << CAPIO_SERVER_CLI_LOG_SERVER - << " CL-Engine initialization completed. ready to listen for incoming requests" + std::cout << CAPIO_SERVER_CLI_LOG_SERVER << "RequestHandlerEngine initialization completed." << std::endl; } diff --git a/src/server/ctl-module/capio_ctl.hpp b/src/server/ctl-module/capio_ctl.hpp index 8ac598597..5d7f1e75a 100644 --- a/src/server/ctl-module/capio_ctl.hpp +++ b/src/server/ctl-module/capio_ctl.hpp @@ -11,7 +11,7 @@ class CapioCTLModule { static void _main(const bool *continue_execution, CSBufRequest_t *readQueue, CSBufRequest_t *writeQueue) { - START_LOG(gettid(), "INFO: instance of FileSystemMonitor"); + START_LOG(gettid(), "INFO: instance of CapioCTLModule"); char request[CAPIO_REQ_MAX_SIZE]; @@ -31,6 +31,8 @@ class CapioCTLModule { *continue_execution = true; th = new std::thread(_main, continue_execution, readQueue, writeQueue); + std::cout << CAPIO_SERVER_CLI_LOG_SERVER << "CapioCTL initialization completed." + << std::endl; } ~CapioCTLModule() { diff --git a/src/server/file-manager/file_manager.hpp b/src/server/file-manager/file_manager.hpp index 4576984d5..b3a564ff0 100644 --- a/src/server/file-manager/file_manager.hpp +++ b/src/server/file-manager/file_manager.hpp @@ -2,7 +2,6 @@ #define FILE_MANAGER_HEADER_HPP class CapioFileManager { - private: std::unordered_map *> *thread_awaiting_file_creation; std::unordered_map *> *thread_awaiting_data; @@ -12,6 +11,8 @@ class CapioFileManager { thread_awaiting_file_creation = new std::unordered_map *>; thread_awaiting_data = new std::unordered_map *>; + std::cout << CAPIO_SERVER_CLI_LOG_SERVER << "CapioFileManager initialization completed." + << std::endl; } ~CapioFileManager() { delete thread_awaiting_file_creation; diff --git a/src/server/file-manager/fs_monitor.hpp b/src/server/file-manager/fs_monitor.hpp index a19563cc4..356d0d20e 100644 --- a/src/server/file-manager/fs_monitor.hpp +++ b/src/server/file-manager/fs_monitor.hpp @@ -52,6 +52,8 @@ class FileSystemMonitor { explicit FileSystemMonitor() { *continue_execution = true; th = new std::thread(_main, std::ref(continue_execution)); + std::cout << CAPIO_SERVER_CLI_LOG_SERVER + << "CapioFileSystemMonitor initialization completed." << std::endl; } ~FileSystemMonitor() { diff --git a/src/server/utils/common.hpp b/src/server/utils/common.hpp index d66c85bff..fdf017513 100644 --- a/src/server/utils/common.hpp +++ b/src/server/utils/common.hpp @@ -6,6 +6,12 @@ #include "capio/constants.hpp" #include "types.hpp" +bool first_is_subpath_of_second(const std::filesystem::path &path, + const std::filesystem::path &base) { + const auto mismatch_pair = std::mismatch(path.begin(), path.end(), base.begin(), base.end()); + return mismatch_pair.second == base.end(); +} + inline bool is_int(const std::string &s) { START_LOG(gettid(), "call(%s)", s.c_str()); bool res = false; @@ -17,4 +23,40 @@ inline bool is_int(const std::string &s) { return res; } +/** + * check if @param to_match is correct against @param glob + * @param glob path with wildcard + * @param to_match string to check + * @return + */ +inline bool match_globs(std::string glob, std::string to_match) { + bool matches = true; + + if (glob.empty()) { + return false; + } + + auto glob_size = glob.size(), to_match_size = to_match.size(); + int i = 0, j = 0; + + while (i < glob_size && j < to_match_size && matches) { + if (glob[i] == '*') { + if (glob.back() == '*') { + return true; + } + std::string glob_end = glob.substr(glob.find('*') + 1, glob.size()); + return to_match.compare(to_match_size - glob_end.size(), glob_end.size(), glob_end); + } + + if (glob[i] != '?') { + matches = glob[i] == to_match[j]; + } + + i++; + j++; + } + + return matches; +} + #endif // CAPIO_SERVER_UTILS_COMMON_HPP From 188f8a57b1bf9005958fa29aa54e1b211190570b Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Tue, 10 Sep 2024 12:07:23 +0200 Subject: [PATCH 011/151] Improved logging request --- src/common/capio/constants.hpp | 2 +- src/posix/handlers/access.hpp | 4 ++-- src/posix/handlers/chdir.hpp | 2 +- src/posix/handlers/fchmod.hpp | 2 +- src/posix/handlers/fchown.hpp | 2 +- src/posix/handlers/fcntl.hpp | 2 +- src/posix/handlers/fgetxattr.hpp | 2 +- src/posix/handlers/getdents.hpp | 2 +- src/posix/handlers/ioctl.hpp | 2 +- src/posix/handlers/stat.hpp | 4 ++-- src/posix/handlers/statfs.hpp | 2 +- src/posix/handlers/statx.hpp | 2 +- src/posix/utils/requests.hpp | 8 +++++--- src/server/client-manager/handlers/consent.hpp | 6 ++++-- 14 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/common/capio/constants.hpp b/src/common/capio/constants.hpp index 6e36ad818..279f08410 100644 --- a/src/common/capio/constants.hpp +++ b/src/common/capio/constants.hpp @@ -36,7 +36,7 @@ constexpr char CAPIO_SERVER_CLI_LOG_SERVER_WARNING[] = "[ \033[1;33m SERVER \033 constexpr char CAPIO_SERVER_CLI_LOG_SERVER_ERROR[] = "[ \033[1;31m SERVER \033[0m ] "; constexpr char LOG_CAPIO_START_REQUEST[] = "\n+++++++++++ SYSCALL %s (%d) +++++++++++"; constexpr char LOG_CAPIO_END_REQUEST[] = "----------- END SYSCALL ----------\n"; -constexpr char CAPIO_SERVER_LOG_START_REQUEST_MSG[] = "+++++++++++++++++REQUEST+++++++++++++++++"; +constexpr char CAPIO_SERVER_LOG_START_REQUEST_MSG[] = "\n+++++++++++++++++REQUEST+++++++++++++++++"; constexpr char CAPIO_SERVER_LOG_END_REQUEST_MSG[] = "~~~~~~~~~~~~~~~END REQUEST~~~~~~~~~~~~~~~"; constexpr int CAPIO_LOG_MAX_MSG_LEN = 2048; diff --git a/src/posix/handlers/access.hpp b/src/posix/handlers/access.hpp index 566069e77..d1393405c 100644 --- a/src/posix/handlers/access.hpp +++ b/src/posix/handlers/access.hpp @@ -20,7 +20,7 @@ int access_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long a path = capio_posix_realpath(pathname); } - consent_to_proceed_request(path, tid); + consent_to_proceed_request(path, tid, __FUNCTION__); return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } @@ -57,7 +57,7 @@ int faccessat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, lon } } - consent_to_proceed_request(path, tid); + consent_to_proceed_request(path, tid, __FUNCTION__); return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } diff --git a/src/posix/handlers/chdir.hpp b/src/posix/handlers/chdir.hpp index 62eda0b8f..143b188fe 100644 --- a/src/posix/handlers/chdir.hpp +++ b/src/posix/handlers/chdir.hpp @@ -22,7 +22,7 @@ int chdir_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long ar path = capio_posix_realpath(path); } - consent_to_proceed_request(path, tid); + consent_to_proceed_request(path, tid, __FUNCTION__); // if not a capio path, then control is given to kernel return CAPIO_POSIX_SYSCALL_SKIP; diff --git a/src/posix/handlers/fchmod.hpp b/src/posix/handlers/fchmod.hpp index 6981918ca..467d56c8b 100644 --- a/src/posix/handlers/fchmod.hpp +++ b/src/posix/handlers/fchmod.hpp @@ -13,7 +13,7 @@ int fchmod_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long a return CAPIO_POSIX_SYSCALL_SKIP; } - consent_to_proceed_request(get_capio_fd_path(fd), tid); + consent_to_proceed_request(get_capio_fd_path(fd), tid, __FUNCTION__); return CAPIO_POSIX_SYSCALL_SKIP; } diff --git a/src/posix/handlers/fchown.hpp b/src/posix/handlers/fchown.hpp index 0e8955f60..f075a376b 100644 --- a/src/posix/handlers/fchown.hpp +++ b/src/posix/handlers/fchown.hpp @@ -14,7 +14,7 @@ int fchown_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long a return CAPIO_POSIX_SYSCALL_SKIP; } - consent_to_proceed_request(get_capio_fd_path(fd), tid); + consent_to_proceed_request(get_capio_fd_path(fd), tid, __FUNCTION__); return CAPIO_POSIX_SYSCALL_SKIP; } diff --git a/src/posix/handlers/fcntl.hpp b/src/posix/handlers/fcntl.hpp index 3af9a39bf..0631b09ac 100644 --- a/src/posix/handlers/fcntl.hpp +++ b/src/posix/handlers/fcntl.hpp @@ -14,7 +14,7 @@ int fcntl_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long ar START_LOG(tid, "call(fd=%d, cmd=%d, arg=%d)", fd, cmd, arg); if (exists_capio_fd(fd)) { - consent_to_proceed_request(get_capio_fd_path(fd), tid); + consent_to_proceed_request(get_capio_fd_path(fd), tid, __FUNCTION__); } return CAPIO_POSIX_SYSCALL_SKIP; } diff --git a/src/posix/handlers/fgetxattr.hpp b/src/posix/handlers/fgetxattr.hpp index ee5c6d3fc..e0d523817 100644 --- a/src/posix/handlers/fgetxattr.hpp +++ b/src/posix/handlers/fgetxattr.hpp @@ -13,7 +13,7 @@ int fgetxattr_handler(long arg0, long arg1, long arg2, long arg3, long arg4, lon START_LOG(tid, "call(name=%s, value=0x%08x, size=%ld)", name.c_str(), value, size); if (exists_capio_fd(fd)) { - consent_to_proceed_request(get_capio_fd_path(fd), tid); + consent_to_proceed_request(get_capio_fd_path(fd), tid, __FUNCTION__); } return CAPIO_POSIX_SYSCALL_SKIP; } diff --git a/src/posix/handlers/getdents.hpp b/src/posix/handlers/getdents.hpp index 9429084f2..40a9d156b 100644 --- a/src/posix/handlers/getdents.hpp +++ b/src/posix/handlers/getdents.hpp @@ -14,7 +14,7 @@ inline int getdents_handler_impl(long arg0, long arg1, long arg2, long *result, is64bit ? "true" : "false"); if (exists_capio_fd(fd)) { - consent_to_proceed_request(get_capio_fd_path(fd), tid); + consent_to_proceed_request(get_capio_fd_path(fd), tid, __FUNCTION__); } return CAPIO_POSIX_SYSCALL_SKIP; } diff --git a/src/posix/handlers/ioctl.hpp b/src/posix/handlers/ioctl.hpp index 9d36cf0bb..5ea0f1f77 100644 --- a/src/posix/handlers/ioctl.hpp +++ b/src/posix/handlers/ioctl.hpp @@ -10,7 +10,7 @@ int ioctl_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long ar START_LOG(tid, "call(fd=%d, request=%ld)", fd, request, tid); if (exists_capio_fd(fd)) { - consent_to_proceed_request(get_capio_fd_path(fd), tid); + consent_to_proceed_request(get_capio_fd_path(fd), tid, __FUNCTION__); } return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } diff --git a/src/posix/handlers/stat.hpp b/src/posix/handlers/stat.hpp index ad903ae4e..c2a879331 100644 --- a/src/posix/handlers/stat.hpp +++ b/src/posix/handlers/stat.hpp @@ -15,7 +15,7 @@ inline int capio_fstat(int fd, struct stat *statbuf, pid_t tid) { START_LOG(tid, "call(fd=%d, statbuf=0x%08x)", fd, statbuf); if (exists_capio_fd(fd)) { - consent_to_proceed_request(get_capio_fd_path(fd), tid); + consent_to_proceed_request(get_capio_fd_path(fd), tid, __FUNCTION__); } return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } @@ -30,7 +30,7 @@ inline int capio_lstat(const std::string_view &pathname, struct stat *statbuf, p const std::filesystem::path absolute_path(pathname); if (is_capio_path(absolute_path)) { - consent_to_proceed_request(pathname, tid); + consent_to_proceed_request(pathname, tid, __FUNCTION__); } return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } diff --git a/src/posix/handlers/statfs.hpp b/src/posix/handlers/statfs.hpp index 4b12ff0b0..190a39ed1 100644 --- a/src/posix/handlers/statfs.hpp +++ b/src/posix/handlers/statfs.hpp @@ -11,7 +11,7 @@ int fstatfs_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long START_LOG(tid, "call(fd=%d)", fd); if (exists_capio_fd(fd)) { - consent_to_proceed_request(get_capio_fd_path(fd), tid); + consent_to_proceed_request(get_capio_fd_path(fd), tid, __FUNCTION__); } return CAPIO_POSIX_SYSCALL_SKIP; } diff --git a/src/posix/handlers/statx.hpp b/src/posix/handlers/statx.hpp index 1bcc5fc6a..9a1c3e90a 100644 --- a/src/posix/handlers/statx.hpp +++ b/src/posix/handlers/statx.hpp @@ -18,7 +18,7 @@ inline int capio_statx(int dirfd, const std::string_view &pathname, int flags, i std::filesystem::path path(pathname); if (is_capio_path(path)) { - consent_to_proceed_request(path, tid); + consent_to_proceed_request(path, tid, __FUNCTION__); } return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; } diff --git a/src/posix/utils/requests.hpp b/src/posix/utils/requests.hpp index ff04b6276..6b43c029e 100644 --- a/src/posix/utils/requests.hpp +++ b/src/posix/utils/requests.hpp @@ -42,10 +42,12 @@ inline void register_listener(long tid) { } // Block until server allows for proceeding to a generic request -inline void consent_to_proceed_request(const std::filesystem::path &path, const long tid) { - START_LOG(capio_syscall(SYS_gettid), "call(path=%s, tid=%ld)", path.c_str(), tid); +inline void consent_to_proceed_request(const std::filesystem::path &path, const long tid, + std::string source_func) { + START_LOG(capio_syscall(SYS_gettid), "call(path=%s, tid=%ld, source_func=%s)", path.c_str(), + tid, source_func.c_str()); char req[CAPIO_REQ_MAX_SIZE]; - sprintf(req, "%04d %ld %s", CAPIO_REQUEST_CONSENT, tid, path.c_str()); + sprintf(req, "%04d %ld %s %s", CAPIO_REQUEST_CONSENT, tid, path.c_str(), source_func.c_str()); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); off64_t res; bufs_response->at(tid)->read(&res); diff --git a/src/server/client-manager/handlers/consent.hpp b/src/server/client-manager/handlers/consent.hpp index e7e571d26..e8de99193 100644 --- a/src/server/client-manager/handlers/consent.hpp +++ b/src/server/client-manager/handlers/consent.hpp @@ -6,8 +6,8 @@ This handler only checks if the client is allowed to continue inline void consent_to_proceed_handler(const char *const str) { pid_t tid; - char path[1024]; - sscanf(str, "%d %s", &tid, path); + char path[1024], source_func[1024]; + sscanf(str, "%d %s %s", &tid, path, source_func); START_LOG(gettid(), "call(tid=%d, path=%s)", tid, path); std::filesystem::path path_fs(path); @@ -27,8 +27,10 @@ inline void consent_to_proceed_handler(const char *const str) { } if (std::filesystem::exists(path) || CapioFileManager::is_committed(path)) { + LOG("It is possible to unlokc waiting thread"); client_manager->reply_to_client(tid, 1); } else { + LOG("Requested file %s does not exists yet. awaiting for creation", path); file_manager->add_thread_awaiting_creation(path, tid); } } From 1b035b916f124869306e34fe14f8e826737cc942 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Tue, 10 Sep 2024 14:47:23 +0200 Subject: [PATCH 012/151] Varius bugfixes --- CMakeLists.txt | 2 +- src/common/capio/constants.hpp | 6 +- src/posix/libcapio_posix.cpp | 1 + .../capio-cl-engine/capio_cl_engine.hpp | 107 ++++++++++++++---- src/server/capio-cl-engine/json_parser.hpp | 2 +- src/server/client-manager/client_manager.hpp | 11 ++ .../client-manager/handlers/consent.hpp | 5 +- src/server/file-manager/file_manager.hpp | 6 + src/server/file-manager/file_manager_impl.hpp | 32 ++++-- src/server/file-manager/fs_monitor.hpp | 2 + 10 files changed, 135 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 94d307574..cc94b6c01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED TRUE) set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) # Set compiler flags -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -pedantic -O0 -Wno-gnu-zero-variadic-macro-arguments") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -pedantic -O0") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") ##################################### diff --git a/src/common/capio/constants.hpp b/src/common/capio/constants.hpp index 279f08410..5ca94039c 100644 --- a/src/common/capio/constants.hpp +++ b/src/common/capio/constants.hpp @@ -36,9 +36,9 @@ constexpr char CAPIO_SERVER_CLI_LOG_SERVER_WARNING[] = "[ \033[1;33m SERVER \033 constexpr char CAPIO_SERVER_CLI_LOG_SERVER_ERROR[] = "[ \033[1;31m SERVER \033[0m ] "; constexpr char LOG_CAPIO_START_REQUEST[] = "\n+++++++++++ SYSCALL %s (%d) +++++++++++"; constexpr char LOG_CAPIO_END_REQUEST[] = "----------- END SYSCALL ----------\n"; -constexpr char CAPIO_SERVER_LOG_START_REQUEST_MSG[] = "\n+++++++++++++++++REQUEST+++++++++++++++++"; -constexpr char CAPIO_SERVER_LOG_END_REQUEST_MSG[] = "~~~~~~~~~~~~~~~END REQUEST~~~~~~~~~~~~~~~"; -constexpr int CAPIO_LOG_MAX_MSG_LEN = 2048; +constexpr char CAPIO_SERVER_LOG_START_REQUEST_MSG[] = "\n+++++++++++++++++REQUEST+++++++++++++++++"; +constexpr char CAPIO_SERVER_LOG_END_REQUEST_MSG[] = "~~~~~~~~~~~~~~~END REQUEST~~~~~~~~~~~~~~~"; +constexpr int CAPIO_LOG_MAX_MSG_LEN = 2048; // CAPIO streaming semantics constexpr char CAPIO_FILE_MODE_NO_UPDATE[] = "no_update"; diff --git a/src/posix/libcapio_posix.cpp b/src/posix/libcapio_posix.cpp index 251db927c..3b25ba21d 100644 --- a/src/posix/libcapio_posix.cpp +++ b/src/posix/libcapio_posix.cpp @@ -360,6 +360,7 @@ static int hook(long syscall_number, long arg0, long arg1, long arg2, long arg3, return 1; } + LOG("Handling syscall NO %ld (max num is %ld)", syscall_number, CAPIO_NR_SYSCALLS); return syscallTable[syscall_number](arg0, arg1, arg2, arg3, arg4, arg5, result); } diff --git a/src/server/capio-cl-engine/capio_cl_engine.hpp b/src/server/capio-cl-engine/capio_cl_engine.hpp index ee5a65f5c..286ec8dfa 100644 --- a/src/server/capio-cl-engine/capio_cl_engine.hpp +++ b/src/server/capio-cl-engine/capio_cl_engine.hpp @@ -1,6 +1,7 @@ #ifndef CAPIO_ENGINE_HPP #define CAPIO_ENGINE_HPP +#include "client-manager/client_manager.hpp" #include "utils/common.hpp" class CapioCLEngine { @@ -18,7 +19,7 @@ class CapioCLEngine { > _locations; - static inline std::string truncate_last_n(const std::string &str, int n) { + static std::string truncate_last_n(const std::string &str, int n) { return str.length() > n ? "[..] " + str.substr(str.length() - n) : str; } @@ -107,6 +108,7 @@ class CapioCLEngine { // TODO: might need to be improved bool file_to_be_handled(std::filesystem::path::iterator::reference path) const { + START_LOG(gettid(), "call(path=%s)", path.c_str()); for (const auto &entry : _locations) { auto capio_path = entry.first; if (path == capio_path) { @@ -121,14 +123,18 @@ class CapioCLEngine { return false; }; - inline void add(std::string &path, std::vector &producers, - std::vector &consumers, const std::string &commit_rule, - const std::string &fire_rule, bool permanent, bool exclude) { + void add(std::string &path, std::vector &producers, + std::vector &consumers, const std::string &commit_rule, + const std::string &fire_rule, bool permanent, bool exclude) { + START_LOG(gettid(), "call(path=%s, commit=%s, fire=%s, permanent=%s, exclude=%s)", + path.c_str(), commit_rule.c_str(), fire_rule.c_str(), permanent ? "YES" : "NO", + exclude ? "YES" : "NO"); _locations.emplace(path, std::make_tuple(producers, consumers, commit_rule, fire_rule, permanent, exclude, true, -1, -1)); } - inline void newFile(const std::string &path) { + void newFile(const std::string &path) { + START_LOG(gettid(), "call(path=%s)", path.c_str()); if (_locations.find(path) == _locations.end()) { std::string commit = CAPIO_FILE_COMMITTED_ON_TERMINATION; std::string fire = CAPIO_FILE_MODE_UPDATE; @@ -153,66 +159,121 @@ class CapioCLEngine { } } - inline void addProducer(const std::string &path, std::string &producer) { + void addProducer(const std::string &path, std::string &producer) { + START_LOG(gettid(), "call(path=%s, producer=%s)", path.c_str(), producer.c_str()); producer.erase(remove_if(producer.begin(), producer.end(), isspace), producer.end()); std::get<0>(_locations.at(path)).emplace_back(producer); } - inline void addConsumer(const std::string &path, std::string &consumer) { + void addConsumer(const std::string &path, std::string &consumer) { + START_LOG(gettid(), "call(path=%s, consumer=%s)", path.c_str(), consumer.c_str()); consumer.erase(remove_if(consumer.begin(), consumer.end(), isspace), consumer.end()); std::get<1>(_locations.at(path)).emplace_back(consumer); } - inline void setCommitRule(const std::string &path, const std::string &commit_rule) { + void setCommitRule(const std::string &path, const std::string &commit_rule) { + START_LOG(gettid(), "call(path=%s, commit_rule=%s)", path.c_str(), commit_rule.c_str()); std::get<2>(_locations.at(path)) = commit_rule; } - inline std::string getCommitRule(const std::string &path) { + std::string getCommitRule(const std::string &path) { + START_LOG(gettid(), "call(path=%s)", path.c_str()); + LOG("Fire rule: %s", std::get<2>(_locations.at(path)).c_str()); return std::get<2>(_locations.at(path)); } - inline void setFireRule(const std::string &path, const std::string &fire_rule) { + void setFireRule(const std::string &path, const std::string &fire_rule) { + START_LOG(gettid(), "call(path=%s, fire_rule=%s)", path.c_str(), fire_rule.c_str()); std::get<3>(_locations.at(path)) = fire_rule; } - inline std::string getFireRule(const std::string &path) { + std::string getFireRule(const std::string &path) { + START_LOG(gettid(), "call(path=%s)", path.c_str()); return std::get<3>(_locations.at(path)); } - inline void setPermanent(const std::string &path, bool value) { + void setPermanent(const std::string &path, bool value) { + START_LOG(gettid(), "call(path=%s, value=%s)", path.c_str(), value ? "true" : "false"); std::get<4>(_locations.at(path)) = value; } - inline void setExclude(const std::string &path, const bool value) { + void setExclude(const std::string &path, const bool value) { + START_LOG(gettid(), "call(path=%s, value=%s)", path.c_str(), value ? "true" : "false"); std::get<5>(_locations.at(path)) = value; } - inline void setDirectory(const std::string &path) { std::get<6>(_locations.at(path)) = false; } + void setDirectory(const std::string &path) { + START_LOG(gettid(), "call(path=%s)", path.c_str()); + std::get<6>(_locations.at(path)) = false; + } - inline bool isDirectory(const std::string &path) const { + bool isDirectory(const std::string &path) const { + START_LOG(gettid(), "call(path=%s)", path.c_str()); return !std::get<6>(_locations.at(path)); } - inline void setFile(const std::string &path) { std::get<6>(_locations.at(path)) = true; } + void setFile(const std::string &path) { + START_LOG(gettid(), "call(path=%s)", path.c_str()); + std::get<6>(_locations.at(path)) = true; + } - inline bool isFile(const std::string &path) const { return std::get<6>(_locations.at(path)); } + bool isFile(const std::string &path) const { + START_LOG(gettid(), "call(path=%s)", path.c_str()); + return std::get<6>(_locations.at(path)); + } - inline void setCommitedNumber(const std::string &path, const int num) { + void setCommitedNumber(const std::string &path, const int num) { + START_LOG(gettid(), "call(path=%s, num=%ld)", path.c_str(), num); std::get<7>(_locations.at(path)) = num; } - inline void setDirectoryFileCount(const std::string &path, long num) { + void setDirectoryFileCount(const std::string &path, long num) { + START_LOG(gettid(), "call(path=%s, num=%ld)", path.c_str(), num); std::get<8>(_locations.at(path)) = num; } - inline void remove(const std::string &path) { _locations.erase(path); } + void remove(const std::string &path) { + START_LOG(gettid(), "call(path=%s)", path.c_str()); + _locations.erase(path); + } // TODO: return vector - inline auto producers(const std::string &path) { return std::get<0>(_locations.at(path)); } + auto producers(const std::string &path) { + START_LOG(gettid(), "call(path=%s)", path.c_str()); + return std::get<0>(_locations.at(path)); + } // TODO: return vector - inline auto consumers(const std::string &path) { return std::get<1>(_locations.at(path)); } + auto consumers(const std::string &path) { + START_LOG(gettid(), "call(path=%s)", path.c_str()); + return std::get<1>(_locations.at(path)); + } + + bool isProducer(const std::string &path, const pid_t pid) { + START_LOG(gettid(), "call(path=%s, pid=%ld", path.c_str(), pid); + + const auto app_name = client_manager->get_app_name(pid); + LOG("App name for tid %d is %s", pid, app_name.c_str()); + + // check for exact entry + if (_locations.find(path) != _locations.end()) { + LOG("Found exact match for path"); + std::vector producers = std::get<0>(_locations.at(path)); + return std::find(producers.begin(), producers.end(), app_name) != producers.end(); + } + LOG("No exact match found in locations. checking for globs"); + // check for glob + for (const auto &[k, entry] : _locations) { + if (match_globs(k, path)) { + LOG("Found possible glob match"); + std::vector producers = std::get<0>(_locations.at(k)); + return std::find(producers.begin(), producers.end(), app_name) != producers.end(); + } + } + LOG("No match has been found"); + return false; + } }; -inline CapioCLEngine *capio_cl_engine; +CapioCLEngine *capio_cl_engine; #endif // CAPIO_ENGINE_HPP diff --git a/src/server/capio-cl-engine/json_parser.hpp b/src/server/capio-cl-engine/json_parser.hpp index 3f03f6284..efae9cad6 100644 --- a/src/server/capio-cl-engine/json_parser.hpp +++ b/src/server/capio-cl-engine/json_parser.hpp @@ -142,7 +142,7 @@ class JsonParser { std::filesystem::path file_fs(elem); if (file_fs.is_relative() || first_is_subpath_of_second(file_fs, get_capio_dir())) { - LOG("Saving file %s to locations", elem.data()); + LOG("Saving file %s to locations", std::string(elem).c_str()); streaming_names.emplace_back(elem); } } diff --git a/src/server/client-manager/client_manager.hpp b/src/server/client-manager/client_manager.hpp index 08d711a9f..abc707af6 100644 --- a/src/server/client-manager/client_manager.hpp +++ b/src/server/client-manager/client_manager.hpp @@ -11,6 +11,7 @@ class ClientManager { public: ClientManager() { + START_LOG(gettid(), "call()"); bufs_response = new CSBufResponse_t(); app_names = new std::unordered_map; files_to_be_committed_by_tid = new std::unordered_map *>; @@ -19,6 +20,7 @@ class ClientManager { } ~ClientManager() { + START_LOG(gettid(), "call()"); delete bufs_response; delete app_names; delete files_to_be_committed_by_tid; @@ -32,6 +34,7 @@ class ClientManager { * @return */ inline void register_new_client(pid_t tid, const std::string &app_name) const { + START_LOG(gettid(), "call(tid=%ld, app_name=%s)", tid, app_name.c_str()); // TODO: replace numbers with constexpr auto *p_buf_response = new CircularBuffer(SHM_COMM_CHAN_NAME_RESP + std::to_string(tid), @@ -47,6 +50,7 @@ class ClientManager { * @return */ inline void remove_client(pid_t tid) { + START_LOG(gettid(), "call(tid=%ld)", tid); auto it_resp = bufs_response->find(tid); if (it_resp != bufs_response->end()) { delete it_resp->second; @@ -68,12 +72,19 @@ class ClientManager { } void add_producer_file_path(pid_t tid, std::string &path) const { + START_LOG(gettid(), "call(tid=%ld, path=%s)", tid, path.c_str()); files_to_be_committed_by_tid->at(tid)->emplace_back(path); } [[nodiscard]] auto get_produced_files(pid_t tid) const { + START_LOG(gettid(), "call(tid=%ld)", tid); return files_to_be_committed_by_tid->at(tid); } + + std::string get_app_name(pid_t tid) const { + START_LOG(gettid(), "call(tid=%ld)", tid); + return app_names->at(tid); + } }; inline ClientManager *client_manager; diff --git a/src/server/client-manager/handlers/consent.hpp b/src/server/client-manager/handlers/consent.hpp index e8de99193..30f33f097 100644 --- a/src/server/client-manager/handlers/consent.hpp +++ b/src/server/client-manager/handlers/consent.hpp @@ -26,8 +26,9 @@ inline void consent_to_proceed_handler(const char *const str) { return; } - if (std::filesystem::exists(path) || CapioFileManager::is_committed(path)) { - LOG("It is possible to unlokc waiting thread"); + if (std::filesystem::exists(path) || CapioFileManager::is_committed(path) || + capio_cl_engine->isProducer(path, tid)) { + LOG("It is possible to unlock waiting thread"); client_manager->reply_to_client(tid, 1); } else { LOG("Requested file %s does not exists yet. awaiting for creation", path); diff --git a/src/server/file-manager/file_manager.hpp b/src/server/file-manager/file_manager.hpp index b3a564ff0..c344f8ee3 100644 --- a/src/server/file-manager/file_manager.hpp +++ b/src/server/file-manager/file_manager.hpp @@ -1,6 +1,10 @@ #ifndef FILE_MANAGER_HEADER_HPP #define FILE_MANAGER_HEADER_HPP +#include +std::mutex threads_mutex; +std::mutex data_mutex; + class CapioFileManager { std::unordered_map *> *thread_awaiting_file_creation; std::unordered_map *> @@ -8,6 +12,7 @@ class CapioFileManager { public: CapioFileManager() { + START_LOG(gettid(), "call()"); thread_awaiting_file_creation = new std::unordered_map *>; thread_awaiting_data = new std::unordered_map *>; @@ -15,6 +20,7 @@ class CapioFileManager { << std::endl; } ~CapioFileManager() { + START_LOG(gettid(), "call()"); delete thread_awaiting_file_creation; delete thread_awaiting_data; } diff --git a/src/server/file-manager/file_manager_impl.hpp b/src/server/file-manager/file_manager_impl.hpp index 90cfb8fa6..d42af97ed 100644 --- a/src/server/file-manager/file_manager_impl.hpp +++ b/src/server/file-manager/file_manager_impl.hpp @@ -1,41 +1,47 @@ #ifndef FILE_MANAGER_HPP #define FILE_MANAGER_HPP #include "client-manager/client_manager.hpp" +#include "file_manager.hpp" inline void CapioFileManager::add_thread_awaiting_creation(std::string path, pid_t tid) const { - if (thread_awaiting_file_creation->find(path) == thread_awaiting_file_creation->end()) { - thread_awaiting_file_creation->emplace(path, new std::vector); - } + START_LOG(gettid(), "call(path=%s, tid=%ld)", path.c_str(), tid); + std::lock_guard lg(threads_mutex); + thread_awaiting_file_creation->try_emplace(path, new std::vector); thread_awaiting_file_creation->at(path)->emplace_back(tid); } inline void CapioFileManager::unlock_thread_awaiting_creation(std::string path) const { + START_LOG(gettid(), "call(path=%s)", path.c_str()); + std::lock_guard lg(threads_mutex); if (thread_awaiting_file_creation->find(path) != thread_awaiting_file_creation->end()) { auto th = thread_awaiting_file_creation->at(path); for (auto tid : *th) { client_manager->reply_to_client(tid, 1); } - thread_awaiting_file_creation->erase(path); } + thread_awaiting_file_creation->erase(path); } inline void CapioFileManager::delete_file_awaiting_creation(std::string path) const { + START_LOG(gettid(), "call(path=%s)", path.c_str()); + std::lock_guard lg(threads_mutex); thread_awaiting_file_creation->erase(path); } // register tid to wait for file size of certain size inline void CapioFileManager::add_thread_awaiting_data(std::string path, int tid, size_t expected_size) const { - if (thread_awaiting_data->find(path) == thread_awaiting_data->end()) { - thread_awaiting_data->emplace(path, new std::unordered_map); - } + START_LOG(gettid(), "call(path=%s, tid=%ld, expected_size=%ld)", path.c_str(), tid, + expected_size); + std::lock_guard lg(data_mutex); + thread_awaiting_data->try_emplace(path, new std::unordered_map); thread_awaiting_data->at(path)->emplace(tid, expected_size); } inline void CapioFileManager::check_and_unlock_thread_awaiting_data(std::string path) const { START_LOG(gettid(), "call(path=%s)", path.c_str()); auto path_size = std::filesystem::file_size(path); - + std::lock_guard lg(data_mutex); if (thread_awaiting_data->find(path) != thread_awaiting_data->end()) { LOG("Path has thread awaiting"); auto threads = thread_awaiting_data->at(path); @@ -81,10 +87,16 @@ inline void CapioFileManager::set_committed(pid_t tid) const { } inline bool CapioFileManager::is_committed(const std::filesystem::path &path) { - return std::filesystem::exists(path / ".capio"); + START_LOG(gettid(), "call(path=%s)", path.c_str()); + std::string computed_path = path.string() + ".capio"; + LOG("File %s %s committed", path.c_str(), + std::filesystem::exists(computed_path) ? "is" : "is not"); + return std::filesystem::exists(computed_path); } inline std::vector CapioFileManager::get_file_awaiting_creation() const { + // NOTE: do not put inside here log code as it will generate a lot of useless log + std::lock_guard lg(threads_mutex); std::vector keys; for (auto itm : *thread_awaiting_file_creation) { keys.emplace_back(itm.first); @@ -93,6 +105,8 @@ inline std::vector CapioFileManager::get_file_awaiting_creation() c } inline std::vector CapioFileManager::get_file_awaiting_data() const { + // NOTE: do not put inside here log code as it will generate a lot of useless log + std::lock_guard lg(data_mutex); std::vector keys; for (auto itm : *thread_awaiting_data) { keys.emplace_back(itm.first); diff --git a/src/server/file-manager/fs_monitor.hpp b/src/server/file-manager/fs_monitor.hpp index 356d0d20e..a01ad8925 100644 --- a/src/server/file-manager/fs_monitor.hpp +++ b/src/server/file-manager/fs_monitor.hpp @@ -50,6 +50,7 @@ class FileSystemMonitor { } explicit FileSystemMonitor() { + START_LOG(gettid(), "call()"); *continue_execution = true; th = new std::thread(_main, std::ref(continue_execution)); std::cout << CAPIO_SERVER_CLI_LOG_SERVER @@ -57,6 +58,7 @@ class FileSystemMonitor { } ~FileSystemMonitor() { + START_LOG(gettid(), "call()"); *continue_execution = false; pthread_cancel(th->native_handle()); th->join(); From 6af3b46fe039a450c373c04518ecdb72a05575e3 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Wed, 11 Sep 2024 12:18:43 +0200 Subject: [PATCH 013/151] cache --- CMakeLists.txt | 5 +++++ src/posix/handlers/write.hpp | 1 + src/posix/utils/cache.hpp | 7 ++++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cc94b6c01..6abd7fc90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,11 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -pedantic -O0") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") +# Silence warning from clang +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -pedantic -O0 -Wno-gnu-zero-variadic-macro-arguments") +endif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + ##################################### # Options ##################################### diff --git a/src/posix/handlers/write.hpp b/src/posix/handlers/write.hpp index 1dc89a488..622f5f20b 100644 --- a/src/posix/handlers/write.hpp +++ b/src/posix/handlers/write.hpp @@ -10,6 +10,7 @@ inline off64_t capio_write(int fd, capio_off64_t count, pid_t tid) { START_LOG(tid, "call(fd=%d, count=%ld)", fd, count); if (exists_capio_fd(fd)) { + LOG("File needs to be handled"); write_request_cache->write_request(get_capio_fd_path(fd), count, tid, fd); } return CAPIO_POSIX_SYSCALL_REQUEST_SKIP; diff --git a/src/posix/utils/cache.hpp b/src/posix/utils/cache.hpp index b917d9629..614d0cfff 100644 --- a/src/posix/utils/cache.hpp +++ b/src/posix/utils/cache.hpp @@ -26,8 +26,10 @@ class WriteRequestCache { ~WriteRequestCache() { this->flush(capio_syscall(SYS_gettid)); } void write_request(std::filesystem::path path, int tid, int fd, long count) { - + START_LOG(capio_syscall(SYS_gettid), "call(path=%s, tid=%ld, fd=%ld, count=%ld)", + path.c_str(), tid, fd, count); if (fd != current_fd) { + LOG("File descriptor changed from previous state. updating"); this->flush(tid); current_path = std::move(path); current_fd = fd; @@ -35,12 +37,15 @@ class WriteRequestCache { current_size += count; if (current_size > _max_size) { + LOG("exceeded maximum cache size. flushing..."); this->flush(tid); } }; void flush(int tid) { + START_LOG(capio_syscall(SYS_gettid), "call(tid=%ld)", tid); if (current_fd != -1 && current_size > 0) { + LOG("Performing write to SHM"); _write_request(tid, current_fd, current_size); } current_size = 0; From a1558ab8f1ef87345748b6e1fdf18f858a0b2d4b Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Wed, 11 Sep 2024 12:59:45 +0200 Subject: [PATCH 014/151] File System Monitor fixes --- src/posix/utils/filesystem.hpp | 11 +++++++++- src/server/file-manager/file_manager.hpp | 2 +- src/server/file-manager/file_manager_impl.hpp | 20 ++++++++++++++++--- src/server/file-manager/fs_monitor.hpp | 20 ++++++++++--------- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/posix/utils/filesystem.hpp b/src/posix/utils/filesystem.hpp index a28367dc3..cb21bdce1 100644 --- a/src/posix/utils/filesystem.hpp +++ b/src/posix/utils/filesystem.hpp @@ -106,7 +106,15 @@ std::filesystem::path capio_posix_realpath(const std::filesystem::path &pathname * @return */ inline std::filesystem::path capio_absolute(const std::filesystem::path &path) { - return path.is_absolute() ? path : capio_posix_realpath(path); + START_LOG(capio_syscall(SYS_gettid), "call(path=%s)", path.c_str()); + if (!path.is_absolute()) { + LOG("PATH is not absolute"); + auto absolute = capio_posix_realpath(path); + LOG("Computed absolute path is %s", absolute.c_str()); + return absolute; + } + LOG("Path is absolute"); + return path; } /** @@ -246,6 +254,7 @@ std::filesystem::path get_dir_path(int dirfd) { LOG("dirfd %d points to path %s", dirfd, it->second.c_str()); return it->second; } else { + LOG("dirfd %d not found. Computing it trough proclnk", dirfd); char proclnk[128]; char dir_pathname[PATH_MAX]; sprintf(proclnk, "/proc/self/fd/%d", dirfd); diff --git a/src/server/file-manager/file_manager.hpp b/src/server/file-manager/file_manager.hpp index c344f8ee3..83ab2c1ba 100644 --- a/src/server/file-manager/file_manager.hpp +++ b/src/server/file-manager/file_manager.hpp @@ -28,7 +28,7 @@ class CapioFileManager { void set_committed(const std::filesystem::path &path) const; void set_committed(pid_t tid) const; static bool is_committed(const std::filesystem::path &path); - void check_and_unlock_thread_awaiting_data(std::string path) const; + void check_and_unlock_thread_awaiting_data(const std::string& path) const; void add_thread_awaiting_data(std::string path, int tid, size_t expected_size) const; void unlock_thread_awaiting_creation(std::string path) const; void add_thread_awaiting_creation(std::string path, pid_t tid) const; diff --git a/src/server/file-manager/file_manager_impl.hpp b/src/server/file-manager/file_manager_impl.hpp index d42af97ed..4e9cb6588 100644 --- a/src/server/file-manager/file_manager_impl.hpp +++ b/src/server/file-manager/file_manager_impl.hpp @@ -38,10 +38,11 @@ inline void CapioFileManager::add_thread_awaiting_data(std::string path, int tid thread_awaiting_data->at(path)->emplace(tid, expected_size); } -inline void CapioFileManager::check_and_unlock_thread_awaiting_data(std::string path) const { +inline void CapioFileManager::check_and_unlock_thread_awaiting_data(const std::string &path) const { START_LOG(gettid(), "call(path=%s)", path.c_str()); - auto path_size = std::filesystem::file_size(path); + LOG("Before lockguard"); std::lock_guard lg(data_mutex); + LOG("Acquired lockguard"); if (thread_awaiting_data->find(path) != thread_awaiting_data->end()) { LOG("Path has thread awaiting"); auto threads = thread_awaiting_data->at(path); @@ -50,19 +51,32 @@ inline void CapioFileManager::check_and_unlock_thread_awaiting_data(std::string LOG("Handling thread"); if (is_committed(path) || item->first >= std::filesystem::file_size(path)) { LOG("Thread %ld can be unlocked", item->first); - client_manager->reply_to_client(item->first, path_size); + /* + * Check for file size only if it is directory, otherwise, + * return the max allowed size, to allow the process to continue. + * This is caused by the fact that std::filesystem::file_size is + * implementation defined when invoked on directories + */ + client_manager->reply_to_client(item->first, + std::filesystem::is_directory(path) + ? ULLONG_MAX + : std::filesystem::file_size(path)); // remove thread from map LOG("Removing thread %ld from threads awaiting on data", item->first); item = threads->erase(item); } else { + LOG("Waiting threads cannot yet be unlocked"); ++item; } } + LOG("Completed loops over threads vector for file!"); + if (threads->empty()) { LOG("There are no threads waiting for path %s. cleaning up map", path.c_str()); thread_awaiting_data->erase(path); } + LOG("Completed checks"); } } diff --git a/src/server/file-manager/fs_monitor.hpp b/src/server/file-manager/fs_monitor.hpp index a01ad8925..89b6ac602 100644 --- a/src/server/file-manager/fs_monitor.hpp +++ b/src/server/file-manager/fs_monitor.hpp @@ -23,16 +23,8 @@ class FileSystemMonitor { * there is enough data) is carried out by the CapioFileManager class * and not by the file_system monitor component itself */ - std::vector paths_to_check; - auto wait_exists = file_manager->get_file_awaiting_creation(); - auto wait_data = file_manager->get_file_awaiting_data(); - - paths_to_check.reserve((wait_data.size() + wait_data.size())); - paths_to_check.insert(paths_to_check.end(), wait_exists.begin(), wait_exists.end()); - paths_to_check.insert(paths_to_check.end(), wait_data.begin(), wait_data.end()); - - for (const auto &file : paths_to_check) { + for (const auto &file : file_manager->get_file_awaiting_creation()) { if (std::filesystem::exists(file)) { LOG("File %s exists. Unlocking thread awaiting for creation", file.c_str()); file_manager->unlock_thread_awaiting_creation(file); @@ -45,6 +37,16 @@ class FileSystemMonitor { } } + for (const auto &file : file_manager->get_file_awaiting_data()) { + if (std::filesystem::exists(file)) { + LOG("File %s exists. Checking if enough data is available", file.c_str()); + // actual update, end eventual removal from map is handled by the + // CapioFileManager class and not by the FileSystemMonitor class + file_manager->check_and_unlock_thread_awaiting_data(file); + LOG("Completed handling.\n\n"); + } + } + nanosleep(&sleep, nullptr); } } From 59b88b1b72ad2424b73acf78384d631eb99d9aaf Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Wed, 11 Sep 2024 14:41:50 +0200 Subject: [PATCH 015/151] Memory initialization and bugfixes --- src/posix/handlers/rename.hpp | 6 +++--- src/posix/utils/cache.hpp | 15 ++++++++++++--- src/posix/utils/filesystem.hpp | 23 +++++++++++------------ src/server/file-manager/file_manager.hpp | 2 +- src/server/file-manager/fs_monitor.hpp | 4 ---- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/posix/handlers/rename.hpp b/src/posix/handlers/rename.hpp index 96dad6ec0..1cc7b0d8a 100644 --- a/src/posix/handlers/rename.hpp +++ b/src/posix/handlers/rename.hpp @@ -6,11 +6,11 @@ #include "utils/filesystem.hpp" int rename_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long *result) { - const std::filesystem::path oldpath(reinterpret_cast(arg0)); - const std::filesystem::path newpath(reinterpret_cast(arg1)); + std::filesystem::path oldpath(reinterpret_cast(arg0)); + std::filesystem::path newpath(reinterpret_cast(arg1)); auto tid = static_cast(syscall_no_intercept(SYS_gettid)); START_LOG(tid, "call(oldpath=%s, newpath=%s)", oldpath.c_str(), newpath.c_str()); - + LOG("Compute paths"); auto oldpath_abs = capio_absolute(oldpath); LOG("oldpath absolute: %s", oldpath_abs.c_str()); auto newpath_abs = capio_absolute(newpath); diff --git a/src/posix/utils/cache.hpp b/src/posix/utils/cache.hpp index 614d0cfff..54681e226 100644 --- a/src/posix/utils/cache.hpp +++ b/src/posix/utils/cache.hpp @@ -28,7 +28,7 @@ class WriteRequestCache { void write_request(std::filesystem::path path, int tid, int fd, long count) { START_LOG(capio_syscall(SYS_gettid), "call(path=%s, tid=%ld, fd=%ld, count=%ld)", path.c_str(), tid, fd, count); - if (fd != current_fd) { + if (fd != current_fd || path.compare(current_path) != 0) { LOG("File descriptor changed from previous state. updating"); this->flush(tid); current_path = std::move(path); @@ -48,6 +48,7 @@ class WriteRequestCache { LOG("Performing write to SHM"); _write_request(tid, current_fd, current_size); } + current_fd = -1; current_size = 0; } }; @@ -82,8 +83,10 @@ class ReadRequestCache { ~ReadRequestCache() { delete available_read_cache; }; void read_request(std::filesystem::path path, long end_of_read, int tid, int fd) { - - if (fd != current_fd) { + START_LOG(capio_syscall(SYS_gettid), "call(path=%s, end_of_read=%ld, tid=%ld)", + path.c_str(), end_of_read, tid); + if (fd != current_fd || path.compare(current_path) != 0) { + LOG("File descriptor / path changed from previous state. updating"); current_path = std::move(path); current_fd = fd; @@ -94,20 +97,26 @@ class ReadRequestCache { max_read = 0; available_read_cache->emplace(path, max_read); } + LOG("Max read value is %llu %s", max_read, max_read == ULLONG_MAX ? "(ULLONG_MAX)" : ""); } // File is committed if server reports its size to be ULLONG_MAX if (max_read == ULLONG_MAX) { + LOG("Max read is ULLONG_MAX. returning as file is committed"); return; } if (end_of_read > max_read) { + LOG("end_of_read > max_read. Performing server request"); max_read = _read_request(current_path, end_of_read, tid, fd); + LOG("Obtained value from server is %ld", max_read); if (available_read_cache->find(path) == available_read_cache->end()) { available_read_cache->emplace(path, max_read); } else { available_read_cache->at(path) = max_read; } + LOG("completed update from server of max read for file. returning control to " + "application"); } }; }; diff --git a/src/posix/utils/filesystem.hpp b/src/posix/utils/filesystem.hpp index cb21bdce1..b0898baf5 100644 --- a/src/posix/utils/filesystem.hpp +++ b/src/posix/utils/filesystem.hpp @@ -253,18 +253,17 @@ std::filesystem::path get_dir_path(int dirfd) { if (it != capio_files_descriptors->end()) { LOG("dirfd %d points to path %s", dirfd, it->second.c_str()); return it->second; - } else { - LOG("dirfd %d not found. Computing it trough proclnk", dirfd); - char proclnk[128]; - char dir_pathname[PATH_MAX]; - sprintf(proclnk, "/proc/self/fd/%d", dirfd); - if (syscall_no_intercept(SYS_readlink, proclnk, dir_pathname, PATH_MAX) < 0) { - LOG("failed to readlink\n"); - return {}; - } - LOG("dirfd %d points to path %s", dirfd, dir_pathname); - return {dir_pathname}; } + LOG("dirfd %d not found. Computing it through proclnk", dirfd); + char proclnk[128] = {}; + char dir_pathname[PATH_MAX] = {}; + sprintf(proclnk, "/proc/self/fd/%d", dirfd); + if (syscall_no_intercept(SYS_readlink, proclnk, dir_pathname, PATH_MAX) < 0) { + LOG("failed to readlink\n"); + return {}; + } + LOG("dirfd %d points to path %s", dirfd, dir_pathname); + return {dir_pathname}; } /** @@ -288,7 +287,7 @@ inline void init_filesystem() { inline void rename_capio_path(const std::string &oldpath, const std::string &newpath) { START_LOG(syscall_no_intercept(SYS_gettid), "call(oldpath=%s, newpath=%s)", oldpath.c_str(), newpath.c_str()); - if (capio_files_paths->find(oldpath) != capio_files_paths->find(newpath)) { + if (capio_files_paths->find(oldpath) != capio_files_paths->end()) { auto entry = capio_files_paths->extract(oldpath); entry.key() = newpath; capio_files_paths->insert(std::move(entry)); diff --git a/src/server/file-manager/file_manager.hpp b/src/server/file-manager/file_manager.hpp index 83ab2c1ba..83ae0589c 100644 --- a/src/server/file-manager/file_manager.hpp +++ b/src/server/file-manager/file_manager.hpp @@ -28,7 +28,7 @@ class CapioFileManager { void set_committed(const std::filesystem::path &path) const; void set_committed(pid_t tid) const; static bool is_committed(const std::filesystem::path &path); - void check_and_unlock_thread_awaiting_data(const std::string& path) const; + void check_and_unlock_thread_awaiting_data(const std::string &path) const; void add_thread_awaiting_data(std::string path, int tid, size_t expected_size) const; void unlock_thread_awaiting_creation(std::string path) const; void add_thread_awaiting_creation(std::string path, pid_t tid) const; diff --git a/src/server/file-manager/fs_monitor.hpp b/src/server/file-manager/fs_monitor.hpp index 89b6ac602..03d9763ae 100644 --- a/src/server/file-manager/fs_monitor.hpp +++ b/src/server/file-manager/fs_monitor.hpp @@ -29,10 +29,6 @@ class FileSystemMonitor { LOG("File %s exists. Unlocking thread awaiting for creation", file.c_str()); file_manager->unlock_thread_awaiting_creation(file); file_manager->delete_file_awaiting_creation(file); - LOG("File %s exists. Checking if enough data is available", file.c_str()); - // actual update, end eventual removal from map is handled by the - // CapioFileManager class and not by the FileSystemMonitor class - file_manager->check_and_unlock_thread_awaiting_data(file); LOG("Completed handling.\n\n"); } } From 60aae95e49b545e2a1730246bb1b4cc5e2716194 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Thu, 12 Sep 2024 14:29:41 +0200 Subject: [PATCH 016/151] Code cleanup and bugfixes --- src/common/capio/queue.hpp | 2 +- src/posix/handlers/open.hpp | 9 +++---- src/posix/utils/cache.hpp | 33 +++++++++++++------------ src/posix/utils/filesystem.hpp | 44 +++++++--------------------------- src/posix/utils/requests.hpp | 8 +++---- src/posix/utils/snapshot.hpp | 36 ++++++++++++++++++---------- src/posix/utils/types.hpp | 7 +++--- 7 files changed, 62 insertions(+), 77 deletions(-) diff --git a/src/common/capio/queue.hpp b/src/common/capio/queue.hpp index 8a8fcc0d6..5cedadcce 100644 --- a/src/common/capio/queue.hpp +++ b/src/common/capio/queue.hpp @@ -22,7 +22,7 @@ template class Queue { Mutex _mutex; NamedSemaphore _sem_num_elems, _sem_num_empty; - inline void _read(T *buff_recv, unsigned long long int num_bytes) { + inline void _read(T *buff_recv, capio_off64_t num_bytes) { _sem_num_elems.lock(); std::lock_guard lg(_mutex); diff --git a/src/posix/handlers/open.hpp b/src/posix/handlers/open.hpp index c5f5731aa..40b7f21b3 100644 --- a/src/posix/handlers/open.hpp +++ b/src/posix/handlers/open.hpp @@ -54,8 +54,7 @@ int creat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long ar if (is_capio_path(path) && fd >= 0) { LOG("Registering path and fd"); - add_capio_fd(tid, path, fd, 0, CAPIO_DEFAULT_FILE_INITIAL_SIZE, flags, - (flags & O_CLOEXEC) == O_CLOEXEC); + add_capio_fd(tid, path, fd, 0, (flags & O_CLOEXEC) == O_CLOEXEC); } *result = fd; @@ -85,8 +84,7 @@ int open_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long arg if (is_capio_path(path) && fd >= 0) { LOG("Adding capio path"); - add_capio_fd(tid, path, fd, 0, CAPIO_DEFAULT_FILE_INITIAL_SIZE, flags, - (flags & O_CLOEXEC) == O_CLOEXEC); + add_capio_fd(tid, path, fd, 0, (flags & O_CLOEXEC) == O_CLOEXEC); } *result = fd; @@ -118,8 +116,7 @@ int openat_handler(long arg0, long arg1, long arg2, long arg3, long arg4, long a if (is_capio_path(path) && fd >= 0) { LOG("Adding capio path"); - add_capio_fd(tid, path, fd, 0, CAPIO_DEFAULT_FILE_INITIAL_SIZE, flags, - (flags & O_CLOEXEC) == O_CLOEXEC); + add_capio_fd(tid, path, fd, 0, (flags & O_CLOEXEC) == O_CLOEXEC); } *result = fd; diff --git a/src/posix/utils/cache.hpp b/src/posix/utils/cache.hpp index 54681e226..8f8a0ae8a 100644 --- a/src/posix/utils/cache.hpp +++ b/src/posix/utils/cache.hpp @@ -61,17 +61,17 @@ class ReadRequestCache { std::filesystem::path current_path; // return amount of readable bytes - static inline off64_t _read_request(const std::filesystem::path &path, - const off64_t end_of_Read, const long tid, const long fd) { + static capio_off64_t _read_request(const std::filesystem::path &path, const off64_t end_of_Read, + const long tid, const long fd) { START_LOG(capio_syscall(SYS_gettid), "call(path=%s, end_of_Read=%ld, tid=%ld, fd=%ld)", path.c_str(), end_of_Read, tid, fd); char req[CAPIO_REQ_MAX_SIZE]; sprintf(req, "%04d %s %ld %ld %ld", CAPIO_REQUEST_READ, path.c_str(), tid, fd, end_of_Read); LOG("Sending read request %s", req); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res; + capio_off64_t res; bufs_response->at(tid)->read(&res); - LOG("Response to request is %ld", res); + LOG("Response to request is %llu", res); return res; } @@ -83,39 +83,42 @@ class ReadRequestCache { ~ReadRequestCache() { delete available_read_cache; }; void read_request(std::filesystem::path path, long end_of_read, int tid, int fd) { - START_LOG(capio_syscall(SYS_gettid), "call(path=%s, end_of_read=%ld, tid=%ld)", + START_LOG(capio_syscall(SYS_gettid), "[cache] call(path=%s, end_of_read=%ld, tid=%ld)", path.c_str(), end_of_read, tid); if (fd != current_fd || path.compare(current_path) != 0) { - LOG("File descriptor / path changed from previous state. updating"); + LOG("[cache] %s changed from previous state. updating", + fd != current_fd ? "File descriptor" : "File path"); current_path = std::move(path); current_fd = fd; - auto entry = available_read_cache->find(path); - if (entry != available_read_cache->end()) { - max_read = entry->second; + if (available_read_cache->find(current_path) != available_read_cache->end()) { + LOG("[cache] Found file entry in cache"); + max_read = available_read_cache->at(current_path); } else { + LOG("[cache] Entry not found, initializing new entry to offset 0"); max_read = 0; - available_read_cache->emplace(path, max_read); + available_read_cache->emplace(current_path, max_read); } - LOG("Max read value is %llu %s", max_read, max_read == ULLONG_MAX ? "(ULLONG_MAX)" : ""); + LOG("[cache] Max read value is %llu %s", max_read, + max_read == ULLONG_MAX ? "(ULLONG_MAX)" : ""); } // File is committed if server reports its size to be ULLONG_MAX if (max_read == ULLONG_MAX) { - LOG("Max read is ULLONG_MAX. returning as file is committed"); + LOG("[cache] Returning as file is committed"); return; } if (end_of_read > max_read) { - LOG("end_of_read > max_read. Performing server request"); + LOG("[cache] end_of_read > max_read. Performing server request"); max_read = _read_request(current_path, end_of_read, tid, fd); - LOG("Obtained value from server is %ld", max_read); + LOG("[cache] Obtained value from server is %llu", max_read); if (available_read_cache->find(path) == available_read_cache->end()) { available_read_cache->emplace(path, max_read); } else { available_read_cache->at(path) = max_read; } - LOG("completed update from server of max read for file. returning control to " + LOG("[cache] completed update from server of max read for file. returning control to " "application"); } }; diff --git a/src/posix/utils/filesystem.hpp b/src/posix/utils/filesystem.hpp index b0898baf5..319f4a46a 100644 --- a/src/posix/utils/filesystem.hpp +++ b/src/posix/utils/filesystem.hpp @@ -48,13 +48,14 @@ inline void add_capio_path(const std::string &path) { } /** - * Add a file descriptor to metadata structures + * Add a file descriptor to metadata structures. is_cloexec is used to handle whether the fd needs + * to be closed before executing an execve or not (fd persists if is_cloexec == true) * @param path * @param fd * @return */ inline void add_capio_fd(pid_t tid, const std::string &path, int fd, capio_off64_t offset, - capio_off64_t init_size, int flags, bool is_cloexec) { + bool is_cloexec) { START_LOG(tid, "call(path=%s, fd=%d)", path.c_str(), fd); add_capio_path(path); LOG("Added capio path %s", path.c_str()); @@ -62,7 +63,7 @@ inline void add_capio_fd(pid_t tid, const std::string &path, int fd, capio_off64 LOG("Inserted tid %d for path %s", tid, path.c_str()); capio_files_descriptors->insert({fd, path}); LOG("Inserted file descriptor tuple"); - files->insert({fd, {std::make_shared(offset), init_size, flags, is_cloexec}}); + files->insert({fd, {std::make_shared(offset), 0, 0, is_cloexec}}); LOG("Registered file"); } @@ -184,15 +185,6 @@ inline bool exists_capio_fd(pid_t fd) { return files->find(fd) != files->end(); } -/** - * Check if a path exists in metadata structures - * @param path - * @return if the path exists - */ -inline bool exists_capio_path(const std::string &path) { - return capio_files_paths->find(path) != capio_files_paths->end(); -} - /** * Get the CLOEXEC property of a file descriptor in metadata structures * @param fd @@ -200,13 +192,6 @@ inline bool exists_capio_path(const std::string &path) { */ inline bool get_capio_fd_cloexec(int fd) { return std::get<3>(files->at(fd)); } -/** - * Get the active flags of a file descriptor in metadata structures - * @param fd - * @return the active flags - */ -inline bool get_capio_fd_flags(int fd) { return std::get<2>(files->at(fd)); } - /** * Get the path of a file descriptor * @param fd @@ -219,14 +204,7 @@ inline const std::string &get_capio_fd_path(int fd) { return capio_files_descrip * @param fd * @return the current offset */ -inline off64_t get_capio_fd_offset(int fd) { return *std::get<0>(files->at(fd)); } - -/** - * Get the actual size of a file descriptor - * @param fd - * @return the actual size of the file - */ -inline off64_t get_capio_fd_size(int fd) { return std::get<1>(files->at(fd)); } +inline capio_off64_t get_capio_fd_offset(int fd) { return *std::get<0>(files->at(fd)); } /** * Get all the file descriptors stored in metadata structures @@ -299,21 +277,15 @@ inline void rename_capio_path(const std::string &oldpath, const std::string &new } } -/** - * Modify the active flags of a file descriptor in metadata structures - * @param fd - * @param flags - * @return - */ -inline void set_capio_fd_flags(int fd, int flags) { std::get<2>(files->at(fd)) = flags; } - /** * Set the offset of a file descriptor in metadata structures * @param fd * @param offset * @return */ -inline void set_capio_fd_offset(int fd, off64_t offset) { *std::get<0>(files->at(fd)) = offset; } +inline void set_capio_fd_offset(int fd, capio_off64_t offset) { + *std::get<0>(files->at(fd)) = offset; +} /** * Change the current directory diff --git a/src/posix/utils/requests.hpp b/src/posix/utils/requests.hpp index 6b43c029e..5208e4164 100644 --- a/src/posix/utils/requests.hpp +++ b/src/posix/utils/requests.hpp @@ -36,8 +36,8 @@ inline void init_client() { * @return */ inline void register_listener(long tid) { - auto *p_buf_response = new CircularBuffer(SHM_COMM_CHAN_NAME_RESP + std::to_string(tid), - CAPIO_REQ_BUFF_CNT, sizeof(off_t)); + auto *p_buf_response = new CircularBuffer( + SHM_COMM_CHAN_NAME_RESP + std::to_string(tid), CAPIO_REQ_BUFF_CNT, sizeof(off_t)); bufs_response->insert(std::make_pair(tid, p_buf_response)); } @@ -49,7 +49,7 @@ inline void consent_to_proceed_request(const std::filesystem::path &path, const char req[CAPIO_REQ_MAX_SIZE]; sprintf(req, "%04d %ld %s %s", CAPIO_REQUEST_CONSENT, tid, path.c_str(), source_func.c_str()); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res; + capio_off64_t res; bufs_response->at(tid)->read(&res); } @@ -103,7 +103,7 @@ inline void open_request(const int fd, const std::filesystem::path &path, const char req[CAPIO_REQ_MAX_SIZE]; sprintf(req, "%04d %ld %d %s", CAPIO_REQUEST_OPEN, tid, fd, path.c_str()); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); - off64_t res; + capio_off64_t res; bufs_response->at(tid)->read(&res); } diff --git a/src/posix/utils/snapshot.hpp b/src/posix/utils/snapshot.hpp index ac5fe8d51..523a813f2 100644 --- a/src/posix/utils/snapshot.hpp +++ b/src/posix/utils/snapshot.hpp @@ -9,16 +9,27 @@ #include "types.hpp" +/** + * EXPLANATION OF SNAPSHOT. + * From the execve manual: By default, file descriptors remain open across an execve(). + * File descriptors that are marked close-on-exec are closed; + * + * This means that the snapshot is used to move file descriptor metadata (that is handled by capio) + * forward to the new process through shared memory. at the execve syscall, a snapshot is created, + * by storing the required information and then it gets inherited by the new process, removing the + * FDs opened with FD_CLOEXEC + */ + inline int *get_fd_snapshot(long tid) { return static_cast(get_shm_if_exist("capio_snapshot_" + std::to_string(tid))); } -void initialize_from_snapshot(const int *fd_shm, long tid) { +void initialize_from_snapshot(const int *fd_shm, pid_t tid) { START_LOG(tid, "call(%ld)", fd_shm); int i = 0; std::string shm_name; int fd; - off64_t *p_shm; + capio_off64_t *p_shm; char *path_shm; std::string pid = std::to_string(tid); @@ -26,17 +37,20 @@ void initialize_from_snapshot(const int *fd_shm, long tid) { while ((fd = fd_shm[i]) != -1) { shm_name = "capio_snapshot_path_" + pid + "_" + std::to_string(fd); path_shm = static_cast(get_shm(shm_name)); + if (munmap(path_shm, PATH_MAX) == -1) { ERR_EXIT("munmap initialize_from_snapshot"); } + if (shm_unlink(shm_name.c_str()) == -1) { ERR_EXIT("shm_unlink snapshot %s", shm_name.c_str()); } + shm_name = "capio_snapshot_" + pid + "_" + std::to_string(fd); - p_shm = static_cast(get_shm(shm_name)); - add_capio_fd(tid, path_shm, fd, p_shm[1], p_shm[2], static_cast(p_shm[3]), - static_cast(p_shm[4])); - if (munmap(p_shm, 6 * sizeof(off64_t)) == -1) { + p_shm = static_cast(get_shm(shm_name)); + add_capio_fd(tid, path_shm, fd, p_shm[1], static_cast(p_shm[2])); + + if (munmap(p_shm, 3 * sizeof(capio_off64_t)) == -1) { ERR_EXIT("munmap 2 initialize_from_snapshot"); } if (shm_unlink(shm_name.c_str()) == -1) { @@ -52,7 +66,7 @@ void initialize_from_snapshot(const int *fd_shm, long tid) { void create_snapshot(long tid) { START_LOG(tid, "call()"); - off64_t *p_shm; + capio_off64_t *p_shm; char *path_shm; std::string pid = std::to_string(tid); @@ -67,13 +81,11 @@ void create_snapshot(long tid) { for (auto &fd : fds) { fd_shm[i] = fd; - p_shm = (off64_t *) create_shm("capio_snapshot_" + pid + "_" + std::to_string(fd), - 5 * sizeof(off64_t)); + p_shm = (capio_off64_t *) create_shm("capio_snapshot_" + pid + "_" + std::to_string(fd), + 3 * sizeof(capio_off64_t)); p_shm[0] = fd; p_shm[1] = get_capio_fd_offset(fd); - p_shm[2] = get_capio_fd_size(fd); - p_shm[3] = get_capio_fd_flags(fd); - p_shm[4] = get_capio_fd_cloexec(fd); + p_shm[2] = get_capio_fd_cloexec(fd); std::string shm_name = "capio_snapshot_path_" + pid + "_" + std::to_string(fd); path_shm = (char *) create_shm(shm_name, PATH_MAX * sizeof(char)); diff --git a/src/posix/utils/types.hpp b/src/posix/utils/types.hpp index 1308da32f..fbd6ee0b4 100644 --- a/src/posix/utils/types.hpp +++ b/src/posix/utils/types.hpp @@ -6,9 +6,10 @@ #include "capio/queue.hpp" -typedef std::unordered_map, off64_t, int, bool>> CPFiles_t; -typedef std::pair CPStatResponse_t; -typedef std::unordered_map *> CPBufResponse_t; +typedef std::unordered_map, capio_off64_t, int, bool>> + CPFiles_t; +typedef std::unordered_map *> CPBufResponse_t; typedef std::unordered_map CPFileDescriptors_t; typedef std::unordered_map> CPFilesPaths_t; From 152b2192530485482b6892e06fc003ae8da179ac Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Thu, 12 Sep 2024 20:18:04 +0200 Subject: [PATCH 017/151] Support for n_files --- .../capio-cl-engine/capio_cl_engine.hpp | 14 ++++-- src/server/capio-cl-engine/json_parser.hpp | 6 +-- .../client-manager/handlers/consent.hpp | 16 +++++-- src/server/file-manager/file_manager_impl.hpp | 47 ++++++++++++++++--- 4 files changed, 65 insertions(+), 18 deletions(-) diff --git a/src/server/capio-cl-engine/capio_cl_engine.hpp b/src/server/capio-cl-engine/capio_cl_engine.hpp index 286ec8dfa..8a49b208d 100644 --- a/src/server/capio-cl-engine/capio_cl_engine.hpp +++ b/src/server/capio-cl-engine/capio_cl_engine.hpp @@ -50,9 +50,7 @@ class CapioCLEngine { for (auto itm : _locations) { std::string name_trunc = truncate_last_n(itm.first, 12); auto kind = std::get<6>(itm.second) ? "F" : "D"; - - std::cout << "| " << kind << " " - << "| " << name_trunc << std::setfill(' ') + std::cout << "| " << kind << " " << "| " << name_trunc << std::setfill(' ') << std::setw(20 - name_trunc.length()) << "| "; auto producers = std::get<0>(itm.second); @@ -159,6 +157,13 @@ class CapioCLEngine { } } + long getDirectoryFileCount(std::string path) { + if (_locations.find(path) != _locations.end()) { + return std::get<8>(_locations.at(path)); + } + return 0; + } + void addProducer(const std::string &path, std::string &producer) { START_LOG(gettid(), "call(path=%s, producer=%s)", path.c_str(), producer.c_str()); producer.erase(remove_if(producer.begin(), producer.end(), isspace), producer.end()); @@ -189,6 +194,9 @@ class CapioCLEngine { std::string getFireRule(const std::string &path) { START_LOG(gettid(), "call(path=%s)", path.c_str()); + if(_locations.find(path) == _locations.end()) { + return CAPIO_FILE_MODE_UPDATE; + } return std::get<3>(_locations.at(path)); } diff --git a/src/server/capio-cl-engine/json_parser.hpp b/src/server/capio-cl-engine/json_parser.hpp index efae9cad6..09f944584 100644 --- a/src/server/capio-cl-engine/json_parser.hpp +++ b/src/server/capio-cl-engine/json_parser.hpp @@ -207,13 +207,9 @@ class JsonParser { std::string commit(commit_rule), firerule(mode); if (n_files != -1) { std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON - << "Setting path: " << path << " to be a directory" + << "Setting path: " << path << " n_files to " << n_files << std::endl; locations->setDirectoryFileCount(path, n_files); - } else { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON - << "Setting path: " << path << " to be a file" << std::endl; - locations->setFile(path); } is_file ? locations->setFile(path) : locations->setDirectory(path); diff --git a/src/server/client-manager/handlers/consent.hpp b/src/server/client-manager/handlers/consent.hpp index 30f33f097..f60f923ab 100644 --- a/src/server/client-manager/handlers/consent.hpp +++ b/src/server/client-manager/handlers/consent.hpp @@ -8,7 +8,7 @@ inline void consent_to_proceed_handler(const char *const str) { pid_t tid; char path[1024], source_func[1024]; sscanf(str, "%d %s %s", &tid, path, source_func); - START_LOG(gettid(), "call(tid=%d, path=%s)", tid, path); + START_LOG(gettid(), "call(tid=%d, path=%s, source=%s)", tid, path, source_func); std::filesystem::path path_fs(path); @@ -26,13 +26,21 @@ inline void consent_to_proceed_handler(const char *const str) { return; } - if (std::filesystem::exists(path) || CapioFileManager::is_committed(path) || - capio_cl_engine->isProducer(path, tid)) { + // TODO: check this expression as being the correct evaluation one + // NOTE: expression is (exists AND (committed OR no_update)) OR is_producer + + bool exists = std::filesystem::exists(path); + bool committed = CapioFileManager::is_committed(path); + bool firable = capio_cl_engine->getFireRule(path) == CAPIO_FILE_MODE_NO_UPDATE; + bool is_producer = capio_cl_engine->isProducer(path, tid); + LOG("exists=%s, committed=%s, firable=%s, is_producer=%s", exists ? "true" : "false", + committed ? "true" : "false", firable ? "true" : "false", is_producer ? "true" : "false"); + if ((exists && (committed || firable)) || is_producer) { LOG("It is possible to unlock waiting thread"); client_manager->reply_to_client(tid, 1); } else { LOG("Requested file %s does not exists yet. awaiting for creation", path); - file_manager->add_thread_awaiting_creation(path, tid); + file_manager->add_thread_awaiting_data(path, tid, 0); } } diff --git a/src/server/file-manager/file_manager_impl.hpp b/src/server/file-manager/file_manager_impl.hpp index 4e9cb6588..0408c1192 100644 --- a/src/server/file-manager/file_manager_impl.hpp +++ b/src/server/file-manager/file_manager_impl.hpp @@ -38,6 +38,7 @@ inline void CapioFileManager::add_thread_awaiting_data(std::string path, int tid thread_awaiting_data->at(path)->emplace(tid, expected_size); } +// TODO: maybe merge check for data and existance inline void CapioFileManager::check_and_unlock_thread_awaiting_data(const std::string &path) const { START_LOG(gettid(), "call(path=%s)", path.c_str()); LOG("Before lockguard"); @@ -46,10 +47,17 @@ inline void CapioFileManager::check_and_unlock_thread_awaiting_data(const std::s if (thread_awaiting_data->find(path) != thread_awaiting_data->end()) { LOG("Path has thread awaiting"); auto threads = thread_awaiting_data->at(path); - + LOG("Obtained threads"); for (auto item = threads->begin(); item != threads->end();) { LOG("Handling thread"); - if (is_committed(path) || item->first >= std::filesystem::file_size(path)) { + uintmax_t filesize = -1; + if (std::filesystem::exists(path)) { + filesize = + std::filesystem::is_directory(path) ? -1 : std::filesystem::file_size(path); + } + if (is_committed(path) || + (item->first >= filesize && + capio_cl_engine->getFireRule(path) == CAPIO_FILE_MODE_NO_UPDATE)) { LOG("Thread %ld can be unlocked", item->first); /* * Check for file size only if it is directory, otherwise, @@ -102,10 +110,37 @@ inline void CapioFileManager::set_committed(pid_t tid) const { inline bool CapioFileManager::is_committed(const std::filesystem::path &path) { START_LOG(gettid(), "call(path=%s)", path.c_str()); - std::string computed_path = path.string() + ".capio"; - LOG("File %s %s committed", path.c_str(), - std::filesystem::exists(computed_path) ? "is" : "is not"); - return std::filesystem::exists(computed_path); + bool file_exists = std::filesystem::exists(path); + LOG("File exists: %s", file_exists ? "true" : "false"); + + if (!file_exists) { + return false; + } + + if (std::filesystem::is_directory(path)) { + // is directory + // check for n_files inside a directory + LOG("Path is a directory"); + auto file_count = capio_cl_engine->getDirectoryFileCount(path); + LOG("Expected file count is %ld", file_count); + long count = 0; + for (auto const &file : std::filesystem::directory_iterator{path}) { + if (file.path().extension() != ".capio") { + ++count; + } + } + bool continue_directory = count >= file_count; + + LOG("Final result: %s", continue_directory ? "COMMITTED" : "NOT COMMITTED"); + return continue_directory; + } else { + // if is file + LOG("Path is a file"); + std::string computed_path = path.string() + ".capio"; + LOG("File %s %s existing", path.c_str(), + std::filesystem::exists(computed_path) ? "is" : "is not"); + return std::filesystem::exists(computed_path); + } } inline std::vector CapioFileManager::get_file_awaiting_creation() const { From e6d0c10b18ef5f22c5918b7d48007ea1f5bc30eb Mon Sep 17 00:00:00 2001 From: marco edoardo santimaria Date: Tue, 17 Sep 2024 13:23:59 +0000 Subject: [PATCH 018/151] Support for CoF --- .gitignore | 2 +- src/common/capio/constants.hpp | 1 + .../capio-cl-engine/capio_cl_engine.hpp | 35 +++++++++++++----- src/server/capio-cl-engine/json_parser.hpp | 37 ++++++++++++++++++- src/server/file-manager/file_manager_impl.hpp | 19 ++++++++-- src/server/file-manager/fs_monitor.hpp | 2 +- 6 files changed, 81 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 489080712..4a0f1467e 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,4 @@ capio_logs # Other debug build - +.devcontainer \ No newline at end of file diff --git a/src/common/capio/constants.hpp b/src/common/capio/constants.hpp index 5ca94039c..8a5f8d98d 100644 --- a/src/common/capio/constants.hpp +++ b/src/common/capio/constants.hpp @@ -44,6 +44,7 @@ constexpr int CAPIO_LOG_MAX_MSG_LEN = 2048; constexpr char CAPIO_FILE_MODE_NO_UPDATE[] = "no_update"; constexpr char CAPIO_FILE_MODE_UPDATE[] = "update"; constexpr char CAPIO_FILE_COMMITTED_ON_CLOSE[] = "on_close"; +constexpr char CAPIO_FILE_COMMITTED_ON_FILE[] = "on_file"; constexpr char CAPIO_FILE_COMMITTED_ON_TERMINATION[] = "on_termination"; // CAPIO POSIX return codes diff --git a/src/server/capio-cl-engine/capio_cl_engine.hpp b/src/server/capio-cl-engine/capio_cl_engine.hpp index 8a49b208d..fb14b5b2d 100644 --- a/src/server/capio-cl-engine/capio_cl_engine.hpp +++ b/src/server/capio-cl-engine/capio_cl_engine.hpp @@ -15,8 +15,8 @@ class CapioCLEngine { bool, // exclude [5] bool, // is_file (if true yes otherwise it is a directory) [6] int, // commit on file number [7] - long> // directory file count [8] - > + long, // directory file count [8] + std::vector>> // File dependencies [9] _locations; static std::string truncate_last_n(const std::string &str, int n) { @@ -50,7 +50,7 @@ class CapioCLEngine { for (auto itm : _locations) { std::string name_trunc = truncate_last_n(itm.first, 12); auto kind = std::get<6>(itm.second) ? "F" : "D"; - std::cout << "| " << kind << " " << "| " << name_trunc << std::setfill(' ') + std::cout << "| " << kind << " | " << name_trunc << std::setfill(' ') << std::setw(20 - name_trunc.length()) << "| "; auto producers = std::get<0>(itm.second); @@ -123,12 +123,13 @@ class CapioCLEngine { void add(std::string &path, std::vector &producers, std::vector &consumers, const std::string &commit_rule, - const std::string &fire_rule, bool permanent, bool exclude) { + const std::string &fire_rule, bool permanent, bool exclude, + const std::vector &dependencies) { START_LOG(gettid(), "call(path=%s, commit=%s, fire=%s, permanent=%s, exclude=%s)", path.c_str(), commit_rule.c_str(), fire_rule.c_str(), permanent ? "YES" : "NO", exclude ? "YES" : "NO"); _locations.emplace(path, std::make_tuple(producers, consumers, commit_rule, fire_rule, - permanent, exclude, true, -1, -1)); + permanent, exclude, true, -1, -1, dependencies)); } void newFile(const std::string &path) { @@ -151,9 +152,10 @@ class CapioCLEngine { } } - _locations.emplace(path, std::make_tuple(std::vector(), - std::vector(), commit, fire, - false, false, true, -1, -1)); + _locations.emplace(path, + std::make_tuple(std::vector(), + std::vector(), commit, fire, false, + false, true, -1, -1, std::vector())); } } @@ -194,7 +196,7 @@ class CapioCLEngine { std::string getFireRule(const std::string &path) { START_LOG(gettid(), "call(path=%s)", path.c_str()); - if(_locations.find(path) == _locations.end()) { + if (_locations.find(path) == _locations.end()) { return CAPIO_FILE_MODE_UPDATE; } return std::get<3>(_locations.at(path)); @@ -281,6 +283,21 @@ class CapioCLEngine { LOG("No match has been found"); return false; } + + void set_file_deps(const std::filesystem::path &path, + const std::vector &dependencies) { + START_LOG(gettid(), "call()"); + std::get<9>(_locations.at(path)) = dependencies; + for (const auto &itm : dependencies) { + LOG("Creating new fie (if it exists) for path %s", itm.c_str()); + newFile(itm); + } + } + + // todo fix leak + std::vector get_file_deps(const std::filesystem::path &path) { + return std::get<9>(_locations.at(path)); + } }; CapioCLEngine *capio_cl_engine; diff --git a/src/server/capio-cl-engine/json_parser.hpp b/src/server/capio-cl-engine/json_parser.hpp index 09f944584..bd86e8f66 100644 --- a/src/server/capio-cl-engine/json_parser.hpp +++ b/src/server/capio-cl-engine/json_parser.hpp @@ -73,7 +73,10 @@ class JsonParser { std::string appname(app_name); std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "File : " << file << " added to app: " << app_name << std::endl; - locations->newFile(file); + if (file.is_relative()) { + file = capio_dir / file; + } + locations->newFile(file.c_str()); locations->addConsumer(file, appname); } else { std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "File : " << file @@ -98,6 +101,9 @@ class JsonParser { std::string appname(app_name); std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Adding file: " << file << " to app: " << app_name << std::endl; + if (file.is_relative()) { + file = capio_dir / file; + } locations->newFile(file); locations->addProducer(file, appname); } @@ -117,6 +123,7 @@ class JsonParser { for (auto file : streaming) { std::string_view committed, mode, commit_rule; std::vector streaming_names; + std::vector file_deps; long int n_close = -1; long n_files, batch_size; bool is_file = true; @@ -175,6 +182,33 @@ class JsonParser { commit_rule = committed; } } + + // check for committed on file: + if (commit_rule == CAPIO_FILE_COMMITTED_ON_FILE) { + simdjson::ondemand::array file_deps_tmp; + error = file["file_deps"].get_array().get(file_deps_tmp); + + if (error) { + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + << "commit rule is on_file but no file_deps section found" + << std::endl; + ERR_EXIT("commit rule is on_file but no file_deps section found"); + } + + std::string_view name_tmp; + for (auto itm : file_deps_tmp) { + name_tmp = itm.get_string().value(); + std::filesystem::path computed_path(name_tmp); + computed_path = computed_path.is_relative() + ? (get_capio_dir() / computed_path) + : computed_path; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + << "Adding file: " << computed_path + << " to file dependencies: " << std::endl; + file_deps.emplace_back(computed_path); + } + } + LOG("Committed: %s", std::string(committed).c_str()); // END PARSING COMMITTED @@ -216,6 +250,7 @@ class JsonParser { locations->setCommitRule(path, commit); locations->setFireRule(path, firerule); locations->setCommitedNumber(path, n_close); + locations->set_file_deps(path, file_deps); } } diff --git a/src/server/file-manager/file_manager_impl.hpp b/src/server/file-manager/file_manager_impl.hpp index 0408c1192..648026ee1 100644 --- a/src/server/file-manager/file_manager_impl.hpp +++ b/src/server/file-manager/file_manager_impl.hpp @@ -38,7 +38,7 @@ inline void CapioFileManager::add_thread_awaiting_data(std::string path, int tid thread_awaiting_data->at(path)->emplace(tid, expected_size); } -// TODO: maybe merge check for data and existance +// TODO: maybe merge check for data and existence inline void CapioFileManager::check_and_unlock_thread_awaiting_data(const std::string &path) const { START_LOG(gettid(), "call(path=%s)", path.c_str()); LOG("Before lockguard"); @@ -111,7 +111,7 @@ inline void CapioFileManager::set_committed(pid_t tid) const { inline bool CapioFileManager::is_committed(const std::filesystem::path &path) { START_LOG(gettid(), "call(path=%s)", path.c_str()); bool file_exists = std::filesystem::exists(path); - LOG("File exists: %s", file_exists ? "true" : "false"); + LOG("File %s exists: %s", path.c_str(), file_exists ? "true" : "false"); if (!file_exists) { return false; @@ -137,8 +137,21 @@ inline bool CapioFileManager::is_committed(const std::filesystem::path &path) { // if is file LOG("Path is a file"); std::string computed_path = path.string() + ".capio"; - LOG("File %s %s existing", path.c_str(), + LOG("CAPIO token file %s %s existing", computed_path.c_str(), std::filesystem::exists(computed_path) ? "is" : "is not"); + + if (capio_cl_engine->getCommitRule(path) == CAPIO_FILE_COMMITTED_ON_FILE) { + LOG("Commit rule is on_file. Checking for file dependencies"); + bool commit_computed = true; + for (auto file : capio_cl_engine->get_file_deps(path)) { + commit_computed = commit_computed && is_committed(file); + } + + LOG("Commit result for file %s is: %s", computed_path.c_str(), + commit_computed ? "committed" : "not committed"); + return commit_computed; + } + return std::filesystem::exists(computed_path); } } diff --git a/src/server/file-manager/fs_monitor.hpp b/src/server/file-manager/fs_monitor.hpp index 03d9763ae..816dcd383 100644 --- a/src/server/file-manager/fs_monitor.hpp +++ b/src/server/file-manager/fs_monitor.hpp @@ -33,7 +33,7 @@ class FileSystemMonitor { } } - for (const auto &file : file_manager->get_file_awaiting_data()) { + for (auto &file : file_manager->get_file_awaiting_data()) { if (std::filesystem::exists(file)) { LOG("File %s exists. Checking if enough data is available", file.c_str()); // actual update, end eventual removal from map is handled by the From aa2824c928a1d3ff78b84a63bd774ddaea65bbff Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Sat, 21 Sep 2024 10:59:27 +0000 Subject: [PATCH 019/151] Support for CoC:n --- .github/workflows/ci-tests.yaml | 98 ++++++++--------- src/capioctl/capioctl.cpp | 4 + src/common/capio/constants.hpp | 2 +- src/common/capio/env.hpp | 10 -- src/common/capio/shm.hpp | 15 +-- .../capio-cl-engine/capio_cl_engine.hpp | 62 +++++++---- src/server/capio-cl-engine/json_parser.hpp | 91 +++++++++------- src/server/capio_server.cpp | 23 ++-- src/server/client-manager/client_manager.hpp | 4 +- src/server/client-manager/handlers/close.hpp | 17 ++- .../client-manager/handlers/consent.hpp | 13 +-- src/server/client-manager/handlers/create.hpp | 8 +- src/server/client-manager/handlers/exit.hpp | 7 +- src/server/client-manager/handlers/open.hpp | 4 +- src/server/client-manager/handlers/read.hpp | 15 +-- src/server/client-manager/handlers/rename.hpp | 2 +- src/server/client-manager/handlers/write.hpp | 4 +- .../client-manager/request_handler_engine.hpp | 12 +-- src/server/file-manager/file_manager.hpp | 21 ++-- src/server/file-manager/file_manager_impl.hpp | 101 ++++++++++++++---- src/server/file-manager/fs_monitor.hpp | 10 +- src/server/utils/distributed_semaphore.hpp | 3 +- src/server/utils/signals.hpp | 19 ++-- tests/unit/syscall/src/clone.cpp | 2 +- 24 files changed, 316 insertions(+), 231 deletions(-) diff --git a/.github/workflows/ci-tests.yaml b/.github/workflows/ci-tests.yaml index 69a573644..98b544d73 100644 --- a/.github/workflows/ci-tests.yaml +++ b/.github/workflows/ci-tests.yaml @@ -17,59 +17,49 @@ jobs: - uses: actions/checkout@v4 - name: "Run codespell" uses: codespell-project/actions-codespell@v2 -# docker-check: -# name: "Check Docker image" -# runs-on: ubuntu-22.04 -# strategy: -# matrix: -# cmake-build-type: -# - "Debug" -# - "Release" -# steps: -# - uses: actions/checkout@v4 -# - uses: docker/setup-qemu-action@v3 -# - uses: docker/setup-buildx-action@v3 -# - name: "Build Docker image" -# uses: docker/build-push-action@v5 -# with: -# build-args: | -# CAPIO_BUILD_TESTS=ON -# CAPIO_LOG=${{ matrix.cmake-build-type == 'Debug' && 'ON' || 'OFF' }} -# CMAKE_BUILD_TYPE=${{ matrix.cmake-build-type }} -# load: true -# tags: alphaunito/capio:latest -# - name: "Run unit tests with Docker" -# run: | -# echo "Run CAPIO POSIX Unit tests" -# docker run --rm \ -# --env CAPIO_DIR=/tmp \ -# --env CAPIO_LOG_LEVEL=-1 \ -# --name capio-docker \ -# alphaunito/capio:latest \ -# capio_posix_unit_tests \ -# --gtest_break_on_failure \ -# --gtest_print_time=1 -# -# echo "Run CAPIO server Unit tests" -# docker run --rm \ -# --env CAPIO_DIR=/tmp \ -# --env CAPIO_LOG_LEVEL=-1 \ -# --name capio-docker \ -# alphaunito/capio:latest \ -# capio_server_unit_tests \ -# --gtest_break_on_failure \ -# --gtest_print_time=1 -# -# echo "Run CAPIO integration tests" -# docker run --rm \ -# --env CAPIO_DIR=/tmp \ -# --env CAPIO_LOG_LEVEL=-1 \ -# --env LD_PRELOAD=libcapio_posix.so \ -# --name capio-docker \ -# alphaunito/capio:latest \ -# capio_integration_tests \ -# --gtest_break_on_failure \ -# --gtest_print_time=1 + docker-check: + name: "Check Docker image" + runs-on: ubuntu-22.04 + strategy: + matrix: + cmake-build-type: + - "Debug" + - "Release" + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-qemu-action@v3 + - uses: docker/setup-buildx-action@v3 + - name: "Build Docker image" + uses: docker/build-push-action@v5 + with: + build-args: | + CAPIO_BUILD_TESTS=ON + CAPIO_LOG=${{ matrix.cmake-build-type == 'Debug' && 'ON' || 'OFF' }} + CMAKE_BUILD_TYPE=${{ matrix.cmake-build-type }} + load: true + tags: alphaunito/capio:latest + - name: "Run unit tests with Docker" + run: | + echo "Run CAPIO POSIX Unit tests" + docker run --rm \ + --env CAPIO_DIR=/tmp \ + --env CAPIO_LOG_LEVEL=-1 \ + --name capio-docker \ + alphaunito/capio:latest \ + capio_posix_unit_tests \ + --gtest_break_on_failure \ + --gtest_print_time=1 + + echo "Run CAPIO integration tests" + docker run --rm \ + --env CAPIO_DIR=/tmp \ + --env CAPIO_LOG_LEVEL=-1 \ + --env LD_PRELOAD=libcapio_posix.so \ + --name capio-docker \ + alphaunito/capio:latest \ + capio_integration_tests \ + --gtest_break_on_failure \ + --gtest_print_time=1 format-check: name: "Check ${{ matrix.path }} clang-format conformance" @@ -153,7 +143,7 @@ jobs: sudo cmake --install ../build --prefix /usr/local - name: "Run tests" id: run-tests - timeout-minutes: 2 + timeout-minutes: 1 env: CAPIO_DIR: ${{ github.workspace }} CAPIO_LOG_LEVEL: -1 diff --git a/src/capioctl/capioctl.cpp b/src/capioctl/capioctl.cpp index 5dad115b9..754aa6b4d 100644 --- a/src/capioctl/capioctl.cpp +++ b/src/capioctl/capioctl.cpp @@ -20,6 +20,10 @@ #include #include "args.hxx" +#include +#include +std::string workflow_name; +char node_name[HOST_NAME_MAX]; #include "capio/queue.hpp" diff --git a/src/common/capio/constants.hpp b/src/common/capio/constants.hpp index 8a5f8d98d..a7edc2ce7 100644 --- a/src/common/capio/constants.hpp +++ b/src/common/capio/constants.hpp @@ -132,7 +132,7 @@ constexpr char CAPIO_SERVER_ARG_PARSER_EPILOGUE[] = "For further help, a full list of the available ENVIRONMENT VARIABLES," " and a guide on config JSON file structure, please visit " "https://github.com/High-Performance-IO/capio"; -constexpr char CAPIO_SERVER_ARG_PARSER_PRE_COMMAND[] = "{ENVIRONMENT_VARS} mpirun -n 1"; +constexpr char CAPIO_SERVER_ARG_PARSER_PRE_COMMAND[] = "{ENVIRONMENT_VARS} "; constexpr char CAPIO_SERVER_ARG_PARSER_LOGILE_DIR_OPT_HELP[] = "Name of the folder to which CAPIO server will put log files into"; constexpr char CAPIO_SERVER_ARG_PARSER_LOGILE_OPT_HELP[] = diff --git a/src/common/capio/env.hpp b/src/common/capio/env.hpp index 50bb565d5..ae33b4a98 100644 --- a/src/common/capio/env.hpp +++ b/src/common/capio/env.hpp @@ -22,20 +22,12 @@ const std::filesystem::path &get_capio_dir() { auto buf = std::unique_ptr(new char[PATH_MAX]); if (val == nullptr) { - - std::cout << "\n" - << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << "Fatal: CAPIO_DIR not provided!" - << std::endl; ERR_EXIT("Fatal: CAPIO_DIR not provided!"); } else { const char *realpath_res = capio_realpath(val, buf.get()); if (realpath_res == nullptr) { - std::cout << "\n" - << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR - << "Fatal: CAPIO_DIR set, but folder does not exists on filesystem!" - << std::endl; ERR_EXIT("error CAPIO_DIR: directory %s does not " "exist. [buf=%s]", val, buf.get()); @@ -98,8 +90,6 @@ inline int get_capio_log_level() { } else { auto [ptr, ec] = std::from_chars(log_level, log_level + strlen(log_level), level); if (ec != std::errc()) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "invalid CAPIO_LOG_LEVEL value" - << std::endl; level = 0; } } diff --git a/src/common/capio/shm.hpp b/src/common/capio/shm.hpp index 7b39d5427..abd85198e 100644 --- a/src/common/capio/shm.hpp +++ b/src/common/capio/shm.hpp @@ -27,15 +27,16 @@ #define SHM_DESTROY_CHECK(source_name) \ if (shm_unlink(source_name) == -1) { \ - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "Unable to destroy shared mem: '" \ - << source_name << "' (" << strerror(errno) << ")" << std::endl; \ + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name << " ] " \ + << "Unable to destroy shared mem: '" << source_name << "' (" << strerror(errno) \ + << ")" << std::endl; \ }; #define SHM_CREATE_CHECK(condition, source) \ if (condition) { \ LOG("error while creating %s", source); \ - std::cout << CAPIO_SERVER_CLI_LOG_SERVER_ERROR << "Unable to create shm: " << source \ - << std::endl; \ + std::cout << CAPIO_SERVER_CLI_LOG_SERVER_ERROR << " [ " << node_name << " ] " \ + << "Unable to create shm: " << source << std::endl; \ ERR_EXIT("Unable to open shm: %s", source); \ }; @@ -66,9 +67,9 @@ class CapioShmCanary { ~CapioShmCanary() { START_LOG(capio_syscall(SYS_gettid), "call()"); -#ifndef __CAPIO_POSIx - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "Removing shared memory canary flag" - << std::endl; +#ifndef __CAPIO_POSIX + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name << " ] " + << "Removing shared memory canary flag" << std::endl; #endif close(_shm_id); SHM_DESTROY_CHECK(_canary_name.c_str()); diff --git a/src/server/capio-cl-engine/capio_cl_engine.hpp b/src/server/capio-cl-engine/capio_cl_engine.hpp index fb14b5b2d..7d31b573b 100644 --- a/src/server/capio-cl-engine/capio_cl_engine.hpp +++ b/src/server/capio-cl-engine/capio_cl_engine.hpp @@ -14,18 +14,18 @@ class CapioCLEngine { bool, // permanent [4] bool, // exclude [5] bool, // is_file (if true yes otherwise it is a directory) [6] - int, // commit on file number [7] + int, // commit on close number [7] long, // directory file count [8] std::vector>> // File dependencies [9] _locations; - static std::string truncate_last_n(const std::string &str, int n) { + static std::string truncateLastN(const std::string &str, int n) { return str.length() > n ? "[..] " + str.substr(str.length() - n) : str; } public: void print() const { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " << "Composition of expected CAPIO FS: " << std::endl << std::endl << "|============================================================================" @@ -48,7 +48,7 @@ class CapioCLEngine { "============|============|===========|=========|" << std::endl; for (auto itm : _locations) { - std::string name_trunc = truncate_last_n(itm.first, 12); + std::string name_trunc = truncateLastN(itm.first, 12); auto kind = std::get<6>(itm.second) ? "F" : "D"; std::cout << "| " << kind << " | " << name_trunc << std::setfill(' ') << std::setw(20 - name_trunc.length()) << "| "; @@ -65,7 +65,7 @@ class CapioCLEngine { } if (i < producers.size()) { - auto prod1 = truncate_last_n(producers.at(i), 12); + auto prod1 = truncateLastN(producers.at(i), 12); std::cout << prod1 << std::setfill(' ') << std::setw(20 - prod1.length()) << " | "; } else { @@ -73,7 +73,7 @@ class CapioCLEngine { } if (i < consumers.size()) { - auto cons1 = truncate_last_n(consumers.at(i), 12); + auto cons1 = truncateLastN(consumers.at(i), 12); std::cout << " " << cons1 << std::setfill(' ') << std::setw(20 - cons1.length()) << " | "; } else { @@ -105,20 +105,18 @@ class CapioCLEngine { }; // TODO: might need to be improved - bool file_to_be_handled(std::filesystem::path::iterator::reference path) const { + static bool fileToBeHandled(std::filesystem::path::iterator::reference path) { START_LOG(gettid(), "call(path=%s)", path.c_str()); - for (const auto &entry : _locations) { - auto capio_path = entry.first; - if (path == capio_path) { - return true; - } - if (capio_path.find('*') != std::string::npos) { // check for globs - if (capio_path.find(path) == 0) { // if path and capio_path begins in the same way - return true; - } - } + + if (path == get_capio_dir()) { + LOG("Path is capio_dir. Ignoring."); + return false; } - return false; + + LOG("Parent path=%s", path.parent_path().c_str()); + LOG("Path %s be handled by CAPIO", + path.parent_path().string().rfind(get_capio_dir(), 0) == 0 ? "SHOULD" : "SHOULD NOT"); + return path.parent_path().string().rfind(get_capio_dir(), 0) == 0; }; void add(std::string &path, std::vector &producers, @@ -169,6 +167,7 @@ class CapioCLEngine { void addProducer(const std::string &path, std::string &producer) { START_LOG(gettid(), "call(path=%s, producer=%s)", path.c_str(), producer.c_str()); producer.erase(remove_if(producer.begin(), producer.end(), isspace), producer.end()); + newFile(path); std::get<0>(_locations.at(path)).emplace_back(producer); } @@ -185,8 +184,12 @@ class CapioCLEngine { std::string getCommitRule(const std::string &path) { START_LOG(gettid(), "call(path=%s)", path.c_str()); - LOG("Fire rule: %s", std::get<2>(_locations.at(path)).c_str()); - return std::get<2>(_locations.at(path)); + if (_locations.find(path) != _locations.end()) { + LOG("Commit rule: %s", std::get<2>(_locations.at(path)).c_str()); + return std::get<2>(_locations.at(path)); + } + LOG("File not present in config file. Returning default rule."); + return CAPIO_FILE_COMMITTED_ON_TERMINATION; } void setFireRule(const std::string &path, const std::string &fire_rule) { @@ -277,6 +280,11 @@ class CapioCLEngine { if (match_globs(k, path)) { LOG("Found possible glob match"); std::vector producers = std::get<0>(_locations.at(k)); + DBG(gettid(), [&](std::vector &arr) { + for (auto itm : arr) { + LOG("producer: %s", itm.c_str()); + } + }(producers)); return std::find(producers.begin(), producers.end(), app_name) != producers.end(); } } @@ -284,8 +292,8 @@ class CapioCLEngine { return false; } - void set_file_deps(const std::filesystem::path &path, - const std::vector &dependencies) { + void setFileDeps(const std::filesystem::path &path, + const std::vector &dependencies) { START_LOG(gettid(), "call()"); std::get<9>(_locations.at(path)) = dependencies; for (const auto &itm : dependencies) { @@ -294,6 +302,16 @@ class CapioCLEngine { } } + int getCommitCloseCount(std::filesystem::path::iterator::reference path) const { + START_LOG(gettid(), "call(path=%s)", path.c_str()); + int count = 0; + if (_locations.find(path) != _locations.end()) { + count = std::get<7>(_locations.at(path)); + } + LOG("Expected number on close to commit file: %d", count); + return count; + }; + // todo fix leak std::vector get_file_deps(const std::filesystem::path &path) { return std::get<9>(_locations.at(path)); diff --git a/src/server/capio-cl-engine/json_parser.hpp b/src/server/capio-cl-engine/json_parser.hpp index bd86e8f66..11f1b476b 100644 --- a/src/server/capio-cl-engine/json_parser.hpp +++ b/src/server/capio-cl-engine/json_parser.hpp @@ -28,7 +28,7 @@ class JsonParser { try { json = simdjson::padded_string::load(source.c_str()); } catch (const simdjson::simdjson_error &e) { - std::cerr << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + std::cerr << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << " [ " << node_name << " ] " << "Exception thrown while opening config file: " << e.what() << std::endl; LOG("Exception thrown while opening config file: %s", e.what()); ERR_EXIT("Exception thrown while opening config file: %s", e.what()); @@ -41,11 +41,11 @@ class JsonParser { ERR_EXIT("Error: workflow name is mandatory"); } workflow_name = std::string(wf_name); - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " << "Parsing configuration for workflow: " << workflow_name << std::endl; LOG("Parsing configuration for workflow: %s", std::string(workflow_name).c_str()); - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " << std::endl; auto io_graph = entries["IO_Graph"]; @@ -56,41 +56,44 @@ class JsonParser { ERR_EXIT("Error: app name is mandatory"); } - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Parsing config for app " << app_name - << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " + << "Parsing config for app " << app_name << std::endl; LOG("Parsing config for app %s", std::string(app_name).c_str()); if (app["input_stream"].get_array().get(input_stream)) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << " [ " << node_name << " ] " << "No input_stream section found for app " << app_name << std::endl; ERR_EXIT("No input_stream section found for app %s", std::string(app_name).c_str()); } else { for (auto itm : input_stream) { std::filesystem::path file(itm.get_string().take_value()); - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Found file : " << file - << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " + << "Found file : " << file << std::endl; if (file.is_relative() || first_is_subpath_of_second(file, get_capio_dir())) { std::string appname(app_name); - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "File : " << file - << " added to app: " << app_name << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " + << "File : " << file << " added to app: " << app_name + << std::endl; if (file.is_relative()) { file = capio_dir / file; } locations->newFile(file.c_str()); locations->addConsumer(file, appname); } else { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "File : " << file + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name + << " ] " + << "File : " << file << " is not relative to CAPIO_DIR. Ignoring..." << std::endl; } } - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " << "Completed input_stream parsing for app: " << app_name << std::endl; LOG("Completed input_stream parsing for app: %s", std::string(app_name).c_str()); } if (app["output_stream"].get_array().get(output_stream)) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << " [ " << node_name << " ] " << "No output_stream section found for app " << app_name << std::endl; ERR_EXIT("No output_stream section found for app %s", std::string(app_name).c_str()); @@ -99,8 +102,9 @@ class JsonParser { std::filesystem::path file(itm.get_string().take_value()); if (file.is_relative() || first_is_subpath_of_second(file, get_capio_dir())) { std::string appname(app_name); - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Adding file: " << file - << " to app: " << app_name << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " + << "Adding file: " << file << " to app: " << app_name + << std::endl; if (file.is_relative()) { file = capio_dir / file; } @@ -108,14 +112,14 @@ class JsonParser { locations->addProducer(file, appname); } } - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " << "Completed output_stream parsing for app: " << app_name << std::endl; LOG("Completed output_stream parsing for app: %s", std::string(app_name).c_str()); } // PARSING STREAMING FILES if (app["streaming"].get_array().get(streaming)) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name << " ] " << "No streaming section found for app: " << app_name << std::endl; LOG("No streaming section found for app: %s", std::string(app_name).c_str()); } else { @@ -134,7 +138,7 @@ class JsonParser { error = file["dirname"].get_array().get(name); if (error || name.is_empty()) { std::cout - << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << " [ " << node_name << " ] " << "error: either name or dirname in streaming section is required" << std::endl; ERR_EXIT( @@ -157,7 +161,7 @@ class JsonParser { // PARSING COMMITTED error = file["committed"].get_string().get(committed); if (error) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << " [ " << node_name << " ] " << "commit rule is mandatory in streaming section" << std::endl; ERR_EXIT("error commit rule is mandatory in streaming section"); } else { @@ -165,15 +169,17 @@ class JsonParser { if (pos != std::string::npos) { commit_rule = committed.substr(0, pos); if (commit_rule != CAPIO_FILE_COMMITTED_ON_CLOSE) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << "commit rule " - << commit_rule << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << " [ " << node_name + << " ] " + << "commit rule " << commit_rule << std::endl; ERR_EXIT("error commit rule: %s", std::string(commit_rule).c_str()); } std::string n_close_str(committed.substr(pos + 1, committed.length())); if (!is_int(n_close_str)) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << " [ " << node_name + << " ] " << "commit rule on_close invalid number" << std::endl; ERR_EXIT("error commit rule on_close invalid number: !is_int()"); } @@ -189,7 +195,8 @@ class JsonParser { error = file["file_deps"].get_array().get(file_deps_tmp); if (error) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << " [ " << node_name + << " ] " << "commit rule is on_file but no file_deps section found" << std::endl; ERR_EXIT("commit rule is on_file but no file_deps section found"); @@ -202,7 +209,8 @@ class JsonParser { computed_path = computed_path.is_relative() ? (get_capio_dir() / computed_path) : computed_path; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name + << " ] " << "Adding file: " << computed_path << " to file dependencies: " << std::endl; file_deps.emplace_back(computed_path); @@ -230,7 +238,8 @@ class JsonParser { } LOG("batch_size: %d", batch_size); for (auto path : streaming_names) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " + << " [ " << node_name << " ] " << "Updating metadata for path: " << path << std::endl; if (path.is_relative()) { path = (capio_dir / path).lexically_normal(); @@ -240,7 +249,8 @@ class JsonParser { // TODO: check for globs std::string commit(commit_rule), firerule(mode); if (n_files != -1) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name + << " ] " << "Setting path: " << path << " n_files to " << n_files << std::endl; locations->setDirectoryFileCount(path, n_files); @@ -250,25 +260,26 @@ class JsonParser { locations->setCommitRule(path, commit); locations->setFireRule(path, firerule); locations->setCommitedNumber(path, n_close); - locations->set_file_deps(path, file_deps); + locations->setFileDeps(path, file_deps); } } - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " << "completed parsing of streaming section for app: " << app_name << std::endl; LOG("completed parsing of streaming section for app: %s", std::string(app_name).c_str()); } // END PARSING STREAMING FILES - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " + << std::endl; } // END OF APP MAIN LOOPS LOG("Completed parsing of io_graph app main loops"); - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Completed parsing of io_graph" - << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " + << "Completed parsing of io_graph" << std::endl; LOG("Completed parsing of io_graph"); if (entries["permanent"].get_array().get(permanent_files)) { // PARSING PERMANENT FILES - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name << " ] " << "No permanent section found for workflow: " << workflow_name << std::endl; LOG("No permanent section found for workflow: %s", std::string(workflow_name).c_str()); } else { @@ -290,15 +301,15 @@ class JsonParser { locations->setPermanent(name.data(), true); } } - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Completed parsing of permanent files" - << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " + << "Completed parsing of permanent files" << std::endl; LOG("Completed parsing of permanent files"); } // END PARSING PERMANENT FILES - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " << std::endl; if (entries["exclude"].get_array().get(exclude_files)) { // PARSING PERMANENT FILES - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name << " ] " << "No exclude section found for workflow: " << workflow_name << std::endl; LOG("No exclude section found for workflow: %s", std::string(workflow_name).c_str()); } else { @@ -320,16 +331,16 @@ class JsonParser { locations->setExclude(name.data(), true); } } - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << "Completed parsing of exclude files" - << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " + << "Completed parsing of exclude files" << std::endl; LOG("Completed parsing of exclude files"); } // END PARSING PERMANENT FILES - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_JSON << " [ " << node_name << " ] " << std::endl; auto home_node_policies = entries["home_node_policy"].error(); if (!home_node_policies) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name << " ] " << "Warning: capio does not support home node policies yet! skipping section " << std::endl; } diff --git a/src/server/capio_server.cpp b/src/server/capio_server.cpp index ce1a3fb3e..6de64504e 100644 --- a/src/server/capio_server.cpp +++ b/src/server/capio_server.cpp @@ -121,19 +121,19 @@ std::string parseCLI(int argc, char **argv) { if (config) { std::string token = args::get(config); - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << "parsing config file: " << token - << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << " [ " << node_name << " ] " + << "parsing config file: " << token << std::endl; // TODO: pass config file path } else if (noConfigFile) { workflow_name = std::string_view(get_capio_workflow_name()); - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "skipping config file parsing." - << std::endl - << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name << " ] " + << "skipping config file parsing." << std::endl + << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name << " ] " << "Obtained from environment variable current workflow name: " << workflow_name.data() << std::endl; } else { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << " [ " << node_name << " ] " << "Error: no config file provided. To skip config file use --no-config option!" << std::endl; #ifdef CAPIO_LOG @@ -142,13 +142,13 @@ std::string parseCLI(int argc, char **argv) { exit(EXIT_FAILURE); } - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << "CAPIO_DIR=" << get_capio_dir().c_str() - << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << " [ " << node_name << " ] " + << "CAPIO_DIR=" << get_capio_dir().c_str() << std::endl; #ifdef CAPIO_LOG CAPIO_LOG_LEVEL = get_capio_log_level(); - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << "LOG_LEVEL set to: " << CAPIO_LOG_LEVEL - << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << " [ " << node_name << " ] " + << "LOG_LEVEL set to: " << CAPIO_LOG_LEVEL << std::endl; std::cout << CAPIO_LOG_SERVER_CLI_LOGGING_ENABLED_WARNING; log->log("LOG_LEVEL set to: %d", CAPIO_LOG_LEVEL); delete log; @@ -159,7 +159,8 @@ std::string parseCLI(int argc, char **argv) { } #endif - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << "server initialization completed!" << std::endl + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << " [ " << node_name << " ] " + << "server initialization completed!" << std::endl << std::flush; if (config) { diff --git a/src/server/client-manager/client_manager.hpp b/src/server/client-manager/client_manager.hpp index abc707af6..ca09dd51c 100644 --- a/src/server/client-manager/client_manager.hpp +++ b/src/server/client-manager/client_manager.hpp @@ -24,8 +24,8 @@ class ClientManager { delete bufs_response; delete app_names; delete files_to_be_committed_by_tid; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_response cleanup completed" - << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name << " ] " + << "buf_response cleanup completed" << std::endl; } /** diff --git a/src/server/client-manager/handlers/close.hpp b/src/server/client-manager/handlers/close.hpp index 0b145c705..199263c20 100644 --- a/src/server/client-manager/handlers/close.hpp +++ b/src/server/client-manager/handlers/close.hpp @@ -10,11 +10,24 @@ inline void close_handler(const char *const str) { std::filesystem::path filename(path); - if (path == get_capio_dir() || !capio_cl_engine->file_to_be_handled(filename)) { + if (!CapioCLEngine::fileToBeHandled(filename)) { + LOG("File should not be handled"); return; } - file_manager->set_committed(path); + LOG("File needs handling"); + + // Call the set_committed method only if the commit rule is on_close + if (capio_cl_engine->getCommitRule(filename) == CAPIO_FILE_COMMITTED_ON_CLOSE) { + file_manager->setCommitted(path); + + // The increase close count is called only on explicit close() sc, as defined by the + // CAPIO-CL specification. If it were to be called every time the file is committed, then + // an extra increase would occur as by default, at termination all files are committed. + // By calling this only when close sc are occurred, we guarantee the correct count of + // how many close sc occurs. + CapioFileManager::increaseCloseCount(path); + } } #endif // CAPIO_CLOSE_HPP diff --git a/src/server/client-manager/handlers/consent.hpp b/src/server/client-manager/handlers/consent.hpp index f60f923ab..30eb3d779 100644 --- a/src/server/client-manager/handlers/consent.hpp +++ b/src/server/client-manager/handlers/consent.hpp @@ -13,14 +13,7 @@ inline void consent_to_proceed_handler(const char *const str) { std::filesystem::path path_fs(path); // Skip operations on CAPIO_DIR - // TODO: check if it is coherent with CAPIO_CL - if (path_fs == get_capio_dir()) { - LOG("Ignore calls on exactly CAPIO_DIR"); - client_manager->reply_to_client(tid, 1); - return; - } - - if (!capio_cl_engine->file_to_be_handled(path_fs)) { + if (!CapioCLEngine::fileToBeHandled(path_fs)) { LOG("Ignore calls as file should not be treated by CAPIO"); client_manager->reply_to_client(tid, 1); return; @@ -30,7 +23,7 @@ inline void consent_to_proceed_handler(const char *const str) { // NOTE: expression is (exists AND (committed OR no_update)) OR is_producer bool exists = std::filesystem::exists(path); - bool committed = CapioFileManager::is_committed(path); + bool committed = CapioFileManager::isCommitted(path); bool firable = capio_cl_engine->getFireRule(path) == CAPIO_FILE_MODE_NO_UPDATE; bool is_producer = capio_cl_engine->isProducer(path, tid); LOG("exists=%s, committed=%s, firable=%s, is_producer=%s", exists ? "true" : "false", @@ -40,7 +33,7 @@ inline void consent_to_proceed_handler(const char *const str) { client_manager->reply_to_client(tid, 1); } else { LOG("Requested file %s does not exists yet. awaiting for creation", path); - file_manager->add_thread_awaiting_data(path, tid, 0); + file_manager->addThreadAwaitingData(path, tid, 0); } } diff --git a/src/server/client-manager/handlers/create.hpp b/src/server/client-manager/handlers/create.hpp index 40861562e..ab0865165 100644 --- a/src/server/client-manager/handlers/create.hpp +++ b/src/server/client-manager/handlers/create.hpp @@ -2,11 +2,13 @@ #define CAPIO_CREATE_HPP inline void create_handler(const char *const str) { - pid_t tid; + pid_t tid, fd; char path[PATH_MAX]; - sscanf(str, "%d %s", &tid, path); + sscanf(str, "%d %d %s", &tid, &fd, path); START_LOG(gettid(), "call(tid=%d, path=%s)", tid, path); - file_manager->unlock_thread_awaiting_creation(path); + file_manager->unlockThreadAwaitingCreation(path); + std::string name(client_manager->get_app_name(tid)); + capio_cl_engine->addProducer(path, name); } #endif // CAPIO_CREATE_HPP diff --git a/src/server/client-manager/handlers/exit.hpp b/src/server/client-manager/handlers/exit.hpp index 365bce71d..f6ca0c07b 100644 --- a/src/server/client-manager/handlers/exit.hpp +++ b/src/server/client-manager/handlers/exit.hpp @@ -6,7 +6,12 @@ inline void exit_handler(const char *const str) { pid_t tid; sscanf(str, "%d", &tid); START_LOG(gettid(), "call(tid=%d)", tid); - file_manager->set_committed(tid); + + // At exit, all files are considered to be committed. hence, call the set_committed + // method. The increase_close_count method is not called, as it would add a close count + // to a file that might have already been closing (hence increasing the close count by an extra + // close + file_manager->setCommitted(tid); client_manager->remove_client(tid); } diff --git a/src/server/client-manager/handlers/open.hpp b/src/server/client-manager/handlers/open.hpp index 7501a5414..291785403 100644 --- a/src/server/client-manager/handlers/open.hpp +++ b/src/server/client-manager/handlers/open.hpp @@ -7,10 +7,10 @@ inline void open_handler(const char *const str) { sscanf(str, "%d %d %s", &tid, &fd, path); START_LOG(gettid(), "call(tid=%d, fd=%d, path=%s", tid, fd, path); - if (std::filesystem::exists(path)) { + if (std::filesystem::exists(path) || capio_cl_engine->isProducer(path, tid)) { client_manager->reply_to_client(tid, 1); } else { - file_manager->add_thread_awaiting_creation(path, tid); + file_manager->addThreadAwaitingCreation(path, tid); } } #endif // OPEN_HPP diff --git a/src/server/client-manager/handlers/read.hpp b/src/server/client-manager/handlers/read.hpp index 8dba63680..1d363736c 100644 --- a/src/server/client-manager/handlers/read.hpp +++ b/src/server/client-manager/handlers/read.hpp @@ -12,28 +12,21 @@ inline void read_handler(const char *const str) { std::filesystem::path path_fs(path); // Skip operations on CAPIO_DIR - // TODO: check if it is coherent with CAPIO_CL - if (path_fs == get_capio_dir()) { - LOG("Ignore calls on exactly CAPIO_DIR"); - client_manager->reply_to_client(tid, 1); - return; - } - - if (!capio_cl_engine->file_to_be_handled(path_fs)) { + if (!CapioCLEngine::fileToBeHandled(path_fs)) { LOG("Ignore calls as file should not be treated by CAPIO"); client_manager->reply_to_client(tid, 1); return; } - auto is_committed = CapioFileManager::is_committed(path); + auto is_committed = CapioFileManager::isCommitted(path); auto file_size = std::filesystem::file_size(path); // return ULLONG_MAX to signal client cache that file is committed and no more requests are // required - if (file_size >= end_of_read || is_committed) { + if (file_size >= end_of_read || is_committed || capio_cl_engine->isProducer(path, tid)) { client_manager->reply_to_client(tid, is_committed ? ULLONG_MAX : file_size); } else { - file_manager->add_thread_awaiting_data(path, tid, end_of_read); + file_manager->addThreadAwaitingData(path, tid, end_of_read); } } diff --git a/src/server/client-manager/handlers/rename.hpp b/src/server/client-manager/handlers/rename.hpp index 3131853e2..b1ad7cc15 100644 --- a/src/server/client-manager/handlers/rename.hpp +++ b/src/server/client-manager/handlers/rename.hpp @@ -6,7 +6,7 @@ inline void rename_handler(const char *const str) { char old_path[PATH_MAX], new_path[PATH_MAX]; sscanf(str, "%d %s %s", &tid, old_path, new_path); START_LOG(gettid(), "call(tid=%d, old=%s, new=%s)", tid, old_path, new_path); - file_manager->unlock_thread_awaiting_creation(new_path); + file_manager->unlockThreadAwaitingCreation(new_path); // TODO: gestire le rename? } diff --git a/src/server/client-manager/handlers/write.hpp b/src/server/client-manager/handlers/write.hpp index a38629b89..4d4d0a552 100644 --- a/src/server/client-manager/handlers/write.hpp +++ b/src/server/client-manager/handlers/write.hpp @@ -12,12 +12,12 @@ inline void write_handler(const char *const str) { START_LOG(gettid(), "call(tid=%d, fd=%d, path=%s, count=%llu)", tid, fd, path, write_size); std::filesystem::path filename(path); - if (path == get_capio_dir() || !capio_cl_engine->file_to_be_handled(filename)) { + if (!CapioCLEngine::fileToBeHandled(filename)) { return; } LOG("File needs to be handled"); - file_manager->check_and_unlock_thread_awaiting_data(path); + file_manager->checkAndUnlockThreadAwaitingData(path); } #endif // WRITE_HPP diff --git a/src/server/client-manager/request_handler_engine.hpp b/src/server/client-manager/request_handler_engine.hpp index efdc8cbcf..9d7ecd5e8 100644 --- a/src/server/client-manager/request_handler_engine.hpp +++ b/src/server/client-manager/request_handler_engine.hpp @@ -56,8 +56,8 @@ class RequestHandlerEngine { if (ec == std::errc()) { strcpy(str, ptr + 1); } else { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << "Received invalid code: " << code - << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << " [ " << node_name << " ] " + << "Received invalid code: " << code << std::endl; ERR_EXIT("Invalid request %d%s", code, ptr); } return code; @@ -80,8 +80,8 @@ class RequestHandlerEngine { START_LOG(gettid(), "call()"); delete buf_requests; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "buf_requests cleanup completed" - << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name << " ] " + << "buf_requests cleanup completed" << std::endl; } [[noreturn]] void start() { @@ -92,8 +92,8 @@ class RequestHandlerEngine { LOG(CAPIO_LOG_SERVER_REQUEST_START); int code = read_next_request(str.get()); if (code < 0 || code > CAPIO_NR_REQUESTS) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << "Received invalid code: " << code - << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << " [ " << node_name << " ] " + << "Received invalid code: " << code << std::endl; ERR_EXIT("Error: received invalid request code"); } diff --git a/src/server/file-manager/file_manager.hpp b/src/server/file-manager/file_manager.hpp index 83ae0589c..253fc56e5 100644 --- a/src/server/file-manager/file_manager.hpp +++ b/src/server/file-manager/file_manager.hpp @@ -25,16 +25,17 @@ class CapioFileManager { delete thread_awaiting_data; } - void set_committed(const std::filesystem::path &path) const; - void set_committed(pid_t tid) const; - static bool is_committed(const std::filesystem::path &path); - void check_and_unlock_thread_awaiting_data(const std::string &path) const; - void add_thread_awaiting_data(std::string path, int tid, size_t expected_size) const; - void unlock_thread_awaiting_creation(std::string path) const; - void add_thread_awaiting_creation(std::string path, pid_t tid) const; - void delete_file_awaiting_creation(std::string path) const; - [[nodiscard]] std::vector get_file_awaiting_creation() const; - [[nodiscard]] std::vector get_file_awaiting_data() const; + void setCommitted(const std::filesystem::path &path) const; + void setCommitted(pid_t tid) const; + static bool isCommitted(const std::filesystem::path &path); + void checkAndUnlockThreadAwaitingData(const std::string &path) const; + static void increaseCloseCount(const std::filesystem::path &path); + void addThreadAwaitingData(std::string path, int tid, size_t expected_size) const; + void unlockThreadAwaitingCreation(std::string path) const; + void addThreadAwaitingCreation(std::string path, pid_t tid) const; + void deleteFileAwaitingCreation(std::string path) const; + [[nodiscard]] std::vector getFileAwaitingCreation() const; + [[nodiscard]] std::vector getFileAwaitingData() const; }; CapioFileManager *file_manager; diff --git a/src/server/file-manager/file_manager_impl.hpp b/src/server/file-manager/file_manager_impl.hpp index 648026ee1..d93bafce1 100644 --- a/src/server/file-manager/file_manager_impl.hpp +++ b/src/server/file-manager/file_manager_impl.hpp @@ -2,15 +2,16 @@ #define FILE_MANAGER_HPP #include "client-manager/client_manager.hpp" #include "file_manager.hpp" +#include "utils/distributed_semaphore.hpp" -inline void CapioFileManager::add_thread_awaiting_creation(std::string path, pid_t tid) const { +inline void CapioFileManager::addThreadAwaitingCreation(std::string path, pid_t tid) const { START_LOG(gettid(), "call(path=%s, tid=%ld)", path.c_str(), tid); std::lock_guard lg(threads_mutex); thread_awaiting_file_creation->try_emplace(path, new std::vector); thread_awaiting_file_creation->at(path)->emplace_back(tid); } -inline void CapioFileManager::unlock_thread_awaiting_creation(std::string path) const { +inline void CapioFileManager::unlockThreadAwaitingCreation(std::string path) const { START_LOG(gettid(), "call(path=%s)", path.c_str()); std::lock_guard lg(threads_mutex); if (thread_awaiting_file_creation->find(path) != thread_awaiting_file_creation->end()) { @@ -22,15 +23,15 @@ inline void CapioFileManager::unlock_thread_awaiting_creation(std::string path) thread_awaiting_file_creation->erase(path); } -inline void CapioFileManager::delete_file_awaiting_creation(std::string path) const { +inline void CapioFileManager::deleteFileAwaitingCreation(std::string path) const { START_LOG(gettid(), "call(path=%s)", path.c_str()); std::lock_guard lg(threads_mutex); thread_awaiting_file_creation->erase(path); } // register tid to wait for file size of certain size -inline void CapioFileManager::add_thread_awaiting_data(std::string path, int tid, - size_t expected_size) const { +inline void CapioFileManager::addThreadAwaitingData(std::string path, int tid, + size_t expected_size) const { START_LOG(gettid(), "call(path=%s, tid=%ld, expected_size=%ld)", path.c_str(), tid, expected_size); std::lock_guard lg(data_mutex); @@ -38,8 +39,8 @@ inline void CapioFileManager::add_thread_awaiting_data(std::string path, int tid thread_awaiting_data->at(path)->emplace(tid, expected_size); } -// TODO: maybe merge check for data and existence -inline void CapioFileManager::check_and_unlock_thread_awaiting_data(const std::string &path) const { +// TODO: +inline void CapioFileManager::checkAndUnlockThreadAwaitingData(const std::string &path) const { START_LOG(gettid(), "call(path=%s)", path.c_str()); LOG("Before lockguard"); std::lock_guard lg(data_mutex); @@ -55,9 +56,19 @@ inline void CapioFileManager::check_and_unlock_thread_awaiting_data(const std::s filesize = std::filesystem::is_directory(path) ? -1 : std::filesystem::file_size(path); } - if (is_committed(path) || - (item->first >= filesize && - capio_cl_engine->getFireRule(path) == CAPIO_FILE_MODE_NO_UPDATE)) { + bool committed = isCommitted(path); + bool file_size_check = item->first >= filesize; + bool is_fnu = capio_cl_engine->getFireRule(path) == CAPIO_FILE_MODE_NO_UPDATE; + bool is_producer = capio_cl_engine->isProducer(path, item->first); + bool lock_condition = committed || is_producer || (file_size_check && is_fnu); + + LOG("( committed(%s) || is_producer(%s) || ( file_size_check(%s) && is_fnu(%s) ) )", + committed ? "true" : "false", is_producer ? "true" : "false", + file_size_check ? "true" : "false", is_fnu ? "true" : "false"); + + LOG("Evaluation of expression: %s", lock_condition ? "TRUE" : "FALSE"); + + if (lock_condition) { LOG("Thread %ld can be unlocked", item->first); /* * Check for file size only if it is directory, otherwise, @@ -88,32 +99,56 @@ inline void CapioFileManager::check_and_unlock_thread_awaiting_data(const std::s } } -inline void CapioFileManager::set_committed(const std::filesystem::path &path) const { +inline void CapioFileManager::increaseCloseCount(const std::filesystem::path &path) { + START_LOG(gettid(), "call(path=%s)", path.c_str()); + auto metadata_path = path.string() + ".capio"; + const auto lock = new DistributedSemaphore(metadata_path + ".lock", 300); + long long close_count = 0; + LOG("Gained mutual exclusive access to token file %s", (metadata_path + ".lock").c_str()); + + std::fstream f(metadata_path, std::ios::in | std::ios::out); + LOG("Opened CAPIO metadata file %s", metadata_path.c_str()); + f >> close_count; + LOG("Close count is %llu. Increasing by one", close_count); + close_count++; + f.close(); + + auto out = fopen(metadata_path.c_str(), "w"); + fprintf(out, " %llu \n", close_count); + fclose(out); + + LOG("Updated close count to %llu", close_count); + + delete lock; +} + +inline void CapioFileManager::setCommitted(const std::filesystem::path &path) const { START_LOG(gettid(), "call(path=%s)", path.c_str()); auto metadata_path = path.string() + ".capio"; LOG("Creating token %s", metadata_path.c_str()); - auto fd = creat(metadata_path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + auto fd = open(metadata_path.c_str(), O_CREAT | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, 0664); LOG("Token fd = %d", fd); close(fd); - check_and_unlock_thread_awaiting_data(path); - unlock_thread_awaiting_creation(path); + checkAndUnlockThreadAwaitingData(path); + unlockThreadAwaitingCreation(path); } -inline void CapioFileManager::set_committed(pid_t tid) const { +inline void CapioFileManager::setCommitted(pid_t tid) const { START_LOG(gettid(), "call(tid=%d)", tid); auto files = client_manager->get_produced_files(tid); for (const auto &file : *files) { LOG("Committing file %s", file.c_str()); - CapioFileManager::set_committed(file); + CapioFileManager::setCommitted(file); } } -inline bool CapioFileManager::is_committed(const std::filesystem::path &path) { +inline bool CapioFileManager::isCommitted(const std::filesystem::path &path) { START_LOG(gettid(), "call(path=%s)", path.c_str()); bool file_exists = std::filesystem::exists(path); LOG("File %s exists: %s", path.c_str(), file_exists ? "true" : "false"); if (!file_exists) { + LOG("File %s does not yet exists", path.c_str()); return false; } @@ -140,11 +175,13 @@ inline bool CapioFileManager::is_committed(const std::filesystem::path &path) { LOG("CAPIO token file %s %s existing", computed_path.c_str(), std::filesystem::exists(computed_path) ? "is" : "is not"); - if (capio_cl_engine->getCommitRule(path) == CAPIO_FILE_COMMITTED_ON_FILE) { + std::string commit_rule = capio_cl_engine->getCommitRule(path); + + if (commit_rule == CAPIO_FILE_COMMITTED_ON_FILE) { LOG("Commit rule is on_file. Checking for file dependencies"); bool commit_computed = true; for (auto file : capio_cl_engine->get_file_deps(path)) { - commit_computed = commit_computed && is_committed(file); + commit_computed = commit_computed && isCommitted(file); } LOG("Commit result for file %s is: %s", computed_path.c_str(), @@ -152,11 +189,33 @@ inline bool CapioFileManager::is_committed(const std::filesystem::path &path) { return commit_computed; } + if (commit_rule == CAPIO_FILE_COMMITTED_ON_CLOSE) { + LOG("Commit rule is ON_CLOSE"); + int commit_count = capio_cl_engine->getCommitCloseCount(path); + LOG("Expected close count is: %d", commit_count); + long actual_commit_count = 0; + + if (std::filesystem::exists(path.string() + ".capio") && commit_count != -1) { + LOG("Commit file exists. retriving commit count"); + std::ifstream in(path.string() + ".capio"); + if (in.is_open()) { + LOG("Opened file"); + in >> actual_commit_count; + } + LOG("Obtained actual commit count: %l", actual_commit_count); + } + + LOG("File %s committed", actual_commit_count >= commit_count ? "IS" : "IS NOT"); + return actual_commit_count >= commit_count; + } + + LOG("Commit rule is ON_TERMINATION. File exists? %s", + std::filesystem::exists(computed_path) ? "TRUE" : "FALSE"); return std::filesystem::exists(computed_path); } } -inline std::vector CapioFileManager::get_file_awaiting_creation() const { +inline std::vector CapioFileManager::getFileAwaitingCreation() const { // NOTE: do not put inside here log code as it will generate a lot of useless log std::lock_guard lg(threads_mutex); std::vector keys; @@ -166,7 +225,7 @@ inline std::vector CapioFileManager::get_file_awaiting_creation() c return keys; } -inline std::vector CapioFileManager::get_file_awaiting_data() const { +inline std::vector CapioFileManager::getFileAwaitingData() const { // NOTE: do not put inside here log code as it will generate a lot of useless log std::lock_guard lg(data_mutex); std::vector keys; diff --git a/src/server/file-manager/fs_monitor.hpp b/src/server/file-manager/fs_monitor.hpp index 816dcd383..e7ac97d11 100644 --- a/src/server/file-manager/fs_monitor.hpp +++ b/src/server/file-manager/fs_monitor.hpp @@ -24,21 +24,21 @@ class FileSystemMonitor { * and not by the file_system monitor component itself */ - for (const auto &file : file_manager->get_file_awaiting_creation()) { + for (const auto &file : file_manager->getFileAwaitingCreation()) { if (std::filesystem::exists(file)) { LOG("File %s exists. Unlocking thread awaiting for creation", file.c_str()); - file_manager->unlock_thread_awaiting_creation(file); - file_manager->delete_file_awaiting_creation(file); + file_manager->unlockThreadAwaitingCreation(file); + file_manager->deleteFileAwaitingCreation(file); LOG("Completed handling.\n\n"); } } - for (auto &file : file_manager->get_file_awaiting_data()) { + for (auto &file : file_manager->getFileAwaitingData()) { if (std::filesystem::exists(file)) { LOG("File %s exists. Checking if enough data is available", file.c_str()); // actual update, end eventual removal from map is handled by the // CapioFileManager class and not by the FileSystemMonitor class - file_manager->check_and_unlock_thread_awaiting_data(file); + file_manager->checkAndUnlockThreadAwaitingData(file); LOG("Completed handling.\n\n"); } } diff --git a/src/server/utils/distributed_semaphore.hpp b/src/server/utils/distributed_semaphore.hpp index 22e800789..246d76a0c 100644 --- a/src/server/utils/distributed_semaphore.hpp +++ b/src/server/utils/distributed_semaphore.hpp @@ -1,9 +1,8 @@ #ifndef DISTRIBUTEDSEMAPHORE_HPP #define DISTRIBUTEDSEMAPHORE_HPP class DistributedSemaphore { - private: std::string name; - struct timespec sleep {}; + timespec sleep{}; bool locked; int fp; diff --git a/src/server/utils/signals.hpp b/src/server/utils/signals.hpp index c22faa82f..71bf80965 100644 --- a/src/server/utils/signals.hpp +++ b/src/server/utils/signals.hpp @@ -12,10 +12,12 @@ void sig_term_handler(int signum, siginfo_t *info, void *ptr) { strsignal(signum), info != nullptr ? info->si_pid : -1); std::cout << std::endl - << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "shutting down server" << std::endl; + << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name << " ] " + << "shutting down server" << std::endl; if (signum == SIGSEGV) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << "Segfault detected!" << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_ERROR << " [ " << node_name << " ] " + << "Segfault detected!" << std::endl; } #ifdef CAPIO_COVERAGE @@ -23,14 +25,17 @@ void sig_term_handler(int signum, siginfo_t *info, void *ptr) { #endif delete request_handlers_engine; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "request_handlers_engine cleanup completed" - << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name << " ] " + << "request_handlers_engine cleanup completed" << std::endl; delete ctl_module; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "ctl_module cleanup completed" << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name << " ] " + << "ctl_module cleanup completed" << std::endl; delete fs_monitor; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << "fs_monitor cleanup completed" << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name << " ] " + << "fs_monitor cleanup completed" << std::endl; delete shm_canary; - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << "shutdown completed" << std::endl; + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_INFO << " [ " << node_name << " ] " + << "shutdown completed" << std::endl; exit(EXIT_SUCCESS); } diff --git a/tests/unit/syscall/src/clone.cpp b/tests/unit/syscall/src/clone.cpp index 0f43d937e..5506cdead 100644 --- a/tests/unit/syscall/src/clone.cpp +++ b/tests/unit/syscall/src/clone.cpp @@ -83,4 +83,4 @@ TEST(SystemCallTest, TestThreadCloneProducerConsumerWithStat) { EXPECT_NE(fclose(fp), EOF); EXPECT_NE(unlink(PATHNAME), -1); t1.join(); -} \ No newline at end of file +} From 8a81ade4927d17300589b8abb8e8440b3232a392 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Fri, 27 Sep 2024 14:08:55 +0200 Subject: [PATCH 020/151] Bugfixes --- .../capio-cl-engine/capio_cl_engine.hpp | 5 +++ src/server/client-manager/handlers/close.hpp | 6 ++- .../client-manager/handlers/consent.hpp | 24 ++++++---- src/server/client-manager/handlers/read.hpp | 3 +- src/server/file-manager/file_manager_impl.hpp | 44 +++++++++++-------- 5 files changed, 52 insertions(+), 30 deletions(-) diff --git a/src/server/capio-cl-engine/capio_cl_engine.hpp b/src/server/capio-cl-engine/capio_cl_engine.hpp index 7d31b573b..99403ca35 100644 --- a/src/server/capio-cl-engine/capio_cl_engine.hpp +++ b/src/server/capio-cl-engine/capio_cl_engine.hpp @@ -272,6 +272,11 @@ class CapioCLEngine { if (_locations.find(path) != _locations.end()) { LOG("Found exact match for path"); std::vector producers = std::get<0>(_locations.at(path)); + DBG(gettid(), [&](std::vector &arr) { + for (auto itm : arr) { + LOG("producer: %s", itm.c_str()); + } + }(producers)); return std::find(producers.begin(), producers.end(), app_name) != producers.end(); } LOG("No exact match found in locations. checking for globs"); diff --git a/src/server/client-manager/handlers/close.hpp b/src/server/client-manager/handlers/close.hpp index 199263c20..cf848d243 100644 --- a/src/server/client-manager/handlers/close.hpp +++ b/src/server/client-manager/handlers/close.hpp @@ -17,8 +17,10 @@ inline void close_handler(const char *const str) { LOG("File needs handling"); - // Call the set_committed method only if the commit rule is on_close - if (capio_cl_engine->getCommitRule(filename) == CAPIO_FILE_COMMITTED_ON_CLOSE) { + // Call the set_committed method only if the commit rule is on_close and calling thread is a + // producer + if (capio_cl_engine->getCommitRule(filename) == CAPIO_FILE_COMMITTED_ON_CLOSE && + capio_cl_engine->isProducer(filename, tid)) { file_manager->setCommitted(path); // The increase close count is called only on explicit close() sc, as defined by the diff --git a/src/server/client-manager/handlers/consent.hpp b/src/server/client-manager/handlers/consent.hpp index 30eb3d779..64fd2f6c2 100644 --- a/src/server/client-manager/handlers/consent.hpp +++ b/src/server/client-manager/handlers/consent.hpp @@ -18,17 +18,23 @@ inline void consent_to_proceed_handler(const char *const str) { client_manager->reply_to_client(tid, 1); return; } + if (capio_cl_engine->isProducer(path, tid)) { + LOG("Application is producer. continuing"); + client_manager->reply_to_client(tid, 1); + return; + } // TODO: check this expression as being the correct evaluation one - // NOTE: expression is (exists AND (committed OR no_update)) OR is_producer - - bool exists = std::filesystem::exists(path); - bool committed = CapioFileManager::isCommitted(path); - bool firable = capio_cl_engine->getFireRule(path) == CAPIO_FILE_MODE_NO_UPDATE; - bool is_producer = capio_cl_engine->isProducer(path, tid); - LOG("exists=%s, committed=%s, firable=%s, is_producer=%s", exists ? "true" : "false", - committed ? "true" : "false", firable ? "true" : "false", is_producer ? "true" : "false"); - if ((exists && (committed || firable)) || is_producer) { + // NOTE: expression is (exists AND (committed OR no_update)) + + bool exists = std::filesystem::exists(path); + bool committed = CapioFileManager::isCommitted(path); + bool firable = capio_cl_engine->getFireRule(path) == CAPIO_FILE_MODE_NO_UPDATE; + + LOG("exists=%s, committed=%s, firable=%s", exists ? "true" : "false", + committed ? "true" : "false", firable ? "true" : "false"); + + if (exists && (committed || firable)) { LOG("It is possible to unlock waiting thread"); client_manager->reply_to_client(tid, 1); } else { diff --git a/src/server/client-manager/handlers/read.hpp b/src/server/client-manager/handlers/read.hpp index 1d363736c..59af4c4d3 100644 --- a/src/server/client-manager/handlers/read.hpp +++ b/src/server/client-manager/handlers/read.hpp @@ -1,5 +1,6 @@ #ifndef READ_HPP #define READ_HPP +#include "file-manager/file_manager_impl.hpp" inline void read_handler(const char *const str) { pid_t tid; @@ -19,7 +20,7 @@ inline void read_handler(const char *const str) { } auto is_committed = CapioFileManager::isCommitted(path); - auto file_size = std::filesystem::file_size(path); + auto file_size = get_file_size_if_exists(path); // return ULLONG_MAX to signal client cache that file is committed and no more requests are // required diff --git a/src/server/file-manager/file_manager_impl.hpp b/src/server/file-manager/file_manager_impl.hpp index d93bafce1..3a83163a4 100644 --- a/src/server/file-manager/file_manager_impl.hpp +++ b/src/server/file-manager/file_manager_impl.hpp @@ -4,6 +4,13 @@ #include "file_manager.hpp" #include "utils/distributed_semaphore.hpp" +inline uintmax_t get_file_size_if_exists(const std::filesystem::path &path) { + if (std::filesystem::exists(path)) { + return std::filesystem::file_size(path); + } + return 0; +} + inline void CapioFileManager::addThreadAwaitingCreation(std::string path, pid_t tid) const { START_LOG(gettid(), "call(path=%s, tid=%ld)", path.c_str(), tid); std::lock_guard lg(threads_mutex); @@ -52,10 +59,9 @@ inline void CapioFileManager::checkAndUnlockThreadAwaitingData(const std::string for (auto item = threads->begin(); item != threads->end();) { LOG("Handling thread"); uintmax_t filesize = -1; - if (std::filesystem::exists(path)) { - filesize = - std::filesystem::is_directory(path) ? -1 : std::filesystem::file_size(path); - } + + filesize = std::filesystem::is_directory(path) ? -1 : get_file_size_if_exists(path); + bool committed = isCommitted(path); bool file_size_check = item->first >= filesize; bool is_fnu = capio_cl_engine->getFireRule(path) == CAPIO_FILE_MODE_NO_UPDATE; @@ -76,10 +82,9 @@ inline void CapioFileManager::checkAndUnlockThreadAwaitingData(const std::string * This is caused by the fact that std::filesystem::file_size is * implementation defined when invoked on directories */ - client_manager->reply_to_client(item->first, - std::filesystem::is_directory(path) - ? ULLONG_MAX - : std::filesystem::file_size(path)); + client_manager->reply_to_client(item->first, std::filesystem::is_directory(path) + ? ULLONG_MAX + : get_file_size_if_exists(path)); // remove thread from map LOG("Removing thread %ld from threads awaiting on data", item->first); item = threads->erase(item); @@ -195,18 +200,21 @@ inline bool CapioFileManager::isCommitted(const std::filesystem::path &path) { LOG("Expected close count is: %d", commit_count); long actual_commit_count = 0; - if (std::filesystem::exists(path.string() + ".capio") && commit_count != -1) { - LOG("Commit file exists. retriving commit count"); - std::ifstream in(path.string() + ".capio"); - if (in.is_open()) { - LOG("Opened file"); - in >> actual_commit_count; + if (std::filesystem::exists(path.string() + ".capio")) { + if (commit_count != -1) { + LOG("Commit file exists. retrieving commit count"); + std::ifstream in(path.string() + ".capio"); + if (in.is_open()) { + LOG("Opened file"); + in >> actual_commit_count; + } + LOG("Obtained actual commit count: %l", actual_commit_count); + LOG("File %s committed", actual_commit_count >= commit_count ? "IS" : "IS NOT"); + return actual_commit_count >= commit_count; } - LOG("Obtained actual commit count: %l", actual_commit_count); + LOG("File needs to be closed exactly once. checking for token existence"); + return std::filesystem::exists(path.string() + ".capio"); } - - LOG("File %s committed", actual_commit_count >= commit_count ? "IS" : "IS NOT"); - return actual_commit_count >= commit_count; } LOG("Commit rule is ON_TERMINATION. File exists? %s", From 0d1d79375c9eb25c309afa2ce97c553d95ab7c29 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Tue, 1 Oct 2024 15:11:57 +0200 Subject: [PATCH 021/151] Moved CAPIO metadata to other directory --- src/common/capio/constants.hpp | 30 ++--- src/common/capio/env.hpp | 44 +++---- src/server/capio_server.cpp | 3 +- src/server/client-manager/client_manager.hpp | 4 +- src/server/client-manager/handlers/read.hpp | 2 +- .../client-manager/request_handler_engine.hpp | 4 +- src/server/ctl-module/capio_ctl.hpp | 8 +- src/server/file-manager/file_manager.hpp | 11 +- src/server/file-manager/file_manager_impl.hpp | 112 ++++++++++-------- src/server/file-manager/fs_monitor.hpp | 4 +- src/server/utils/common.hpp | 4 +- src/server/utils/env.hpp | 40 ------- src/server/utils/signals.hpp | 4 +- src/server/utils/types.hpp | 17 --- 14 files changed, 111 insertions(+), 176 deletions(-) delete mode 100644 src/server/utils/env.hpp diff --git a/src/common/capio/constants.hpp b/src/common/capio/constants.hpp index a7edc2ce7..7c0d54985 100644 --- a/src/common/capio/constants.hpp +++ b/src/common/capio/constants.hpp @@ -84,24 +84,18 @@ constexpr char CAPIO_LOG_POSIX_SYSCALL_END[] = "~~~~~~~~~ END SYSCA constexpr char CAPIO_SERVER_DEFAULT_LOG_FILE_PREFIX[] = "server_thread_\0"; constexpr char CAPIO_LOG_SERVER_BANNER[] = "\n\n " - "\033[1;34m /$$$$$$ /$$$$$$ /$$$$$$$\033[0;96m /$$$$$$ /$$$$$$ \033[1;33m " - "$$$$$$$$\\ $$$$$$\\ \n" - "\033[1;34m /$$__ $$ /$$__ $$| $$__ $$\033[0;96m|_ $$_/ /$$__ $$\033[1;33m $$ " - "_____|$$ __$$\\ \n" - "\033[1;34m| $$ \\__/| $$ \\ $$| $$ \\ $$ \033[0;96m | $$ | $$ \\ $$\033[1;33m $$ " - "| $$ / \\__|\n" - "\033[1;34m| $$ | $$$$$$$$| $$$$$$$/ \033[0;96m| $$ | $$ | $$\033[1;33m $$$$$$\\ " - "$$$$$\\ \\$$$$$$\\ \n" - "\033[1;34m| $$ | $$__ $$| $$____/ \033[0;96m| $$ | $$ | $$\033[1;33m \\______|$$ " - "__| \\____$$\\ \n" - "\033[1;34m| $$ $$| $$ | $$| $$ \033[0;96m| $$ | $$ | $$\033[1;33m $$ | " - " $$\\ $$ |\n" - "\033[1;34m| $$$$$$/| $$ | $$| $$ \033[0;96m/$$$$$$| $$$$$$/\033[1;33m $$ | " - " \\$$$$$$ |\n" - "\033[1;34m \\______/ |__/ |__/|__/ \033[0;96m|______/ \\______/\033[1;33m " - "\\__| \\______/ \n\n" - "\033[0m CAPIO - Cross Application Programmable IO - File System edition \n" - " V. " CAPIO_VERSION "\n\n"; + "\033[1;34m /$$$$$$ /$$$$$$ /$$$$$$$\033[0;96m /$$$$$$ /$$$$$$ \n" + "\033[1;34m /$$__ $$ /$$__ $$| $$__ $$\033[0;96m|_ $$_/ /$$__ $$\n" + "\033[1;34m| $$ \\__/| $$ \\ $$| $$ \\ $$ \033[0;96m | $$ | $$ \\ " + "$$\n" + "\033[1;34m| $$ | $$$$$$$$| $$$$$$$/ \033[0;96m| $$ | $$ | $$\n" + "\033[1;34m| $$ | $$__ $$| $$____/ \033[0;96m| $$ | $$ | $$\n" + "\033[1;34m| $$ $$| $$ | $$| $$ \033[0;96m| $$ | $$ | $$\n" + "\033[1;34m| $$$$$$/| $$ | $$| $$ \033[0;96m/$$$$$$| $$$$$$/\n" + "\033[1;34m \\______/ |__/ |__/|__/ \033[0;96m|______/ " + "\\______/\n\n" + "\033[0m CAPIO - Cross Application Programmable IO \n" + " V. " CAPIO_VERSION "\n\n"; constexpr char CAPIO_LOG_SERVER_CLI_LEVEL_INFO[] = "[ \033[1;32m SERVER \033[0m ] "; constexpr char CAPIO_LOG_SERVER_CLI_LEVEL_WARNING[] = "[ \033[1;33m SERVER \033[0m ] "; constexpr char CAPIO_LOG_SERVER_CLI_LEVEL_ERROR[] = "[ \033[1;31m SERVER \033[0m ] "; diff --git a/src/common/capio/env.hpp b/src/common/capio/env.hpp index ae33b4a98..f02aefa37 100644 --- a/src/common/capio/env.hpp +++ b/src/common/capio/env.hpp @@ -12,7 +12,7 @@ #include "logger.hpp" #include "syscall.hpp" -const std::filesystem::path &get_capio_dir() { +inline const std::filesystem::path &get_capio_dir() { static std::filesystem::path capio_dir{}; START_LOG(capio_syscall(SYS_gettid), "call()"); // TODO: if CAPIO_DIR is not set, it should be left as null @@ -45,40 +45,24 @@ const std::filesystem::path &get_capio_dir() { return capio_dir; } -inline long get_cache_lines() { +inline const std::filesystem::path &get_capio_metadata_path() { + static std::filesystem::path metadata_path{}; START_LOG(capio_syscall(SYS_gettid), "call()"); - static long data_bufs_size = -1; - if (data_bufs_size == -1) { - LOG("Value not set. getting value"); - char *value = std::getenv("CAPIO_CACHE_LINES"); - if (value != nullptr) { - LOG("Getting value from environment variable"); - data_bufs_size = strtol(value, nullptr, 10); - } else { - LOG("Getting default value"); - data_bufs_size = CAPIO_CACHE_LINES_DEFAULT; + if (metadata_path.empty()) { + const char *val = std::getenv("CAPIO_METADATA_DIR"); + auto buf = std::unique_ptr(new char[PATH_MAX]); + + if (val == nullptr) { + metadata_path = get_capio_dir() / ".capio_metadata"; } - } - LOG("data_bufs_size=%ld", data_bufs_size); - return data_bufs_size; -} -inline long get_cache_line_size() { - START_LOG(capio_syscall(SYS_gettid), "call()"); - static long data_bufs_count = -1; - if (data_bufs_count == -1) { - LOG("Value not set. getting value"); - char *value = std::getenv("CAPIO_CACHE_LINE_SIZE"); - if (value != nullptr) { - LOG("Getting value from environment variable"); - data_bufs_count = strtol(value, nullptr, 10); - } else { - LOG("Getting default value"); - data_bufs_count = CAPIO_CACHE_LINE_SIZE_DEFAULT; + if (!std::filesystem::exists(metadata_path)) { + std::filesystem::create_directories(metadata_path); } } - LOG("data_bufs_count=%ld", data_bufs_count); - return data_bufs_count; + LOG("CAPIO_METADATA_DIR=%s", metadata_path.c_str()); + + return metadata_path; } inline int get_capio_log_level() { diff --git a/src/server/capio_server.cpp b/src/server/capio_server.cpp index 6de64504e..913f1175f 100644 --- a/src/server/capio_server.cpp +++ b/src/server/capio_server.cpp @@ -29,7 +29,6 @@ char node_name[HOST_NAME_MAX]; #include "capio/logger.hpp" #include "capio/semaphore.hpp" #include "utils/common.hpp" -#include "utils/env.hpp" #include "client-manager/request_handler_engine.hpp" #include "ctl-module/capio_ctl.hpp" @@ -154,7 +153,7 @@ std::string parseCLI(int argc, char **argv) { delete log; #else if (std::getenv("CAPIO_LOG_LEVEL") != nullptr) { - std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING + std::cout << CAPIO_LOG_SERVER_CLI_LEVEL_WARNING << " [ " << node_name << " ] " << CAPIO_LOG_SERVER_CLI_LOGGING_NOT_AVAILABLE << std::endl; } #endif diff --git a/src/server/client-manager/client_manager.hpp b/src/server/client-manager/client_manager.hpp index ca09dd51c..e700b3f46 100644 --- a/src/server/client-manager/client_manager.hpp +++ b/src/server/client-manager/client_manager.hpp @@ -15,8 +15,8 @@ class ClientManager { bufs_response = new CSBufResponse_t(); app_names = new std::unordered_map; files_to_be_committed_by_tid = new std::unordered_map *>; - std::cout << CAPIO_SERVER_CLI_LOG_SERVER << "ClientManager initialization completed." - << std::endl; + std::cout << CAPIO_SERVER_CLI_LOG_SERVER << " [ " << node_name << " ] " + << "ClientManager initialization completed." << std::endl; } ~ClientManager() { diff --git a/src/server/client-manager/handlers/read.hpp b/src/server/client-manager/handlers/read.hpp index 59af4c4d3..b183b329c 100644 --- a/src/server/client-manager/handlers/read.hpp +++ b/src/server/client-manager/handlers/read.hpp @@ -20,7 +20,7 @@ inline void read_handler(const char *const str) { } auto is_committed = CapioFileManager::isCommitted(path); - auto file_size = get_file_size_if_exists(path); + auto file_size = CapioFileManager::get_file_size_if_exists(path); // return ULLONG_MAX to signal client cache that file is committed and no more requests are // required diff --git a/src/server/client-manager/request_handler_engine.hpp b/src/server/client-manager/request_handler_engine.hpp index 9d7ecd5e8..29b6e01c0 100644 --- a/src/server/client-manager/request_handler_engine.hpp +++ b/src/server/client-manager/request_handler_engine.hpp @@ -72,8 +72,8 @@ class RequestHandlerEngine { buf_requests = new CSBufRequest_t(SHM_COMM_CHAN_NAME, CAPIO_REQ_BUFF_CNT, CAPIO_REQ_MAX_SIZE, workflow_name); - std::cout << CAPIO_SERVER_CLI_LOG_SERVER << "RequestHandlerEngine initialization completed." - << std::endl; + std::cout << CAPIO_SERVER_CLI_LOG_SERVER << " [ " << node_name << " ] " + << "RequestHandlerEngine initialization completed." << std::endl; } ~RequestHandlerEngine() { diff --git a/src/server/ctl-module/capio_ctl.hpp b/src/server/ctl-module/capio_ctl.hpp index 5d7f1e75a..27d4518d9 100644 --- a/src/server/ctl-module/capio_ctl.hpp +++ b/src/server/ctl-module/capio_ctl.hpp @@ -19,7 +19,9 @@ class CapioCTLModule { LOG("Reading incoming request"); readQueue->read(request); LOG("Received request %s", request); - writeQueue->write("CIAO PLUTO"); + /* + * TODO: implement logic of CAPIO-CTL + */ } } @@ -31,8 +33,8 @@ class CapioCTLModule { *continue_execution = true; th = new std::thread(_main, continue_execution, readQueue, writeQueue); - std::cout << CAPIO_SERVER_CLI_LOG_SERVER << "CapioCTL initialization completed." - << std::endl; + std::cout << CAPIO_SERVER_CLI_LOG_SERVER << " [ " << node_name << " ] " + << "CapioCTL initialization completed." << std::endl; } ~CapioCTLModule() { diff --git a/src/server/file-manager/file_manager.hpp b/src/server/file-manager/file_manager.hpp index 253fc56e5..e29b53be8 100644 --- a/src/server/file-manager/file_manager.hpp +++ b/src/server/file-manager/file_manager.hpp @@ -10,14 +10,16 @@ class CapioFileManager { std::unordered_map *> *thread_awaiting_data; + static std::string getAndCreateMetadataPath(const std::string &path); + public: CapioFileManager() { START_LOG(gettid(), "call()"); thread_awaiting_file_creation = new std::unordered_map *>; thread_awaiting_data = new std::unordered_map *>; - std::cout << CAPIO_SERVER_CLI_LOG_SERVER << "CapioFileManager initialization completed." - << std::endl; + std::cout << CAPIO_SERVER_CLI_LOG_SERVER << " [ " << node_name << " ] " + << "CapioFileManager initialization completed." << std::endl; } ~CapioFileManager() { START_LOG(gettid(), "call()"); @@ -25,11 +27,12 @@ class CapioFileManager { delete thread_awaiting_data; } + static uintmax_t get_file_size_if_exists(const std::filesystem::path &path); + static void increaseCloseCount(const std::filesystem::path &path); + static bool isCommitted(const std::filesystem::path &path); void setCommitted(const std::filesystem::path &path) const; void setCommitted(pid_t tid) const; - static bool isCommitted(const std::filesystem::path &path); void checkAndUnlockThreadAwaitingData(const std::string &path) const; - static void increaseCloseCount(const std::filesystem::path &path); void addThreadAwaitingData(std::string path, int tid, size_t expected_size) const; void unlockThreadAwaitingCreation(std::string path) const; void addThreadAwaitingCreation(std::string path, pid_t tid) const; diff --git a/src/server/file-manager/file_manager_impl.hpp b/src/server/file-manager/file_manager_impl.hpp index 3a83163a4..a1e254c60 100644 --- a/src/server/file-manager/file_manager_impl.hpp +++ b/src/server/file-manager/file_manager_impl.hpp @@ -1,10 +1,24 @@ #ifndef FILE_MANAGER_HPP #define FILE_MANAGER_HPP +#include "capio/env.hpp" #include "client-manager/client_manager.hpp" #include "file_manager.hpp" #include "utils/distributed_semaphore.hpp" -inline uintmax_t get_file_size_if_exists(const std::filesystem::path &path) { +inline std::string CapioFileManager::getAndCreateMetadataPath(const std::string &path) { + START_LOG(gettid(), "call(path=%s)", path.c_str()); + std::filesystem::path result = + get_capio_metadata_path() / (path.substr(path.find(get_capio_dir()) + 1) + ".capio"); + + LOG("metadata path is %s", result.c_str()); + LOG("Creating metadata directory (%s)", result.parent_path().c_str()); + std::filesystem::create_directories(result.parent_path()); + + LOG("Created capio metadata parent path (if no file existed). returning metadata token file"); + return result; +} + +inline uintmax_t CapioFileManager::get_file_size_if_exists(const std::filesystem::path &path) { if (std::filesystem::exists(path)) { return std::filesystem::file_size(path); } @@ -106,7 +120,7 @@ inline void CapioFileManager::checkAndUnlockThreadAwaitingData(const std::string inline void CapioFileManager::increaseCloseCount(const std::filesystem::path &path) { START_LOG(gettid(), "call(path=%s)", path.c_str()); - auto metadata_path = path.string() + ".capio"; + auto metadata_path = getAndCreateMetadataPath(path); const auto lock = new DistributedSemaphore(metadata_path + ".lock", 300); long long close_count = 0; LOG("Gained mutual exclusive access to token file %s", (metadata_path + ".lock").c_str()); @@ -129,7 +143,7 @@ inline void CapioFileManager::increaseCloseCount(const std::filesystem::path &pa inline void CapioFileManager::setCommitted(const std::filesystem::path &path) const { START_LOG(gettid(), "call(path=%s)", path.c_str()); - auto metadata_path = path.string() + ".capio"; + auto metadata_path = getAndCreateMetadataPath(path); LOG("Creating token %s", metadata_path.c_str()); auto fd = open(metadata_path.c_str(), O_CREAT | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, 0664); LOG("Token fd = %d", fd); @@ -149,13 +163,6 @@ inline void CapioFileManager::setCommitted(pid_t tid) const { inline bool CapioFileManager::isCommitted(const std::filesystem::path &path) { START_LOG(gettid(), "call(path=%s)", path.c_str()); - bool file_exists = std::filesystem::exists(path); - LOG("File %s exists: %s", path.c_str(), file_exists ? "true" : "false"); - - if (!file_exists) { - LOG("File %s does not yet exists", path.c_str()); - return false; - } if (std::filesystem::is_directory(path)) { // is directory @@ -173,54 +180,57 @@ inline bool CapioFileManager::isCommitted(const std::filesystem::path &path) { LOG("Final result: %s", continue_directory ? "COMMITTED" : "NOT COMMITTED"); return continue_directory; - } else { - // if is file - LOG("Path is a file"); - std::string computed_path = path.string() + ".capio"; - LOG("CAPIO token file %s %s existing", computed_path.c_str(), - std::filesystem::exists(computed_path) ? "is" : "is not"); - - std::string commit_rule = capio_cl_engine->getCommitRule(path); - - if (commit_rule == CAPIO_FILE_COMMITTED_ON_FILE) { - LOG("Commit rule is on_file. Checking for file dependencies"); - bool commit_computed = true; - for (auto file : capio_cl_engine->get_file_deps(path)) { - commit_computed = commit_computed && isCommitted(file); - } + } - LOG("Commit result for file %s is: %s", computed_path.c_str(), - commit_computed ? "committed" : "not committed"); - return commit_computed; + // if is file + LOG("Path is a file"); + std::string metadata_computed_path = getAndCreateMetadataPath(path); + LOG("Computed metadata file path is %s", metadata_computed_path.c_str()); + + std::string commit_rule = capio_cl_engine->getCommitRule(path); + + bool metadata_token_exists = std::filesystem::exists(metadata_computed_path); + + if (commit_rule == CAPIO_FILE_COMMITTED_ON_FILE) { + LOG("Commit rule is on_file. Checking for file dependencies"); + bool commit_computed = true; + for (auto file : capio_cl_engine->get_file_deps(path)) { + commit_computed = commit_computed && isCommitted(file); } - if (commit_rule == CAPIO_FILE_COMMITTED_ON_CLOSE) { - LOG("Commit rule is ON_CLOSE"); - int commit_count = capio_cl_engine->getCommitCloseCount(path); - LOG("Expected close count is: %d", commit_count); - long actual_commit_count = 0; - - if (std::filesystem::exists(path.string() + ".capio")) { - if (commit_count != -1) { - LOG("Commit file exists. retrieving commit count"); - std::ifstream in(path.string() + ".capio"); - if (in.is_open()) { - LOG("Opened file"); - in >> actual_commit_count; - } - LOG("Obtained actual commit count: %l", actual_commit_count); - LOG("File %s committed", actual_commit_count >= commit_count ? "IS" : "IS NOT"); - return actual_commit_count >= commit_count; + LOG("Commit result for file %s is: %s", path.c_str(), + commit_computed ? "committed" : "not committed"); + + return commit_computed; + } + + if (commit_rule == CAPIO_FILE_COMMITTED_ON_CLOSE) { + LOG("Commit rule is ON_CLOSE"); + int commit_count = capio_cl_engine->getCommitCloseCount(path); + LOG("Expected close count is: %d", commit_count); + + if (metadata_token_exists) { + if (commit_count != -1) { + long actual_commit_count = 0; + LOG("Commit file exists. retrieving commit count"); + std::ifstream in(metadata_computed_path); + if (in.is_open()) { + LOG("Opened file"); + in >> actual_commit_count; } - LOG("File needs to be closed exactly once. checking for token existence"); - return std::filesystem::exists(path.string() + ".capio"); + LOG("Obtained actual commit count: %l", actual_commit_count); + LOG("File %s committed", actual_commit_count >= commit_count ? "IS" : "IS NOT"); + return actual_commit_count >= commit_count; } + LOG("File needs to be closed exactly once and token exists. returning"); + return true; } - - LOG("Commit rule is ON_TERMINATION. File exists? %s", - std::filesystem::exists(computed_path) ? "TRUE" : "FALSE"); - return std::filesystem::exists(computed_path); + LOG("Commit file does not yet exists. returning false"); + return false; } + + LOG("Commit rule is ON_TERMINATION. File exists? %s", metadata_token_exists ? "TRUE" : "FALSE"); + return metadata_token_exists; } inline std::vector CapioFileManager::getFileAwaitingCreation() const { diff --git a/src/server/file-manager/fs_monitor.hpp b/src/server/file-manager/fs_monitor.hpp index e7ac97d11..2de5a5765 100644 --- a/src/server/file-manager/fs_monitor.hpp +++ b/src/server/file-manager/fs_monitor.hpp @@ -51,7 +51,7 @@ class FileSystemMonitor { START_LOG(gettid(), "call()"); *continue_execution = true; th = new std::thread(_main, std::ref(continue_execution)); - std::cout << CAPIO_SERVER_CLI_LOG_SERVER + std::cout << CAPIO_SERVER_CLI_LOG_SERVER << " [ " << node_name << " ] " << "CapioFileSystemMonitor initialization completed." << std::endl; } @@ -65,6 +65,6 @@ class FileSystemMonitor { } }; -FileSystemMonitor *fs_monitor; +inline FileSystemMonitor *fs_monitor; #endif // CAPIO_FS_FILE_SYSTEM_MONITOR_HPP diff --git a/src/server/utils/common.hpp b/src/server/utils/common.hpp index fdf017513..85067000f 100644 --- a/src/server/utils/common.hpp +++ b/src/server/utils/common.hpp @@ -6,8 +6,8 @@ #include "capio/constants.hpp" #include "types.hpp" -bool first_is_subpath_of_second(const std::filesystem::path &path, - const std::filesystem::path &base) { +inline bool first_is_subpath_of_second(const std::filesystem::path &path, + const std::filesystem::path &base) { const auto mismatch_pair = std::mismatch(path.begin(), path.end(), base.begin(), base.end()); return mismatch_pair.second == base.end(); } diff --git a/src/server/utils/env.hpp b/src/server/utils/env.hpp deleted file mode 100644 index 22f66310d..000000000 --- a/src/server/utils/env.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef CAPIO_SERVER_UTILS_ENV_HPP -#define CAPIO_SERVER_UTILS_ENV_HPP - -#include - -#include "capio/constants.hpp" - -off64_t get_file_initial_size() { - START_LOG(gettid(), "call()"); - static off64_t file_initial_size = 0; - if (file_initial_size == 0) { - char *val; - val = std::getenv("CAPIO_FILE_INIT_SIZE"); - if (val != nullptr) { - file_initial_size = std::strtol(val, nullptr, 10); - } else { - file_initial_size = CAPIO_DEFAULT_FILE_INITIAL_SIZE; - } - } - - return file_initial_size; -} - -off64_t get_prefetch_data_size() { - START_LOG(gettid(), "call()"); - static off64_t prefetch_data_size = -1; - if (prefetch_data_size == -1) { - char *val; - val = std::getenv("CAPIO_PREFETCH_DATA_SIZE"); - if (val != nullptr) { - prefetch_data_size = std::strtol(val, nullptr, 10); - } else { - prefetch_data_size = 0; - } - } - - return prefetch_data_size; -} - -#endif // CAPIO_SERVER_UTILS_ENV_HPP diff --git a/src/server/utils/signals.hpp b/src/server/utils/signals.hpp index 71bf80965..355c3ec35 100644 --- a/src/server/utils/signals.hpp +++ b/src/server/utils/signals.hpp @@ -7,7 +7,7 @@ extern "C" void __gcov_dump(void); #endif -void sig_term_handler(int signum, siginfo_t *info, void *ptr) { +inline void sig_term_handler(int signum, siginfo_t *info, void *ptr) { START_LOG(gettid(), "call(signal=[%d] (%s) from process with pid=%ld)", signum, strsignal(signum), info != nullptr ? info->si_pid : -1); @@ -40,7 +40,7 @@ void sig_term_handler(int signum, siginfo_t *info, void *ptr) { exit(EXIT_SUCCESS); } -void setup_signal_handlers() { +inline void setup_signal_handlers() { START_LOG(gettid(), "call()"); static struct sigaction sigact; memset(&sigact, 0, sizeof(sigact)); diff --git a/src/server/utils/types.hpp b/src/server/utils/types.hpp index e11af4c58..d3477cda7 100644 --- a/src/server/utils/types.hpp +++ b/src/server/utils/types.hpp @@ -8,23 +8,6 @@ #include "capio/queue.hpp" -typedef std::unordered_map CSPidsMap_T; -typedef std::unordered_map CSAppsMap_t; -typedef std::unordered_map> CSFilesSentMap_t; -typedef std::unordered_map> - CSProcessFileMetadataMap_t; -typedef std::unordered_map> CSDataBufferMap_t; -typedef std::unordered_map< - std::string, std::tuple> - CSMetadataConfMap_t; -typedef std::vector> - CSMetadataConfGlobs_t; -typedef std::unordered_map> CSWritersMap_t; -typedef std::unordered_map *, Semaphore *>>> - CSClientsRemotePendingNFilesMap_t; typedef std::unordered_map *> CSBufResponse_t; typedef CircularBuffer CSBufRequest_t; From 8c4898107e982ce1cb61af3d8df882e0f77353f4 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Wed, 2 Oct 2024 11:55:56 +0200 Subject: [PATCH 022/151] Protected map access to avoid std::out_of_range --- .../capio-cl-engine/capio_cl_engine.hpp | 78 +++++++++++++------ 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/src/server/capio-cl-engine/capio_cl_engine.hpp b/src/server/capio-cl-engine/capio_cl_engine.hpp index 99403ca35..a3c3acc2e 100644 --- a/src/server/capio-cl-engine/capio_cl_engine.hpp +++ b/src/server/capio-cl-engine/capio_cl_engine.hpp @@ -168,18 +168,24 @@ class CapioCLEngine { START_LOG(gettid(), "call(path=%s, producer=%s)", path.c_str(), producer.c_str()); producer.erase(remove_if(producer.begin(), producer.end(), isspace), producer.end()); newFile(path); - std::get<0>(_locations.at(path)).emplace_back(producer); + if (_locations.find(path) != _locations.end()) { + std::get<0>(_locations.at(path)).emplace_back(producer); + } } void addConsumer(const std::string &path, std::string &consumer) { START_LOG(gettid(), "call(path=%s, consumer=%s)", path.c_str(), consumer.c_str()); consumer.erase(remove_if(consumer.begin(), consumer.end(), isspace), consumer.end()); - std::get<1>(_locations.at(path)).emplace_back(consumer); + if (_locations.find(path) != _locations.end()) { + std::get<1>(_locations.at(path)).emplace_back(consumer); + } } void setCommitRule(const std::string &path, const std::string &commit_rule) { START_LOG(gettid(), "call(path=%s, commit_rule=%s)", path.c_str(), commit_rule.c_str()); - std::get<2>(_locations.at(path)) = commit_rule; + if (_locations.find(path) != _locations.end()) { + std::get<2>(_locations.at(path)) = commit_rule; + } } std::string getCommitRule(const std::string &path) { @@ -194,55 +200,72 @@ class CapioCLEngine { void setFireRule(const std::string &path, const std::string &fire_rule) { START_LOG(gettid(), "call(path=%s, fire_rule=%s)", path.c_str(), fire_rule.c_str()); - std::get<3>(_locations.at(path)) = fire_rule; + if (_locations.find(path) != _locations.end()) { + std::get<3>(_locations.at(path)) = fire_rule; + } } std::string getFireRule(const std::string &path) { START_LOG(gettid(), "call(path=%s)", path.c_str()); - if (_locations.find(path) == _locations.end()) { - return CAPIO_FILE_MODE_UPDATE; + if (_locations.find(path) != _locations.end()) { + return std::get<3>(_locations.at(path)); } - return std::get<3>(_locations.at(path)); + return CAPIO_FILE_MODE_UPDATE; } void setPermanent(const std::string &path, bool value) { START_LOG(gettid(), "call(path=%s, value=%s)", path.c_str(), value ? "true" : "false"); - std::get<4>(_locations.at(path)) = value; + if (_locations.find(path) != _locations.end()) { + std::get<4>(_locations.at(path)) = value; + } } void setExclude(const std::string &path, const bool value) { START_LOG(gettid(), "call(path=%s, value=%s)", path.c_str(), value ? "true" : "false"); - std::get<5>(_locations.at(path)) = value; + if (_locations.find(path) != _locations.end()) { + std::get<5>(_locations.at(path)) = value; + } } void setDirectory(const std::string &path) { START_LOG(gettid(), "call(path=%s)", path.c_str()); - std::get<6>(_locations.at(path)) = false; + if (_locations.find(path) != _locations.end()) { + std::get<6>(_locations.at(path)) = false; + } } - bool isDirectory(const std::string &path) const { + void setFile(const std::string &path) { START_LOG(gettid(), "call(path=%s)", path.c_str()); - return !std::get<6>(_locations.at(path)); + if (_locations.find(path) != _locations.end()) { + std::get<6>(_locations.at(path)) = true; + } } - void setFile(const std::string &path) { + bool isFile(const std::string &path) const { START_LOG(gettid(), "call(path=%s)", path.c_str()); - std::get<6>(_locations.at(path)) = true; + if (_locations.find(path) != _locations.end()) { + return std::get<6>(_locations.at(path)); + } + return false; } - bool isFile(const std::string &path) const { + bool isDirectory(const std::string &path) const { START_LOG(gettid(), "call(path=%s)", path.c_str()); - return std::get<6>(_locations.at(path)); + return !isFile(path); } void setCommitedNumber(const std::string &path, const int num) { START_LOG(gettid(), "call(path=%s, num=%ld)", path.c_str(), num); - std::get<7>(_locations.at(path)) = num; + if (_locations.find(path) != _locations.end()) { + std::get<7>(_locations.at(path)) = num; + } } void setDirectoryFileCount(const std::string &path, long num) { START_LOG(gettid(), "call(path=%s, num=%ld)", path.c_str(), num); - std::get<8>(_locations.at(path)) = num; + if (_locations.find(path) != _locations.end()) { + std::get<8>(_locations.at(path)) = num; + } } void remove(const std::string &path) { @@ -251,15 +274,21 @@ class CapioCLEngine { } // TODO: return vector - auto producers(const std::string &path) { + std::vector producers(const std::string &path) { START_LOG(gettid(), "call(path=%s)", path.c_str()); - return std::get<0>(_locations.at(path)); + if (_locations.find(path) != _locations.end()) { + return std::get<0>(_locations.at(path)); + } + return {}; } // TODO: return vector - auto consumers(const std::string &path) { + std::vector consumers(const std::string &path) { START_LOG(gettid(), "call(path=%s)", path.c_str()); - return std::get<1>(_locations.at(path)); + if (_locations.find(path) != _locations.end()) { + return std::get<1>(_locations.at(path)); + } + return {}; } bool isProducer(const std::string &path, const pid_t pid) { @@ -319,7 +348,10 @@ class CapioCLEngine { // todo fix leak std::vector get_file_deps(const std::filesystem::path &path) { - return std::get<9>(_locations.at(path)); + if (_locations.find(path) != _locations.end()) { + return std::get<9>(_locations.at(path)); + } + return {}; } }; From cc23d1560cdd388ebc4ce77047c5f1f5edc7a6ab Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Wed, 2 Oct 2024 12:21:56 +0200 Subject: [PATCH 023/151] updated readme --- .github/workflows/ci-tests.yaml | 60 ++++++++++---------- README.md | 97 ++++++--------------------------- 2 files changed, 47 insertions(+), 110 deletions(-) diff --git a/.github/workflows/ci-tests.yaml b/.github/workflows/ci-tests.yaml index 98b544d73..5d75c4755 100644 --- a/.github/workflows/ci-tests.yaml +++ b/.github/workflows/ci-tests.yaml @@ -173,33 +173,33 @@ jobs: - name: "Show server logs on failure" if: ${{ always() && steps.run-tests.outcome == 'failure' && matrix.build_type == 'Debug' }} run: tail -v -n +1 capio_logs/server/$(hostname)/server_thread_*.log -# - name: "Generate coverage report" -# if: ${{ matrix.build_type == 'Debug' }} -# run: | -# pip install --upgrade gcovr -# gcovr \ -# --exclude-throw-branches \ -# --xml coverage.xml \ -# --gcov-executable "${{ startsWith(matrix.cxx, 'clang-') && format('llvm-cov-{0} gcov', env.CXX_VERSION) || format('gcov-{0}', env.CXX_VERSION) }}" \ -# ../build -# - name: "Upload coverage report" -# if: ${{ matrix.build_type == 'Debug' }} -# uses: actions/upload-artifact@v4 -# with: -# name: ${{ format('{0}-tests', matrix.cxx) }} -# path: ./coverage.xml -# retention-days: 1 -# if-no-files-found: error -# upload-to-codecov: -# name: "Codecov report upload" -# needs: [ "unit-tests" , "codespell-check" , "format-check" ] -# runs-on: ubuntu-22.04 -# steps: -# - uses: actions/checkout@v4 -# - name: "Download artifacts" -# uses: actions/download-artifact@v4 -# - name: "Upload coverage to Codecov" -# uses: codecov/codecov-action@v4 -# with: -# fail_ci_if_error: true -# token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file + - name: "Generate coverage report" + if: ${{ matrix.build_type == 'Debug' }} + run: | + pip install --upgrade gcovr + gcovr \ + --exclude-throw-branches \ + --xml coverage.xml \ + --gcov-executable "${{ startsWith(matrix.cxx, 'clang-') && format('llvm-cov-{0} gcov', env.CXX_VERSION) || format('gcov-{0}', env.CXX_VERSION) }}" \ + ../build + - name: "Upload coverage report" + if: ${{ matrix.build_type == 'Debug' }} + uses: actions/upload-artifact@v4 + with: + name: ${{ format('{0}-tests', matrix.cxx) }} + path: ./coverage.xml + retention-days: 1 + if-no-files-found: error + upload-to-codecov: + name: "Codecov report upload" + needs: [ "unit-tests" , "codespell-check" , "format-check" ] + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: "Download artifacts" + uses: actions/download-artifact@v4 + - name: "Upload coverage to Codecov" + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file diff --git a/README.md b/README.md index 95d9dbea1..28c92bf7a 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,16 @@ CAPIO (Cross-Application Programmable I/O), is a middleware aimed at injecting streaming capabilities to workflow steps without changing the application codebase. It has been proven to work with C/C++ binaries, Fortran Binaries, JAVA, -python and bash. +python and bash. [![codecov](https://codecov.io/gh/High-Performance-IO/capio/graph/badge.svg?token=6ATRB5VJO3)](https://codecov.io/gh/High-Performance-IO/capio) ![CI-Tests](https://github.com/High-Performance-IO/capio/actions/workflows/ci-tests.yaml/badge.svg) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://raw.githubusercontent.com/High-Performance-IO/capio/master/LICENSE) +> [!IMPORTANT] +> This version of CAPIO does not support writes to memory. +> If you need it please refer to releases/v1.0.0 + ## Build and run tests ### Dependencies @@ -16,7 +20,6 @@ CAPIO depends on the following software that needs to be manually installed: - `cmake >=3.15` - `c++17` or newer -- `openmpi` - `pthreads` The following dependencies are automatically fetched during cmake configuration phase, and compiled when required. @@ -48,7 +51,7 @@ the first is optional). want to execute your program (one daemon for each node). If you desire to specify a custom folder for capio, set `CAPIO_DIR` as a environment variable. ```bash - [CAPIO_DIR=your_capiodir] [mpiexec -N 1 --hostfile your_hostfile] capio_server -c conf.json + [CAPIO_DIR=your_capiodir] capio_server -c conf.json ``` > [!NOTE] @@ -84,17 +87,13 @@ CAPIO can be controlled through the usage of environment variables. The availabl - `CAPIO_LOG_DIR` This environment variable is defined only for capio_posix applications and specifies the directory name to which capio will be created. If this variable is not defined, capio will log by default to `capio_logs`. An equivalent behaviour can be set on the capio server using the `-d` option; -- `CAPIO_CACHE_LINES`: This environment variable controls how many lines of cache are presents between posix and server - applications. defaults to 10 lines; - `CAPIO_CACHE_LINE_SIZE`: This environment variable controls the size of a single cache line. defaults to 256KB; #### Server only environment variable -- `CAPIO_FILE_INIT_SIZE`: This environment variable defines the default size of pre allocated memory for a new file - handled by capio. Defaults to 4MB. Bigger sizes will reduce the overhead of malloc but will fill faster node memory. - Value has to be expressed in bytes; -- `CAPIO_PREFETCH_DATA_SIZE`: If this variable is set, then data transfers between nodes will be always, at least of the - given value in bytes; +- `CAPIO_METADATA_DIR`: This environmental variable controls the location of the metadata files used by CAPIO. it + defaults to CAPIO_DIR. BE CAREFUL to put this folder on a path that is accessible by all instances of the running + CAPIO servers. #### Posix only environment variable @@ -109,95 +108,33 @@ CAPIO can be controlled through the usage of environment variables. The availabl ## How to inject streaming capabilities into your workflow -With CAPIO is possible to run the applications of your workflow that communicates through files concurrently. CAPIO will -synchronize transparently the concurrent reads and writes on those files. If a file is never modified after it is closed -you can set the streaming semantics equals to "on_close" on the configuration file. In this way, all the reads done on -this file will hung until the writer closes the file, allowing the consumer application to read the file even if the -producer is still running. -Another supported file streaming semantics is "append" in which a read is satisfied when the producer writes the -requested data. This is the most aggressive (and efficient) form of streaming semantics (because the consumer can start -reading while the producer is writing the file). This semantic must be used only if the producer does not modify a piece -of data after it is written. -The streaming semantic on_termination tells CAPIO to not allowing streaming on that file. This is the default streaming -semantics if a semantics for a file is not specified. -The following is an example of a simple configuration: - -```json -{ - "name": "my_workflow", - "IO_Graph": [ - { - "name": "writer", - "output_stream": [ - "file0.dat", - "file1.dat", - "file2.dat" - ], - "streaming": [ - { - "name": ["file0.dat"], - "committed": "on_close" - }, - { - "name": ["file1.dat"], - "committed": "on_close", - "mode": "no_update" - }, - { - "name": ["file2.dat"], - "committed": "on_termination" - } - ] - }, - { - "name": "reader", - "input_stream": [ - "file0.dat", - "file1.dat", - "file2.dat" - ] - } - ] -} -``` - -> [!NOTE] -> We are working on an extension of the possible streaming semantics and in a detailed -> documentation about the configuration file! - -## Examples +You can find documentation as well as examples, on the official documentation page at -The [examples](examples) folder contains some examples that shows how to use mpi_io with CAPIO. -There are also examples on how to write JSON configuration files for the semantics implemented by CAPIO: +[Official documentation website](https://capio.hpc4ai.it/docs) -- [on_close](https://github.com/High-Performance-IO/capio/wiki/Examples#on_close-semantic): A pipeline composed by a - producer and a consumer with "on_close" semantics -- [no_update](https://github.com/High-Performance-IO/capio/wiki/Examples#noupdate-semantics): A pipeline composed by a - producer and a consumer with "no_update" semantics -- [mix_semantics](https://github.com/High-Performance-IO/capio/wiki/Examples#mixed-semantics): A pipeline composed by a - producer and a consumer with mix semantics ## Report bugs + get help [Create a new issue](https://github.com/High-Performance-IO/capio/issues/new) -[Get help](https://github.com/High-Performance-IO/capio/wiki) +[Get help](capio.hpc4ai.it/docs) -> [!TIP] -> A [wiki](https://github.com/High-Performance-IO/capio/wiki) is in development! You might want to check the wiki to get -> more in depth information about CAPIO! ## CAPIO Team Made with :heart: by: -Alberto Riccardo Martinelli (designer and maintainer) \ Marco Edoardo Santimaria (Designer and maintainer) \ Iacopo Colonnelli (Workflows expert and maintainer) \ Massimo Torquati (Designer) \ Marco Aldinucci (Designer) +Former members: + +Alberto Riccardo Martinelli (designer and maintainer) + ## Papers + [![CAPIO](https://img.shields.io/badge/CAPIO-10.1109/HiPC58850.2023.00031-red)]([https://arxiv.org/abs/2206.10048](https://dx.doi.org/10.1109/HiPC58850.2023.00031)) From 17df088ddabcf6cd3fa29d8bf5ea08259177b829 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Mon, 7 Oct 2024 14:08:08 +0200 Subject: [PATCH 024/151] Added doxygen documentation Added doxygen documentation to the CAPIO codebase for most of the server component and for some of the posix components. Added a script to be called on the root directory to build and generate the capio html documentation inside the doxy/ folder. --- .gitignore | 10 +- doxy/Doxyfile | 2885 +++++++++++++++++ doxy/logo.png | Bin 0 -> 1499 bytes scripts/gen_documentation.sh | 10 + src/common/capio/logger.hpp | 10 + src/common/capio/queue.hpp | 6 + src/common/capio/semaphore.hpp | 13 + .../capio-cl-engine/capio_cl_engine.hpp | 4 + src/server/capio-cl-engine/json_parser.hpp | 10 + src/server/client-manager/client_manager.hpp | 24 +- src/server/client-manager/handlers/close.hpp | 6 + .../client-manager/handlers/consent.hpp | 10 +- src/server/client-manager/handlers/create.hpp | 6 + src/server/client-manager/handlers/exit.hpp | 6 + .../client-manager/handlers/handshake.hpp | 12 + src/server/client-manager/handlers/open.hpp | 7 + src/server/client-manager/handlers/read.hpp | 6 + src/server/client-manager/handlers/rename.hpp | 6 + src/server/client-manager/handlers/write.hpp | 6 + .../client-manager/request_handler_engine.hpp | 9 + src/server/ctl-module/capio_ctl.hpp | 5 +- src/server/file-manager/file_manager.hpp | 6 +- src/server/file-manager/file_manager_impl.hpp | 81 +- src/server/file-manager/fs_monitor.hpp | 22 +- src/server/utils/common.hpp | 15 + src/server/utils/distributed_semaphore.hpp | 17 + src/server/utils/signals.hpp | 11 + src/server/utils/types.hpp | 14 + 28 files changed, 3199 insertions(+), 18 deletions(-) create mode 100644 doxy/Doxyfile create mode 100644 doxy/logo.png create mode 100755 scripts/gen_documentation.sh diff --git a/.gitignore b/.gitignore index 4a0f1467e..9c6e26e48 100644 --- a/.gitignore +++ b/.gitignore @@ -44,7 +44,15 @@ cmake-build-* files_location*.txt capio_logs +#Doxygen generated documentation +doxy/html +doxy/latex +doxy/doxygen-awesome-css-* +doxy/theme + # Other debug build -.devcontainer \ No newline at end of file +.devcontainer +.DS_Store + diff --git a/doxy/Doxyfile b/doxy/Doxyfile new file mode 100644 index 000000000..f8e4df32d --- /dev/null +++ b/doxy/Doxyfile @@ -0,0 +1,2885 @@ +# Doxyfile 1.12.0 + +# This file describes the settings to be used by the documentation system +# Doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use Doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use Doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "CAPIO" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Cross Application Programmable IO - Software documentation" + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = logo.png + +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output +# directory. + +PROJECT_ICON = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where Doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES then Doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this +# option can be useful when feeding Doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. +# The default value is: NO. + +CREATE_SUBDIRS = YES + +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + +# If the ALLOW_UNICODE_NAMES tag is set to YES, Doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by Doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, Doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, Doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, Doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, Doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which Doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where Doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, Doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then Doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by Doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and Doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# Doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as Doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then Doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:^^" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by Doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make Doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by Doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by Doxygen, so you can +# mix Doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 6. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 6 + +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + +# When enabled Doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let Doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also makes the inheritance and +# collaboration diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software) sources only. Doxygen will parse +# them like normal C++ but will assume all classes use public instead of private +# inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# Doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then Doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, Doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# Doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run Doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number of threads Doxygen is allowed to use +# during processing. When set to 0 Doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = NO + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, Doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = YES + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = YES + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES Doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and macOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. + +CASE_SENSE_NAMES = SYSTEM + +# If the HIDE_SCOPE_NAMES tag is set to NO then Doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then Doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE = NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then Doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then Doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then Doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then Doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then Doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and Doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING Doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# Doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by Doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by Doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents Doxygen's defaults, run Doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. +# +# Note that if you run Doxygen from a directory containing a file called +# DoxygenLayout.xml, Doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +# The EXTERNAL_TOOL_PATH tag can be used to extend the search path (PATH +# environment variable) so that external tools such as latex and gs can be +# found. +# Note: Directories specified with EXTERNAL_TOOL_PATH are added in front of the +# path already specified by the PATH variable, and are added in the order +# specified. +# Note: This option is particularly useful for macOS version 14 (Sonoma) and +# higher, when running Doxygen from Doxywizard, because in this case any user- +# defined changes to the PATH are ignored. A typical example on macOS is to set +# EXTERNAL_TOOL_PATH = /Library/TeX/texbin /usr/local/bin +# together with the standard path, the full search path used by doxygen when +# launching external tools will then become +# PATH=/Library/TeX/texbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + +EXTERNAL_TOOL_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by Doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by Doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then Doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, Doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# If WARN_IF_INCOMPLETE_DOC is set to YES, Doxygen will warn about incomplete +# function parameter documentation. If set to NO, Doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, Doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, Doxygen will warn about +# undocumented enumeration values. If set to NO, Doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If the WARN_AS_ERROR tag is set to YES then Doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then Doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the Doxygen process Doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then Doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined Doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that Doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of Doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = ../src ../README.md logo.png + +# This tag can be used to specify the character encoding of the source files +# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# This tag can be used to specify the character encoding of the source files +# that Doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). +# See also: INPUT_ENCODING for further information on supported encodings. + +INPUT_FILE_ENCODING = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by Doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, +# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to +# be provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cxxm \ + *.cpp \ + *.cppm \ + *.ccm \ + *.c++ \ + *.c++m \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.ixx \ + *.l \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f18 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which Doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = */tests/* \ + *build* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# ANamespace::AClass, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that Doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that Doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by Doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by Doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the Doxygen output. + +USE_MDFILE_AS_MAINPAGE = ../README.md + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# multi-line macros, enums or list initialized variables directly into the +# documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct Doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of Doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by Doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then Doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, Doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank Doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that Doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that Doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of Doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank Doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that Doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank Doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that Doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by Doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = theme/doxygen-awesome.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generates light mode output, DARK always +# generates dark mode output, AUTO_LIGHT automatically sets the mode according +# to the user preference, uses light mode if no preference is set (the default), +# AUTO_DARK automatically sets the mode according to the user preference, uses +# dark mode if no preference is set and TOGGLE allows a user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use gray-scales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# If the HTML_COPY_CLIPBOARD tag is set to YES then Doxygen will show an icon in +# the top right corner of code and text fragments that allows the user to copy +# its content to the clipboard. Note this only works if supported by the browser +# and the web page is served via a secure context (see: +# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: +# protocol. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COPY_CLIPBOARD = YES + +# Doxygen stores a couple of settings persistently in the browser (via e.g. +# cookies). By default these settings apply to all HTML pages generated by +# Doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# the settings under a project specific key, such that the user preferences will +# be stored separately. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_PROJECT_COOKIE = + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, Doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then Doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by Doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# Doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty Doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by Doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# Doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# When the SHOW_ENUM_VALUES tag is set doxygen will show the specified +# enumeration values besides the enumeration mnemonics. +# The default value is: NO. + +SHOW_ENUM_VALUES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, Doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the OBFUSCATE_EMAILS tag is set to YES, Doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, Doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# Doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with JavaScript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled Doxygen will generate a search box for +# the HTML output. The underlying search engine uses JavaScript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the JavaScript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /