diff options
author | Dimitri Staessens <dimitri@ouroboros.rocks> | 2024-02-17 10:19:46 +0100 |
---|---|---|
committer | Sander Vrijders <sander@ouroboros.rocks> | 2024-02-19 11:49:07 +0100 |
commit | 06ee3370998f965b469d1c2859e3e34159c71e20 (patch) | |
tree | 93881747a4f1e99f6932231b0cb2358941cb9741 /src/irmd | |
parent | 7bb8aed15c7f29de4d9719acf8db7fdf73731af5 (diff) | |
download | ouroboros-06ee3370998f965b469d1c2859e3e34159c71e20.tar.gz ouroboros-06ee3370998f965b469d1c2859e3e34159c71e20.zip |
irmd: Revise IRMd internals
This is a full revision of the IRMd internal implementation.
The registry is now a proper subcomponent managing its own internal
lock (a single mutex). Some tests are added for the registry and its
data structures. Some macros for tests are added in <ouroboros/test.h>.
Flow allocation is now more symmetric between the client side (alloc)
and server size (accept). Each will create a flow in pending state
(ALLOC_PENDING/ACCEPT_PENDING) that is potentially fulfilled by an
IPCP using respond_alloc and respond_accept primitives. Deallocation
is split in flow_dealloc (application side) and ipcp_flow_dealloc
(IPCP side) to get the flow in DEALLOC_PENDING and DEALLOCATED state.
Cleanup of failed flow allocation is now properly handled instead of
relying on the sanitizer thread. The new sanitizer only needs to
monitor crashed processes.
On shutdown, the IRMd will now detect hanging processes and SIGKILL
them and clean up their fuse mountpoints if needed.
A lot of other things have been cleaned up and shuffled around a bit.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
Diffstat (limited to 'src/irmd')
30 files changed, 6604 insertions, 3051 deletions
diff --git a/src/irmd/CMakeLists.txt b/src/irmd/CMakeLists.txt index fba63a62..3a5be324 100644 --- a/src/irmd/CMakeLists.txt +++ b/src/irmd/CMakeLists.txt @@ -38,7 +38,7 @@ else () unset(HAVE_TOML) endif () -set(IRMD_REQ_ARR_TIMEOUT 500 CACHE STRING +set(IRMD_REQ_ARR_TIMEOUT 1000 CACHE STRING "Timeout for an application to respond to a new flow (ms)") set(IRMD_FLOW_TIMEOUT 5000 CACHE STRING "Timeout for a flow allocation response (ms)") @@ -53,9 +53,13 @@ set(QUERY_TIMEOUT 3000 CACHE STRING set(CONNECT_TIMEOUT 60000 CACHE STRING "Timeout to connect an IPCP to another IPCP (ms)") set(IRMD_MIN_THREADS 8 CACHE STRING - "Minimum number of worker threads in the IRMd.") + "Minimum number of worker threads in the IRMd") set(IRMD_ADD_THREADS 8 CACHE STRING "Number of extra threads to start when the IRMD faces thread starvation") +set(IRMD_PKILL_TIMEOUT 30 CACHE STRING + "Number of seconds to wait before sending SIGKILL to subprocesses on exit") +set(IRMD_KILL_ALL_PROCESSES TRUE CACHE BOOL + "Kill all processes on exit") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY) @@ -65,12 +69,12 @@ set(SOURCE_FILES ipcp.c configfile.c main.c - utils.c reg/flow.c reg/ipcp.c reg/proc.c reg/prog.c reg/name.c + reg/reg.c ) add_executable (irmd ${SOURCE_FILES}) @@ -86,4 +90,5 @@ endif () install(TARGETS irmd RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}) # Enable once irmd has tests -# add_subdirectory(tests) +#add_subdirectory(tests) +add_subdirectory(reg) diff --git a/src/irmd/config.h.in b/src/irmd/config.h.in index a3505038..b25053f7 100644 --- a/src/irmd/config.h.in +++ b/src/irmd/config.h.in @@ -49,9 +49,10 @@ #define IRMD_MIN_THREADS @IRMD_MIN_THREADS@ #define IRMD_ADD_THREADS @IRMD_ADD_THREADS@ + #cmakedefine HAVE_FUSE #ifdef HAVE_FUSE -#define FUSE_PREFIX "@FUSE_PREFIX@" +#define FUSE_PREFIX "@FUSE_PREFIX@" #endif #cmakedefine HAVE_TOML @@ -60,6 +61,9 @@ #define OUROBOROS_CONFIG_FILE "@OUROBOROS_CONFIG_FILE@" #endif +#define IRMD_PKILL_TIMEOUT @IRMD_PKILL_TIMEOUT@ + +#cmakedefine IRMD_KILL_ALL_PROCESSES #cmakedefine HAVE_LIBGCRYPT #define O7S_ASCII_ART \ diff --git a/src/irmd/configfile.c b/src/irmd/configfile.c index 35f526a1..1537a1cc 100644 --- a/src/irmd/configfile.c +++ b/src/irmd/configfile.c @@ -25,27 +25,29 @@ #if defined (HAVE_TOML) -#define _XOPEN_SOURCE 500 +#define _POSIX_C_SOURCE 200809L +#define _XOPEN_SOURCE 500 #define OUROBOROS_PREFIX "irmd/configuration" -#include <toml.h> +#include <ouroboros/errno.h> +#include <ouroboros/ipcp.h> +#include <ouroboros/logs.h> +#include <ouroboros/utils.h> + +#include "irmd.h" +#include "configfile.h" + +#include "reg/reg.h" #include <assert.h> #include <errno.h> #include <inttypes.h> #include <stdlib.h> #include <string.h> +#include <toml.h> #include <arpa/inet.h> -#include <ouroboros/errno.h> -#include <ouroboros/ipcp.h> -#include <ouroboros/logs.h> - -#include "irmd.h" -#include "configfile.h" -#include "utils.h" - #define ERRBUFSZ 200 static int toml_hash(toml_table_t * table, @@ -361,9 +363,12 @@ static int toml_autobind(toml_table_t * table, static int toml_register(toml_table_t * table, pid_t pid) { - toml_array_t * reg; - int i; - int ret = 0; + toml_array_t * reg; + int i; + int ret = 0; + struct name_info info = { + .pol_lb = LB_SPILL + }; reg = toml_array_in(table, "reg"); if (reg == NULL) @@ -378,7 +383,9 @@ static int toml_register(toml_table_t * table, log_dbg("Registering %s in %d", name.u.s, pid); - ret = name_create(name.u.s, LB_SPILL); + strcpy(info.name, name.u.s); + + ret = name_create(&info); if (ret < 0 && ret != -ENAME) { free(name.u.s); break; @@ -422,19 +429,17 @@ static int toml_connect(toml_table_t * table, return ret; } -static int toml_ipcp(toml_table_t * table, - const struct ipcp_info * info, - struct ipcp_config * conf) +static int toml_ipcp(toml_table_t * table, + struct ipcp_info * info, + struct ipcp_config * conf) { toml_datum_t bootstrap; toml_datum_t enrol; - pid_t pid; int ret; log_dbg("Found IPCP %s in configuration file.", info->name); - pid = create_ipcp(info); - if (pid < 0) { + if (create_ipcp(info) < 0) { log_err("Failed to create IPCP %s.", info->name); return -1; } @@ -454,26 +459,26 @@ static int toml_ipcp(toml_table_t * table, } if (enrol.ok) { - char layer[LAYER_NAME_SIZE + 1]; - ret = enroll_ipcp(pid, enrol.u.s); + struct layer_info layer; + ret = enroll_ipcp(info->pid, enrol.u.s); free(enrol.u.s); if (ret < 0) { log_err("Failed to enrol %s.", info->name); return -1; } - if (get_layer_for_ipcp(pid, layer) < 0) + if (reg_get_ipcp(info, &layer) < 0) return -1; - if (toml_autobind(table, pid, info->name, layer)) + if (toml_autobind(table, info->pid, info->name, layer.name)) return -1; - if (toml_register(table, pid) < 0) { + if (toml_register(table, info->pid) < 0) { log_err("Failed to register names."); return -1; } - if (toml_connect(table, pid) < 0) { + if (toml_connect(table, info->pid) < 0) { log_err("Failed to register names."); return -1; } @@ -519,13 +524,14 @@ static int toml_ipcp(toml_table_t * table, strcpy(conf->layer_info.name, bootstrap.u.s); free(bootstrap.u.s); - if (bootstrap_ipcp(pid, conf) < 0) + if (bootstrap_ipcp(info->pid, conf) < 0) return -1; - if (toml_autobind(table, pid, info->name, conf->layer_info.name) < 0) + if (toml_autobind(table, info->pid, info->name, + conf->layer_info.name) < 0) return -1; - if (toml_register(table, pid) < 0) { + if (toml_register(table, info->pid) < 0) { log_err("Failed to register names."); return -1; } @@ -544,6 +550,9 @@ static int toml_ipcp_list(toml_table_t * table, struct ipcp_info info; struct ipcp_config conf; + memset(&conf, 0, sizeof(conf)); + memset(&info, 0, sizeof(info)); + key = toml_key_in(table, i); if (key == NULL) break; @@ -553,8 +562,6 @@ static int toml_ipcp_list(toml_table_t * table, return -1; } - memset(&conf, 0, sizeof(conf)); - info.type = type; strcpy(info.name,key); conf.type = type; @@ -565,31 +572,37 @@ static int toml_ipcp_list(toml_table_t * table, return ret; } -static int args_to_argv(const char * args, +static int args_to_argv(const char * prog, + const char * args, char *** argv) { char * tok; char * str; int argc = 0; - if (args == NULL) { - *argv = NULL; - return 0; - } - str = (char *) args; - tok = str; - while (*(tok += strspn(tok, " ")) != '\0') { - tok += strcspn(tok, " "); - argc++; + if (str != NULL) { + tok = str; + while (*(tok += strspn(tok, " ")) != '\0') { + tok += strcspn(tok, " "); + argc++; + } } - *argv = malloc((argc + 1) * sizeof(**argv)); + *argv = malloc((argc + 2) * sizeof(**argv)); if (*argv == NULL) goto fail_malloc; - argc = 0; + (*argv)[0] = strdup(prog); + if ((*argv)[0] == NULL) + goto fail_malloc2; + + argc++; + + if (str == NULL) + goto finish; + tok = str; while (*(tok += strspn(tok, " ")) != '\0') { size_t toklen = strcspn(tok, " "); @@ -602,6 +615,7 @@ static int args_to_argv(const char * args, tok += toklen; } + finish: (*argv)[argc] = NULL; return argc; @@ -613,30 +627,30 @@ static int args_to_argv(const char * args, } -static int toml_prog(char * prog, +static int toml_prog(const char * prog, const char * args, const char * name) { uint16_t flags = 0; int argc; - char ** argv; + char ** exec; int ret; if (args != NULL) flags |= BIND_AUTO; - argc = args_to_argv(args, &argv); + argc = args_to_argv(prog, args, &exec); if (argc < 0) { log_err("Failed to parse arguments: %s", args); return -1; } - ret = bind_program(prog, name, flags, argc, argv); + ret = bind_program(exec, name, flags); if (ret < 0) log_err("Failed to bind program %s %s for name %s.", prog, args, name); - argvfree(argv); + argvfree(exec); return ret; } @@ -683,27 +697,31 @@ static int toml_name(toml_table_t * table, toml_array_t * progs; toml_array_t * args; toml_datum_t lb; - enum pol_balance lb_pol = LB_SPILL; + struct name_info info = { + .pol_lb = LB_SPILL + }; log_dbg("Found service name %s in configuration file.", name); lb = toml_string_in(table, "lb"); if (lb.ok) { if (strcmp(lb.u.s, "spill") == 0) - lb_pol = LB_SPILL; + info.pol_lb = LB_SPILL; else if (strcmp(lb.u.s, "round-robin") == 0) - lb_pol = LB_RR; + info.pol_lb = LB_RR; else - lb_pol = LB_INVALID; + info.pol_lb = LB_INVALID; free(lb.u.s); } - if (lb_pol == LB_INVALID) { + if (info.pol_lb == LB_INVALID) { log_err("Invalid load-balancing policy for %s.", name); return -1; } - if (name_create(name, lb_pol) < 0) { + strcpy(info.name, name); + + if (name_create(&info) < 0) { log_err("Failed to create name %s.", name); return -1; } diff --git a/src/irmd/ipcp.c b/src/irmd/ipcp.c index 6f4d2c0f..3253a8f3 100644 --- a/src/irmd/ipcp.c +++ b/src/irmd/ipcp.c @@ -26,179 +26,124 @@ #define OUROBOROS_PREFIX "irmd/ipcp" -#include <ouroboros/logs.h> #include <ouroboros/errno.h> -#include <ouroboros/utils.h> +#include <ouroboros/flow.h> +#include <ouroboros/logs.h> #include <ouroboros/sockets.h> +#include <ouroboros/time.h> +#include <ouroboros/utils.h> #include "ipcp.h" -#include <stdlib.h> -#include <string.h> +#include <fcntl.h> +#include <pthread.h> #include <signal.h> +#include <spawn.h> #include <stdbool.h> -#include <pthread.h> +#include <stdlib.h> +#include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/socket.h> #include <sys/time.h> -#include <spawn.h> ipcp_msg_t * send_recv_ipcp_msg(pid_t pid, ipcp_msg_t * msg) { - int sockfd = 0; - uint8_t buf[SOCK_BUF_SIZE]; - char * sock_path = NULL; - ssize_t len; - ipcp_msg_t * recv_msg = NULL; - struct timeval tv; - - if (kill(pid, 0) < 0) - return NULL; - - sock_path = ipcp_sock_path(pid); - if (sock_path == NULL) - return NULL; - - sockfd = client_socket_open(sock_path); - if (sockfd < 0) { - free(sock_path); - return NULL; - } - - free(sock_path); - - len = ipcp_msg__get_packed_size(msg); - if (len == 0) { - close(sockfd); - return NULL; - } - - switch (msg->code) { - case IPCP_MSG_CODE__IPCP_BOOTSTRAP: - tv.tv_sec = BOOTSTRAP_TIMEOUT / 1000; - tv.tv_usec = (BOOTSTRAP_TIMEOUT % 1000) * 1000; - break; - case IPCP_MSG_CODE__IPCP_ENROLL: - tv.tv_sec = ENROLL_TIMEOUT / 1000; - tv.tv_usec = (ENROLL_TIMEOUT % 1000) * 1000; - break; - case IPCP_MSG_CODE__IPCP_REG: - tv.tv_sec = REG_TIMEOUT / 1000; - tv.tv_usec = (REG_TIMEOUT % 1000) * 1000; - break; - case IPCP_MSG_CODE__IPCP_QUERY: - tv.tv_sec = QUERY_TIMEOUT / 1000; - tv.tv_usec = (QUERY_TIMEOUT % 1000) * 1000; - break; - case IPCP_MSG_CODE__IPCP_CONNECT: - tv.tv_sec = CONNECT_TIMEOUT / 1000; - tv.tv_usec = (CONNECT_TIMEOUT % 1000) * 1000; - break; - default: - tv.tv_sec = SOCKET_TIMEOUT / 1000; - tv.tv_usec = (SOCKET_TIMEOUT % 1000) * 1000; - - break; - } - - if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, - (void *) &tv, sizeof(tv))) - log_warn("Failed to set timeout on socket."); - - pthread_cleanup_push(__cleanup_close_ptr, (void *) &sockfd); - - ipcp_msg__pack(msg, buf); - - if (write(sockfd, buf, len) != -1) - len = read(sockfd, buf, SOCK_BUF_SIZE); - - if (len > 0) - recv_msg = ipcp_msg__unpack(NULL, len, buf); - - pthread_cleanup_pop(true); - - return recv_msg; -} + int sockfd; + uint8_t buf[SOCK_BUF_SIZE]; + char * sock_path; + ssize_t len; + ipcp_msg_t * recv_msg; + struct timeval tv; + struct timespec tic; + struct timespec toc; + + if (kill(pid, 0) < 0) + return NULL; + + sock_path = ipcp_sock_path(pid); + if (sock_path == NULL) + return NULL; + + sockfd = client_socket_open(sock_path); + if (sockfd < 0) { + free(sock_path); + return NULL; + } -pid_t ipcp_create(const struct ipcp_info * info) -{ - pid_t pid; - char * exec_name = NULL; - char irmd_pid[10]; - char full_name[256]; - char * argv[5]; - - switch(info->type) { - case IPCP_UNICAST: - exec_name = IPCP_UNICAST_EXEC; - break; - case IPCP_BROADCAST: - exec_name = IPCP_BROADCAST_EXEC; + free(sock_path); + + len = ipcp_msg__get_packed_size(msg); + if (len == 0) { + close(sockfd); + return NULL; + } + + switch (msg->code) { + case IPCP_MSG_CODE__IPCP_BOOTSTRAP: + tv.tv_sec = BOOTSTRAP_TIMEOUT / 1000; + tv.tv_usec = (BOOTSTRAP_TIMEOUT % 1000) * 1000; break; - case IPCP_UDP: - exec_name = IPCP_UDP_EXEC; + case IPCP_MSG_CODE__IPCP_ENROLL: + tv.tv_sec = ENROLL_TIMEOUT / 1000; + tv.tv_usec = (ENROLL_TIMEOUT % 1000) * 1000; break; - case IPCP_ETH_LLC: - exec_name = IPCP_ETH_LLC_EXEC; + case IPCP_MSG_CODE__IPCP_REG: + tv.tv_sec = REG_TIMEOUT / 1000; + tv.tv_usec = (REG_TIMEOUT % 1000) * 1000; break; - case IPCP_ETH_DIX: - exec_name = IPCP_ETH_DIX_EXEC; + case IPCP_MSG_CODE__IPCP_QUERY: + tv.tv_sec = QUERY_TIMEOUT / 1000; + tv.tv_usec = (QUERY_TIMEOUT % 1000) * 1000; break; - case IPCP_LOCAL: - exec_name = IPCP_LOCAL_EXEC; + case IPCP_MSG_CODE__IPCP_CONNECT: + tv.tv_sec = CONNECT_TIMEOUT / 1000; + tv.tv_usec = (CONNECT_TIMEOUT % 1000) * 1000; break; default: - return -1; + tv.tv_sec = SOCKET_TIMEOUT / 1000; + tv.tv_usec = (SOCKET_TIMEOUT % 1000) * 1000; + break; } - if (exec_name == NULL) { - log_err("IPCP type not installed."); - return -1; - } + if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, + (void *) &tv, sizeof(tv))) + log_warn("Failed to set timeout on socket."); - sprintf(irmd_pid, "%u", getpid()); + pthread_cleanup_push(__cleanup_close_ptr, (void *) &sockfd); - strcpy(full_name, INSTALL_PREFIX"/"INSTALL_SBINDIR"/"); - strcat(full_name, exec_name); + ipcp_msg__pack(msg, buf); - /* log_file to be placed at the end */ - argv[0] = full_name; - argv[1] = irmd_pid; - argv[2] = (char *) info->name; - if (log_syslog) - argv[3] = "1"; - else - argv[3] = NULL; + clock_gettime(CLOCK_REALTIME, &tic); - argv[4] = NULL; + if (write(sockfd, buf, len) != -1) + len = read(sockfd, buf, SOCK_BUF_SIZE); - if (posix_spawn(&pid, argv[0], NULL, NULL, argv, NULL) != 0) { - log_err("Failed to spawn new process"); - return -1; - } + clock_gettime(CLOCK_REALTIME, &toc); - return pid; -} + pthread_cleanup_pop(true); /* close socket */ -int ipcp_destroy(pid_t pid) -{ - if (kill(pid, SIGTERM)) { - log_err("Failed to destroy IPCP"); - return -1; + if (len > 0) + recv_msg = ipcp_msg__unpack(NULL, len, buf); + else { + if (errno == EAGAIN) { + int diff = ts_diff_ms(&tic, &toc); + log_warn("IPCP command timed out after %d ms.", diff); + } + return NULL; } - return 0; + return recv_msg; } int ipcp_bootstrap(pid_t pid, struct ipcp_config * conf, struct layer_info * info) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; if (conf == NULL) return -EINVAL; @@ -240,9 +185,9 @@ int ipcp_enroll(pid_t pid, const char * dst, struct layer_info * info) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; if (dst == NULL) return -EINVAL; @@ -283,9 +228,9 @@ int ipcp_connect(pid_t pid, const char * component, qosspec_t qs) { - ipcp_msg_t msg = IPCP_MSG__INIT; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; ipcp_msg_t * recv_msg; + int ret; msg.code = IPCP_MSG_CODE__IPCP_CONNECT; msg.dst = (char *) dst; @@ -314,9 +259,9 @@ int ipcp_disconnect(pid_t pid, const char * dst, const char * component) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; msg.code = IPCP_MSG_CODE__IPCP_DISCONNECT; msg.dst = (char *) dst; @@ -339,20 +284,17 @@ int ipcp_disconnect(pid_t pid, return ret; } -int ipcp_reg(pid_t pid, - const uint8_t * hash, - size_t len) +int ipcp_reg(pid_t pid, + const buffer_t hash) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; - - assert(hash); + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; msg.code = IPCP_MSG_CODE__IPCP_REG; msg.has_hash = true; - msg.hash.len = len; - msg.hash.data = (uint8_t *)hash; + msg.hash.data = (uint8_t *) hash.data; + msg.hash.len = hash.len; recv_msg = send_recv_ipcp_msg(pid, &msg); if (recv_msg == NULL) @@ -369,18 +311,17 @@ int ipcp_reg(pid_t pid, return ret; } -int ipcp_unreg(pid_t pid, - const uint8_t * hash, - size_t len) +int ipcp_unreg(pid_t pid, + const buffer_t hash) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; msg.code = IPCP_MSG_CODE__IPCP_UNREG; msg.has_hash = true; - msg.hash.len = len; - msg.hash.data = (uint8_t *) hash; + msg.hash.data = (uint8_t *) hash.data; + msg.hash.len = hash.len; recv_msg = send_recv_ipcp_msg(pid, &msg); if (recv_msg == NULL) @@ -397,18 +338,17 @@ int ipcp_unreg(pid_t pid, return ret; } -int ipcp_query(pid_t pid, - const uint8_t * hash, - size_t len) +int ipcp_query(pid_t pid, + const buffer_t dst) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; msg.code = IPCP_MSG_CODE__IPCP_QUERY; msg.has_hash = true; - msg.hash.len = len; - msg.hash.data = (uint8_t *) hash; + msg.hash.data = (uint8_t *) dst.data; + msg.hash.len = dst.len; recv_msg = send_recv_ipcp_msg(pid, &msg); if (recv_msg == NULL) @@ -425,36 +365,24 @@ int ipcp_query(pid_t pid, return ret; } -static int __ipcp_flow_alloc(pid_t pid, - int flow_id, - pid_t n_pid, - const uint8_t * dst, - size_t len, - qosspec_t qs, - bool join, - const buffer_t data) +int ipcp_flow_join(const struct flow_info * flow, + const buffer_t dst) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; - - assert(dst); + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; - msg.code = join ? IPCP_MSG_CODE__IPCP_FLOW_JOIN - : IPCP_MSG_CODE__IPCP_FLOW_ALLOC; + msg.code = IPCP_MSG_CODE__IPCP_FLOW_JOIN; msg.has_flow_id = true; - msg.flow_id = flow_id; + msg.flow_id = flow->id; msg.has_pid = true; - msg.pid = n_pid; + msg.pid = flow->n_pid; msg.has_hash = true; - msg.hash.len = len; - msg.hash.data = (uint8_t *) dst; - msg.qosspec = qos_spec_s_to_msg(&qs);; - msg.has_pk = true; - msg.pk.data = data.data; - msg.pk.len = data.len; + msg.hash.data = (uint8_t *) dst.data; + msg.hash.len = dst.len; + msg.has_pk = false; - recv_msg = send_recv_ipcp_msg(pid, &msg); + recv_msg = send_recv_ipcp_msg(flow->n_1_pid, &msg); free(msg.qosspec); if (recv_msg == NULL) return -EIPCP; @@ -470,52 +398,66 @@ static int __ipcp_flow_alloc(pid_t pid, return ret; } -int ipcp_flow_alloc(pid_t pid, - int flow_id, - pid_t n_pid, - const uint8_t * dst, - size_t len, - qosspec_t qs, - const buffer_t data) +int ipcp_flow_alloc(const struct flow_info * flow, + const buffer_t dst, + const buffer_t data) { - return __ipcp_flow_alloc(pid, flow_id, n_pid, dst, - len, qs, false, data); -} + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; -int ipcp_flow_join(pid_t pid, - int flow_id, - pid_t n_pid, - const uint8_t * dst, - size_t len, - qosspec_t qs) -{ - buffer_t data = {NULL, 0}; - return __ipcp_flow_alloc(pid, flow_id, n_pid, dst, - len, qs, true, data); + msg.code = IPCP_MSG_CODE__IPCP_FLOW_ALLOC; + msg.has_flow_id = true; + msg.flow_id = flow->id; + msg.has_pid = true; + msg.pid = flow->n_pid; + msg.qosspec = qos_spec_s_to_msg(&flow->qs); + msg.has_hash = true; + msg.hash.data = (uint8_t *) dst.data; + msg.hash.len = dst.len; + msg.has_pk = true; + msg.pk.data = data.data; + msg.pk.len = data.len; + + recv_msg = send_recv_ipcp_msg(flow->n_1_pid, &msg); + free(msg.qosspec); + if (recv_msg == NULL) { + log_err("Did not receive message."); + return -EIPCP; + } + + if (!recv_msg->has_result) { + log_err("Message has no result"); + ipcp_msg__free_unpacked(recv_msg, NULL); + return -EIPCP; + } + + ret = recv_msg->result; + ipcp_msg__free_unpacked(recv_msg, NULL); + + return ret; } -int ipcp_flow_alloc_resp(pid_t pid, - int flow_id, - pid_t n_pid, - int response, - const buffer_t data) +int ipcp_flow_alloc_resp(const struct flow_info * flow, + int response, + const buffer_t data) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; msg.code = IPCP_MSG_CODE__IPCP_FLOW_ALLOC_RESP; msg.has_flow_id = true; - msg.flow_id = flow_id; + msg.flow_id = flow->id; msg.has_pid = true; - msg.pid = n_pid; + msg.pid = flow->n_pid; msg.has_response = true; msg.response = response; msg.has_pk = true; msg.pk.data = data.data; msg.pk.len = data.len; - recv_msg = send_recv_ipcp_msg(pid, &msg); + recv_msg = send_recv_ipcp_msg(flow->n_1_pid, &msg); if (recv_msg == NULL) return -EIPCP; @@ -534,9 +476,9 @@ int ipcp_flow_dealloc(pid_t pid, int flow_id, time_t timeo) { - ipcp_msg_t msg = IPCP_MSG__INIT; - ipcp_msg_t * recv_msg = NULL; - int ret = -1; + ipcp_msg_t msg = IPCP_MSG__INIT; + ipcp_msg_t * recv_msg; + int ret; msg.code = IPCP_MSG_CODE__IPCP_FLOW_DEALLOC; msg.has_flow_id = true; diff --git a/src/irmd/ipcp.h b/src/irmd/ipcp.h index 28366c62..b7413cd2 100644 --- a/src/irmd/ipcp.h +++ b/src/irmd/ipcp.h @@ -24,15 +24,9 @@ #include <ouroboros/protobuf.h> #include <ouroboros/sockets.h> -#include <sys/types.h> - #ifndef OUROBOROS_IRMD_IPCP_H #define OUROBOROS_IRMD_IPCP_H -pid_t ipcp_create(const struct ipcp_info * info); - -int ipcp_destroy(pid_t pid); - int ipcp_enroll(pid_t pid, const char * dst, struct layer_info * info); @@ -50,38 +44,25 @@ int ipcp_disconnect(pid_t pid, const char * dst, const char * component); -int ipcp_reg(pid_t pid, - const uint8_t * hash, - size_t len); +int ipcp_reg(pid_t pid, + const buffer_t hash); -int ipcp_unreg(pid_t pid, - const uint8_t * hash, - size_t len); +int ipcp_unreg(pid_t pid, + const buffer_t hash); -int ipcp_query(pid_t pid, - const uint8_t * hash, - size_t len); +int ipcp_query(pid_t pid, + const buffer_t dst); -int ipcp_flow_alloc(pid_t pid, - int flow_id, - pid_t n_pid, - const uint8_t * dst, - size_t len, - qosspec_t qs, - const buffer_t data); +int ipcp_flow_alloc(const struct flow_info * flow, + const buffer_t hash, + const buffer_t data); -int ipcp_flow_join(pid_t pid, - int flow_id, - pid_t n_pid, - const uint8_t * dst, - size_t len, - qosspec_t qs); +int ipcp_flow_join(const struct flow_info * flow, + const buffer_t dst); -int ipcp_flow_alloc_resp(pid_t pid, - int flow_id, - pid_t n_pid, - int response, - const buffer_t data); +int ipcp_flow_alloc_resp(const struct flow_info * flow, + int response, + const buffer_t data); int ipcp_flow_dealloc(pid_t pid, int flow_id, diff --git a/src/irmd/irmd.h b/src/irmd/irmd.h index 2059ad55..6db8b5bd 100644 --- a/src/irmd/irmd.h +++ b/src/irmd/irmd.h @@ -26,7 +26,7 @@ #include <ouroboros/ipcp.h> #include <ouroboros/irm.h> -int create_ipcp(const struct ipcp_info * info); +int create_ipcp(struct ipcp_info * info); int bootstrap_ipcp(pid_t pid, struct ipcp_config * conf); @@ -39,11 +39,7 @@ int connect_ipcp(pid_t pid, const char * component, qosspec_t qs); -int get_layer_for_ipcp(pid_t pid, - char * buf); - -int name_create(const char * name, - enum pol_balance pol); +int name_create(const struct name_info * info); int name_reg(const char * name, pid_t pid); @@ -51,10 +47,8 @@ int name_reg(const char * name, int bind_process(pid_t pid, const char * name); -int bind_program(const char * prog, - const char * name, - uint16_t flags, - int argc, - char ** argv); +int bind_program(char ** exec, + const char * name, + uint8_t flags); #endif /* OUROBOROS_IRMD_H*/
\ No newline at end of file diff --git a/src/irmd/main.c b/src/irmd/main.c index 31ac5edc..2cbe8ed4 100644 --- a/src/irmd/main.c +++ b/src/irmd/main.c @@ -40,23 +40,18 @@ #include <ouroboros/lockfile.h> #include <ouroboros/logs.h> #include <ouroboros/pthread.h> -#include <ouroboros/shm_rbuff.h> +#include <ouroboros/rib.h> #include <ouroboros/shm_rdrbuff.h> #include <ouroboros/sockets.h> -#include <ouroboros/time_utils.h> +#include <ouroboros/time.h> #include <ouroboros/tpm.h> #include <ouroboros/utils.h> #include <ouroboros/version.h> #include "irmd.h" #include "ipcp.h" -#include "reg/flow.h" -#include "reg/ipcp.h" -#include "reg/name.h" -#include "reg/proc.h" -#include "reg/prog.h" +#include "reg/reg.h" #include "configfile.h" -#include "utils.h" #include <sys/socket.h> #include <sys/un.h> @@ -79,12 +74,10 @@ #define DEALLOC_TIME 300 /* s */ #define MSGBUFSZ 2048 -#define registry_has_name(name) \ - (registry_get_name(name) != NULL) - enum irm_state { IRMD_NULL = 0, - IRMD_RUNNING + IRMD_RUNNING, + IRMD_SHUTDOWN }; struct cmd { @@ -97,22 +90,6 @@ struct cmd { struct { bool log_stdout; /* log to stdout */ - - struct list_head names; /* registered names known */ - size_t n_names; /* number of names */ - - struct list_head ipcps; /* list of ipcps in system */ - size_t n_ipcps; /* number of ipcps */ - - struct list_head procs; /* processes */ - struct list_head progs; /* programs known */ - struct list_head spawned_pids; /* child processes */ - - struct bmp * flow_ids; /* flow_ids for flows */ - struct list_head flows; /* flow information */ - - pthread_rwlock_t reg_lock; /* lock for registration info */ - #ifdef HAVE_TOML char * cfg_file; /* configuration file path */ #endif @@ -156,574 +133,235 @@ static void irmd_set_state(enum irm_state state) pthread_rwlock_unlock(&irmd.state_lock); } -static void clear_reg_flow(struct reg_flow * f) { - ssize_t idx; - - assert(f); - - if (f->data.len != 0) { - free(f->data.data); - f->data.len = 0; - } - - while ((idx = shm_rbuff_read(f->n_rb)) >= 0) - shm_rdrbuff_remove(irmd.rdrb, idx); - - while ((idx = shm_rbuff_read(f->n_1_rb)) >= 0) - shm_rdrbuff_remove(irmd.rdrb, idx); -} - -static struct reg_flow * registry_get_flow(int flow_id) -{ - struct list_head * p; - - list_for_each(p, &irmd.flows) { - struct reg_flow * f = list_entry(p, struct reg_flow, next); - if (f->flow_id == flow_id) - return f; - } - - return NULL; -} - -static struct reg_flow * registry_get_pending_flow_for_pid(pid_t n_pid) -{ - struct list_head * p; - - list_for_each(p, &irmd.flows) { - struct reg_flow * e = list_entry(p, struct reg_flow, next); - enum flow_state state = reg_flow_get_state(e); - if (e->n_pid == n_pid && state == FLOW_ALLOC_REQ_PENDING) - return e; - } - - return NULL; -} - -static int registry_add_ipcp(struct reg_ipcp * ipcp) -{ - struct list_head * p; - - assert(ipcp); - - list_for_each(p, &irmd.ipcps) { - struct reg_ipcp * i; - i = list_entry(p, struct reg_ipcp, next); - if (i->info.type > ipcp->info.type) - break; - } - - list_add_tail(&ipcp->next, p); - ++irmd.n_ipcps; - - return 0; -} - -static struct reg_ipcp * registry_get_ipcp_by_pid(pid_t pid) +static pid_t spawn_program(char ** argv) { - struct list_head * p; - - list_for_each(p, &irmd.ipcps) { - struct reg_ipcp * ipcp; - ipcp = list_entry(p, struct reg_ipcp, next); - if (ipcp->pid == pid) - return ipcp; - } - - return NULL; -} - -static void registry_del_ipcp(pid_t pid) -{ - struct reg_ipcp * ipcp; - - ipcp = registry_get_ipcp_by_pid(pid); - if (ipcp == NULL) - return; - - list_del(&ipcp->next); - reg_ipcp_destroy(ipcp); - --irmd.n_ipcps; -} - -static struct reg_ipcp * registry_get_ipcp_by_name(const char * name) -{ - struct list_head * p; + pid_t pid; + struct stat s; - list_for_each(p, &irmd.ipcps) { - struct reg_ipcp * ipcp; - ipcp = list_entry(p, struct reg_ipcp, next); - if (strcmp(name, ipcp->info.name) == 0) - return ipcp; + if (stat(argv[0], &s) != 0) { + log_warn("Program %s does not exist.", argv[0]); + return -1; } - return NULL; -} - -static struct reg_ipcp * registry_get_ipcp_by_layer(const char * layer) -{ - struct list_head * p; - - list_for_each(p, &irmd.ipcps) { - struct reg_ipcp * ipcp; - ipcp = list_entry(p, struct reg_ipcp, next); - if (strcmp(layer, ipcp->layer) == 0) - return ipcp; + if (!(s.st_mode & S_IXUSR)) { + log_warn("Program %s is not executable.", argv[0]); + return -1; } - return NULL; -} - -static struct reg_ipcp * registry_get_ipcp_by_dst_name(const char * name, - pid_t src) -{ - struct list_head * p; - struct list_head * h; - uint8_t * hash; - pid_t pid; - size_t len; - - pthread_rwlock_rdlock(&irmd.reg_lock); - - list_for_each_safe(p, h, &irmd.ipcps) { - struct reg_ipcp * ipcp; - ipcp = list_entry(p, struct reg_ipcp, next); - if (ipcp->layer == NULL) - continue; - - if (ipcp->pid == src) - continue; - - if (ipcp->info.type == IPCP_BROADCAST) - continue; - - len = IPCP_HASH_LEN(ipcp); - - hash = malloc(len); - if (hash == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - return NULL; - } - - str_hash(ipcp->dir_hash_algo, hash, name); - - pid = ipcp->pid; - - pthread_rwlock_unlock(&irmd.reg_lock); - - if (ipcp_query(pid, hash, len) == 0) { - free(hash); - return ipcp; - } - - free(hash); - - pthread_rwlock_rdlock(&irmd.reg_lock); + if (posix_spawn(&pid, argv[0], NULL, NULL, argv, NULL)) { + log_err("Failed to spawn new process for %s.", argv[0]); + return -1; } - pthread_rwlock_unlock(&irmd.reg_lock); - - return NULL; -} - -int get_layer_for_ipcp(pid_t pid, - char * buf) -{ - struct reg_ipcp * ipcp; - - pthread_rwlock_rdlock(&irmd.reg_lock); - - ipcp = registry_get_ipcp_by_pid(pid); - if (ipcp == NULL || ipcp->layer == NULL) - goto fail; - - strcpy(buf, ipcp->layer); - - pthread_rwlock_unlock(&irmd.reg_lock); - - return 0; + log_info("Instantiated %s as process %d.", argv[0], pid); - fail: - pthread_rwlock_unlock(&irmd.reg_lock); - return -1; + return pid; } -static struct reg_name * registry_get_name(const char * name) +static pid_t spawn_ipcp(struct ipcp_info * info) { - struct list_head * p; + char * exec_name = NULL; + char irmd_pid[10]; + char full_name[256]; + char * argv[5]; + pid_t pid; - list_for_each(p, &irmd.names) { - struct reg_name * e = list_entry(p, struct reg_name, next); - if (!strcmp(name, e->name)) - return e; + switch(info->type) { + case IPCP_UNICAST: + exec_name = IPCP_UNICAST_EXEC; + break; + case IPCP_BROADCAST: + exec_name = IPCP_BROADCAST_EXEC; + break; + case IPCP_UDP: + exec_name = IPCP_UDP_EXEC; + break; + case IPCP_ETH_LLC: + exec_name = IPCP_ETH_LLC_EXEC; + break; + case IPCP_ETH_DIX: + exec_name = IPCP_ETH_DIX_EXEC; + break; + case IPCP_LOCAL: + exec_name = IPCP_LOCAL_EXEC; + break; + default: + assert(false); } - return NULL; -} - -static struct reg_name * registry_get_name_by_hash(enum hash_algo algo, - const uint8_t * hash, - size_t len) -{ - struct list_head * p; - uint8_t * thash; - - thash = malloc(len); - if (thash == NULL) - return NULL; - - list_for_each(p, &irmd.names) { - struct reg_name * n = list_entry(p, struct reg_name, next); - str_hash(algo, thash, n->name); - if (memcmp(thash, hash, len) == 0) { - free(thash); - return n; - } + if (exec_name == NULL) { + log_err("IPCP type not installed."); + return -1; } - free(thash); - - return NULL; -} - -static int registry_add_name(struct reg_name * n) -{ - - assert(n); - - list_add(&n->next, &irmd.names); - - ++irmd.n_names; - - return 0; -} - -static void registry_del_name(const char * name) -{ - struct reg_name * n; - - n = registry_get_name(name); - if (n == NULL) - return; - - list_del(&n->next); - reg_name_destroy(n); - --irmd.n_names; -} - -static void registry_names_del_proc(pid_t pid) -{ - struct list_head * p; + sprintf(irmd_pid, "%u", getpid()); - assert(pid > 0); + strcpy(full_name, INSTALL_PREFIX"/"INSTALL_SBINDIR"/"); + strcat(full_name, exec_name); - list_for_each(p, &irmd.names) { - struct reg_name * n = list_entry(p, struct reg_name, next); - reg_name_del_pid(n, pid); - } - - return; -} + /* log_file to be placed at the end */ + argv[0] = full_name; + argv[1] = irmd_pid; + argv[2] = (char *) info->name; + if (log_syslog) + argv[3] = "1"; + else + argv[3] = NULL; -static void registry_destroy_names(void) -{ - struct list_head * p; - struct list_head * h; + argv[4] = NULL; - list_for_each_safe(p, h, &irmd.names) { - struct reg_name * n = list_entry(p, struct reg_name, next); - list_del(&n->next); - reg_name_set_state(n, NAME_NULL); - reg_name_destroy(n); + pid = spawn_program(argv); + if (pid < 0) { + log_err("Failed to spawn IPCP %s.", info->name); + return -1; } -} - -static int registry_add_prog(struct reg_prog * p) -{ - assert(p); - list_add(&p->next, &irmd.progs); + info->pid = pid; + info->state = IPCP_BOOT; return 0; } -static void registry_del_prog(const char * prog) +static int kill_ipcp(pid_t pid) { - struct list_head * p; - struct list_head * h; + int status; - assert(prog); - - list_for_each_safe(p, h, &irmd.progs) { - struct reg_prog * e = list_entry(p, struct reg_prog, next); - if (!strcmp(prog, e->prog)) { - list_del(&e->next); - reg_prog_destroy(e); - } + if (kill(pid, SIGTERM) < 0) { + log_err("Failed to destroy IPCP: %s.", strerror(errno)); + return -1; } -} - -static struct reg_prog * registry_get_prog(const char * prog) -{ - struct list_head * p; - assert(prog); - - list_for_each(p, &irmd.progs) { - struct reg_prog * e = list_entry(p, struct reg_prog, next); - if (!strcmp(e->prog, prog)) - return e; - } - - return NULL; -} - -static int registry_add_proc(struct reg_proc * p) -{ - assert(p); - - list_add(&p->next, &irmd.procs); + waitpid(pid, &status, 0); return 0; } -static void registry_del_proc(pid_t pid) -{ - struct list_head * p; - struct list_head * h; - - list_for_each_safe(p, h, &irmd.procs) { - struct reg_proc * e = list_entry(p, struct reg_proc, next); - if (pid == e->pid) { - list_del(&e->next); - reg_proc_destroy(e); - } - } -} - -static struct reg_proc * registry_get_proc(pid_t pid) +int create_ipcp(struct ipcp_info * info) { - struct list_head * p; - - list_for_each(p, &irmd.procs) { - struct reg_proc * e = list_entry(p, struct reg_proc, next); - if (pid == e->pid) - return e; - } - - return NULL; -} + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_MS(SOCKET_TIMEOUT); + int status; -pid_t create_ipcp(const struct ipcp_info * info) -{ - struct pid_el * ppid; - struct reg_ipcp * ipcp; - pid_t pid; + assert(info->pid == 0); - pthread_rwlock_rdlock(&irmd.reg_lock); + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); - ipcp = registry_get_ipcp_by_name(info->name); - if (ipcp != NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("IPCP by that name already exists."); - return -EPERM; + if (spawn_ipcp(info) < 0) { + log_err("Failed to create IPCP."); + goto fail_ipcp; } - pthread_rwlock_unlock(&irmd.reg_lock); - - ppid = malloc(sizeof(*ppid)); - if (ppid == NULL) - goto fail_ppid; - - ipcp = reg_ipcp_create(info); - if (ipcp == NULL) { + if (reg_create_ipcp(info) < 0) { log_err("Failed to create IPCP entry."); goto fail_reg_ipcp; } - pid = ipcp_create(info); - if (pid == -1) { - log_err("Failed to create IPCP."); - goto fail_ipcp; + if (reg_wait_ipcp_boot(info, &abstime)) { + log_err("IPCP %d failed to boot.", info->pid); + goto fail_boot; } - ipcp->pid = pid; - - pthread_rwlock_wrlock(&irmd.reg_lock); - - registry_add_ipcp(ipcp); - - ppid->pid = ipcp->pid; - list_add(&ppid->next, &irmd.spawned_pids); + log_info("Created IPCP %d.", info->pid); - pthread_rwlock_unlock(&irmd.reg_lock); - - /* IRMd maintenance will clean up if booting fails. */ - if (reg_ipcp_wait_boot(ipcp)) { - log_err("IPCP %d failed to boot.", pid); - return -1; - } - - log_info("Created IPCP %d.", pid); + return 0; - return pid; + fail_boot: + waitpid(info->pid, &status, 0); + reg_destroy_ipcp(info->pid); + return -1; - fail_ipcp: - reg_ipcp_destroy(ipcp); fail_reg_ipcp: - free(ppid); - fail_ppid: + kill_ipcp(info->pid); + fail_ipcp: return -1; } -static int create_ipcp_r(pid_t pid, - int result) +static int create_ipcp_r(struct ipcp_info * info) { - struct list_head * p; - - pthread_rwlock_rdlock(&irmd.reg_lock); - - list_for_each(p, &irmd.ipcps) { - struct reg_ipcp * e = list_entry(p, struct reg_ipcp, next); - if (e->pid == pid) { - enum ipcp_state state; - state = result ? IPCP_NULL : IPCP_OPERATIONAL; - reg_ipcp_set_state(e, state); - break; - } - } - - pthread_rwlock_unlock(&irmd.reg_lock); - - return 0; -} - -static void clear_spawned_process(pid_t pid) -{ - struct list_head * p; - struct list_head * h; - - list_for_each_safe(p, h, &(irmd.spawned_pids)) { - struct pid_el * a = list_entry(p, struct pid_el, next); - if (a->pid == pid) { - list_del(&a->next); - free(a); - } - } + return reg_respond_ipcp(info); } static int destroy_ipcp(pid_t pid) { - pthread_rwlock_wrlock(&irmd.reg_lock); - - registry_del_ipcp(pid); - - clear_spawned_process(pid); - - pthread_rwlock_unlock(&irmd.reg_lock); - - if (ipcp_destroy(pid)) + if (kill_ipcp(pid)) { log_err("Could not destroy IPCP."); + goto fail; + } + + if (reg_destroy_ipcp(pid)) { + log_err("Failed to remove IPCP from registry."); + goto fail; + } return 0; + fail: + return -1; } int bootstrap_ipcp(pid_t pid, struct ipcp_config * conf) { - struct reg_ipcp * ipcp; - struct layer_info info; + struct ipcp_info info; + struct layer_info layer; - pthread_rwlock_wrlock(&irmd.reg_lock); + info.pid = pid; - ipcp = registry_get_ipcp_by_pid(pid); - if (ipcp == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("No such IPCP: %d.", pid); - return -1; + if (reg_get_ipcp(&info, NULL) < 0) { + log_err("Could not find IPCP %d.", pid); + goto fail; } - if (ipcp->info.type != conf->type) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Configuration does not match IPCP type."); - return -1; - } + if (conf->type == IPCP_UDP) + conf->layer_info.dir_hash_algo = (enum pol_dir_hash) HASH_MD5; - if (ipcp_bootstrap(ipcp->pid, conf, &info)) { - pthread_rwlock_unlock(&irmd.reg_lock); + if (ipcp_bootstrap(pid, conf, &layer)) { log_err("Could not bootstrap IPCP."); - return -1; - } - - ipcp->layer = strdup(info.name); - if (ipcp->layer == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_warn("Failed to set name of layer."); - return -ENOMEM; + goto fail; } - ipcp->dir_hash_algo = (enum hash_algo) info.dir_hash_algo; + info.state = IPCP_BOOTSTRAPPED; - pthread_rwlock_unlock(&irmd.reg_lock); + if (reg_set_layer_for_ipcp(&info, &layer) < 0) { + log_err("Failed to set layer info for IPCP."); + goto fail; + } - log_info("Bootstrapped IPCP %d in layer %s.", - pid, conf->layer_info.name); + log_info("Bootstrapped IPCP %d.", pid); return 0; + fail: + return -1; } int enroll_ipcp(pid_t pid, const char * dst) { - struct reg_ipcp * ipcp; - struct layer_info info; - - pthread_rwlock_rdlock(&irmd.reg_lock); + struct layer_info layer; + struct ipcp_info info; - ipcp = registry_get_ipcp_by_pid(pid); - if (ipcp == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("No such IPCP."); - return -1; - } + info.pid = pid; - if (ipcp->layer != NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("IPCP in wrong state"); - return -1; + if (reg_get_ipcp(&info, NULL) < 0) { + log_err("Could not find IPCP."); + goto fail; } - pthread_rwlock_unlock(&irmd.reg_lock); - - if (ipcp_enroll(pid, dst, &info) < 0) { + if (ipcp_enroll(pid, dst, &layer) < 0) { log_err("Could not enroll IPCP %d.", pid); - return -1; - } - - pthread_rwlock_wrlock(&irmd.reg_lock); - - ipcp = registry_get_ipcp_by_pid(pid); - if (ipcp == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("No such IPCP."); - return -1; + goto fail; } - ipcp->layer = strdup(info.name); - if (ipcp->layer == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Failed to strdup layer_name."); - return -ENOMEM; + if (reg_set_layer_for_ipcp(&info, &layer) < 0) { + log_err("Failed to set layer info for IPCP."); + goto fail; } - ipcp->dir_hash_algo = (enum hash_algo) info.dir_hash_algo; - - pthread_rwlock_unlock(&irmd.reg_lock); - - log_info("Enrolled IPCP %d in layer %s.", - pid, info.name); + log_info("Enrolled IPCP %d in layer %s.", pid, layer.name); return 0; + fail: + return -1; } int connect_ipcp(pid_t pid, @@ -731,28 +369,20 @@ int connect_ipcp(pid_t pid, const char * component, qosspec_t qs) { - struct reg_ipcp * ipcp; - struct ipcp_info * info; + struct ipcp_info info; - pthread_rwlock_rdlock(&irmd.reg_lock); + info.pid = pid; - ipcp = registry_get_ipcp_by_pid(pid); - if (ipcp == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); + if (reg_get_ipcp(&info, NULL) < 0) { log_err("No such IPCP."); return -EIPCP; } - info = &ipcp->info; - - if (info->type != IPCP_UNICAST && info->type != IPCP_BROADCAST) { - pthread_rwlock_unlock(&irmd.reg_lock); + if (info.type != IPCP_UNICAST && info.type != IPCP_BROADCAST) { log_err("Cannot establish connections for this IPCP type."); return -EIPCP; } - pthread_rwlock_unlock(&irmd.reg_lock); - log_dbg("Connecting %s to %s.", component, dst); if (ipcp_connect(pid, dst, component, qs)) { @@ -770,28 +400,20 @@ static int disconnect_ipcp(pid_t pid, const char * dst, const char * component) { - struct reg_ipcp * ipcp; - struct ipcp_info * info; + struct ipcp_info info; - pthread_rwlock_rdlock(&irmd.reg_lock); + info.pid = pid; - ipcp = registry_get_ipcp_by_pid(pid); - if (ipcp == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); + if (reg_get_ipcp(&info, NULL) < 0) { log_err("No such IPCP."); return -EIPCP; } - info = &ipcp->info; - - if (info->type != IPCP_UNICAST && info->type != IPCP_BROADCAST) { - pthread_rwlock_unlock(&irmd.reg_lock); + if (info.type != IPCP_UNICAST && info.type != IPCP_BROADCAST) { log_err("Cannot tear down connections for this IPCP type."); return -EIPCP; } - pthread_rwlock_unlock(&irmd.reg_lock); - if (ipcp_disconnect(pid, dst, component)) { log_err("Could not disconnect IPCP."); return -EPERM; @@ -803,95 +425,99 @@ static int disconnect_ipcp(pid_t pid, return 0; } -int bind_program(const char * prog, - const char * name, - uint16_t flags, - int argc, - char ** argv) +int bind_program(char ** exec, + const char * name, + uint8_t flags) { - struct reg_prog * p; - struct reg_name * n; + struct prog_info prog; + struct name_info ni; - if (prog == NULL || name == NULL) + if (name == NULL || exec == NULL || exec[0] == NULL) return -EINVAL; - pthread_rwlock_wrlock(&irmd.reg_lock); + memset(&prog, 0, sizeof(prog)); + memset(&ni, 0, sizeof(ni)); - p = registry_get_prog(path_strip(prog)); - if (p == NULL) { - p = reg_prog_create(prog, flags, argc, argv); - if (p == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - return -ENOMEM; - } - - registry_add_prog(p); + if (!reg_has_prog(exec[0])) { + strcpy(prog.name, path_strip(exec[0])); + strcpy(prog.path, exec[0]); + if (reg_create_prog(&prog) < 0) + goto fail_prog; } - if (reg_prog_add_name(p, name)) { - log_err("Failed adding name."); - pthread_rwlock_unlock(&irmd.reg_lock); - return -ENOMEM; + if (!reg_has_name(name)) { + ni.pol_lb = LB_SPILL; + strcpy(ni.name, name); + if (reg_create_name(&ni) < 0) { + log_err("Failed to create name %s.", name); + goto fail_name; + } } - n = registry_get_name(name); - if (n != NULL && reg_name_add_prog(n, p) < 0) - log_err("Failed adding program %s for name %s.", prog, name); - - pthread_rwlock_unlock(&irmd.reg_lock); + if (reg_bind_prog(name, exec, flags) < 0) { + log_err("Failed to bind program %s to name %s", exec[0], name); + goto fail_bind; + } - log_info("Bound program %s to name %s.", prog, name); + log_info("Bound program %s to name %s.", exec[0], name); return 0; + + fail_bind: + if (strlen(ni.name) > 0) + reg_destroy_name(name); + fail_name: + if (strlen(prog.name) > 0) + reg_destroy_prog(exec[0]); + fail_prog: + return -1; } int bind_process(pid_t pid, const char * name) { - struct reg_proc * pc = NULL; - struct reg_name * rn; - struct timespec now; - struct timespec dl = {0, 10 * MILLION}; + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_MS(10); + struct name_info ni; if (name == NULL) return -EINVAL; - clock_gettime(PTHREAD_COND_CLOCK, &now); - - ts_add(&dl, &now, &dl); - - pthread_rwlock_wrlock(&irmd.reg_lock); - - while (!kill(pid, 0)) { - pc = registry_get_proc(pid); - if (pc != NULL || ts_diff_ms(&now, &dl) > 0) - break; - clock_gettime(PTHREAD_COND_CLOCK, &now); - sched_yield(); - } + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); - if (pc == NULL) { + if (reg_wait_proc(pid, &abstime) < 0) { log_err("Process %d does not %s.", pid, kill(pid, 0) ? "exist" : "respond"); - pthread_rwlock_unlock(&irmd.reg_lock); - return -1; + goto fail; } - if (reg_proc_add_name(pc, name)) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Failed to add name %s to process %d.", name, pid); - return -1; - } + memset(&ni, 0, sizeof(ni)); - rn = registry_get_name(name); - if (rn != NULL && reg_name_add_pid(rn, pid) < 0) - log_err("Failed adding process %d for name %s.", pid, name); + if (!reg_has_name(name)) { + ni.pol_lb = LB_SPILL; + strcpy(ni.name, name); + if (reg_create_name(&ni) < 0) { + log_err("Failed to create name %s.", name); + goto fail; + } + } - pthread_rwlock_unlock(&irmd.reg_lock); + if (reg_bind_proc(name, pid) < 0) { + log_err("Failed to add name %s to process %d.", name, pid); + goto fail_bind; + } log_info("Bound process %d to name %s.", pid, name); return 0; + + fail_bind: + if (strlen(ni.name) > 0) + reg_destroy_name(name); + fail: + return -1; + } static int unbind_program(const char * prog, @@ -900,33 +526,19 @@ static int unbind_program(const char * prog, if (prog == NULL) return -EINVAL; - pthread_rwlock_wrlock(&irmd.reg_lock); - - if (name == NULL) - registry_del_prog(prog); - else { - struct reg_name * rn; - struct reg_prog * pg; - - pg = registry_get_prog(prog); - if (pg == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - return -EINVAL; + if (name == NULL) { + if (reg_destroy_prog(prog) < 0) { + log_err("Failed to unbind %s.", prog); + return -1; } - - reg_prog_del_name(pg, name); - - rn = registry_get_name(name); - if (rn != NULL) - reg_name_del_prog(rn, prog); - } - - pthread_rwlock_unlock(&irmd.reg_lock); - - if (name == NULL) log_info("Program %s unbound.", prog); - else - log_info("All names matching %s unbound for %s.", name, prog); + } else { + if (reg_unbind_prog(name, prog) < 0) { + log_err("Failed to unbind %s from %s", prog, name); + return -1; + } + log_info("Name %s unbound for %s.", name, prog); + } return 0; } @@ -934,473 +546,249 @@ static int unbind_program(const char * prog, static int unbind_process(pid_t pid, const char * name) { - pthread_rwlock_wrlock(&irmd.reg_lock); - - if (name == NULL) - registry_del_proc(pid); - else { - struct reg_name * n; - struct reg_proc * p; - - p = registry_get_proc(pid); - if (p != NULL) - reg_proc_del_name(p, name); - - n = registry_get_name(name); - if (n != NULL) - reg_name_del_pid(n, pid); - } - - pthread_rwlock_unlock(&irmd.reg_lock); - - if (name == NULL) + if (name == NULL) { + if (reg_destroy_proc(pid) < 0) { + log_err("Failed to unbind %d.", pid); + return -1; + } log_info("Process %d unbound.", pid); - else - log_info("All names matching %s unbound for %d.", name, pid); - - return 0; -} - -static int get_ipcp_info(ipcp_list_msg_t ** msg, - struct reg_ipcp * ipcp) -{ - *msg = malloc(sizeof(**msg)); - if (*msg == NULL) - goto fail; - - ipcp_list_msg__init(*msg); - - (*msg)->name = strdup(ipcp->info.name); - if ((*msg)->name == NULL) - goto fail_name; - - (*msg)->layer = strdup( - ipcp->layer != NULL ? ipcp->layer : "Not enrolled"); - if ((*msg)->layer == NULL) - goto fail_layer; - - (*msg)->pid = ipcp->pid; - (*msg)->type = ipcp->info.type; + } else { + if (reg_unbind_proc(name, pid) < 0) { + log_err("Failed to unbind %d from %s", pid, name); + return -1; + } + log_info("Name %s unbound for process %d.", name, pid); + } return 0; - - fail_layer: - free((*msg)->name); - fail_name: - free(*msg); - *msg = NULL; - fail: - return -1; } -static ssize_t list_ipcps(ipcp_list_msg_t *** ipcps, - size_t * n_ipcps) +static int list_ipcps(ipcp_list_msg_t *** ipcps, + size_t * n_ipcps) { - struct list_head * p; - int i = 0; + int n; - pthread_rwlock_rdlock(&irmd.reg_lock); - - *n_ipcps = irmd.n_ipcps; - if (*n_ipcps == 0) { - pthread_rwlock_unlock(&irmd.reg_lock); - return 0; - } - - *ipcps = malloc(irmd.n_ipcps * sizeof(**ipcps)); - if (*ipcps == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - *n_ipcps = 0; - return -ENOMEM; - } - - list_for_each(p, &irmd.ipcps) { - struct reg_ipcp * e = list_entry(p, struct reg_ipcp, next); - if (get_ipcp_info(&((*ipcps)[i]), e) < 0) - goto fail; - ++i; - } + n = reg_list_ipcps(ipcps); + if (n < 0) + goto fail; - pthread_rwlock_unlock(&irmd.reg_lock); + *n_ipcps = (size_t) n; return 0; - fail: - pthread_rwlock_unlock(&irmd.reg_lock); - while (i > 0) - ipcp_list_msg__free_unpacked((*ipcps)[--i], NULL); - - free(*ipcps); + *ipcps = NULL; *n_ipcps = 0; - return -ENOMEM; + return -1; } -int name_create(const char * name, - enum pol_balance lb) +int name_create(const struct name_info * info) { - struct reg_name * n; - struct list_head * p; - - assert(name); + int ret; - pthread_rwlock_wrlock(&irmd.reg_lock); + assert(info != NULL); - if (registry_has_name(name)) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_warn("Name %s already exists.", name); + ret = reg_create_name(info); + if (ret == -EEXIST) { + log_info("Name %s already exists.", info->name); return 0; } - n = reg_name_create(name, lb); - if (n == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_dbg("Could not create name."); - return -ENOMEM; - } - - if (registry_add_name(n) < 0) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Failed to add name %s.", name); - return -ENOMEM; - } - - /* check the tables for existing bindings */ - list_for_each(p, &irmd.procs) { - struct list_head * q; - struct reg_proc * e; - e = list_entry(p, struct reg_proc, next); - list_for_each(q, &e->names) { - struct str_el * s; - s = list_entry(q, struct str_el, next); - if (!strcmp(s->str, name)) - reg_name_add_pid(n, e->pid); - } - } - - list_for_each(p, &irmd.progs) { - struct list_head * q; - struct reg_prog * e; - e = list_entry(p, struct reg_prog, next); - list_for_each(q, &e->names) { - struct str_el * s; - s = list_entry(q, struct str_el, next); - if (!strcmp(s->str, name)) - reg_name_add_prog(n, e); - } + if (ret < 0) { + log_err("Failed to create name %s.", info->name); + return -1; } - pthread_rwlock_unlock(&irmd.reg_lock); - - log_info("Created new name: %s.", name); + log_info("Created new name: %s.", info->name); return 0; } static int name_destroy(const char * name) { - assert(name); - pthread_rwlock_wrlock(&irmd.reg_lock); + assert(name != NULL); - if (!registry_has_name(name)) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_warn("Registry entry for %s does not exist.", name); - return -ENAME; + if (reg_destroy_name(name) < 0) { + log_err("Failed to destroy name %s.", name); + return -1; } - registry_del_name(name); - - pthread_rwlock_unlock(&irmd.reg_lock); - log_info("Destroyed name: %s.", name); return 0; } -static int get_name_info(name_info_msg_t ** msg, - struct reg_name * n) -{ - *msg = malloc(sizeof(**msg)); - if (*msg == NULL) - goto fail; - - name_info_msg__init(*msg); - - (*msg)->name = strdup(n->name); - if ((*msg)->name == NULL) - goto fail_name; - - (*msg)->pol_lb = n->pol_lb; - - return 0; - - fail_name: - free(*msg); - *msg = NULL; - fail: - return -1; -} - -static ssize_t list_names(name_info_msg_t *** names, - size_t * n_names) +static int list_names(name_info_msg_t *** names, + size_t * n_names) { - struct list_head * p; - int i = 0; + int n; - pthread_rwlock_rdlock(&irmd.reg_lock); - - *n_names = irmd.n_names; - if (*n_names == 0) { - pthread_rwlock_unlock(&irmd.reg_lock); - return 0; - } - - *names = malloc(irmd.n_names * sizeof(**names)); - if (*names == NULL) { - *n_names = 0; - pthread_rwlock_unlock(&irmd.reg_lock); - return -ENOMEM; - } - - list_for_each(p, &irmd.names) { - struct reg_name * n = list_entry(p, struct reg_name, next); - if (get_name_info(&((*names)[i]), n) < 0) - goto fail; - ++i; - } + n = reg_list_names(names); + if (n < 0) + goto fail; - pthread_rwlock_unlock(&irmd.reg_lock); + *n_names = (size_t) n; return 0; - fail: - pthread_rwlock_unlock(&irmd.reg_lock); - while (i > 0) - name_info_msg__free_unpacked((*names)[--i], NULL); - - free(*names); + *names = NULL; *n_names = 0; - return -ENOMEM; + return -1; } int name_reg(const char * name, pid_t pid) { - size_t len; - struct reg_ipcp * ipcp; - uint8_t * hash; - int err; + struct ipcp_info info; + struct layer_info layer; + buffer_t hash; assert(name); - pthread_rwlock_wrlock(&irmd.reg_lock); + info.pid = pid; - if (!registry_has_name(name)) { - err = -ENAME; - goto fail; - } - - ipcp = registry_get_ipcp_by_pid(pid); - if (ipcp == NULL) { - err = -EIPCP; - goto fail; + if (!reg_has_name(name)) { + log_err("Failed to get name %s.", name); + return -ENAME; } - if (ipcp->layer == NULL) { - err = -EPERM; - goto fail; + if (reg_get_ipcp(&info, &layer) < 0) { + log_err("Failed to get IPCP %d.", pid); + return -EIPCP; } - len = IPCP_HASH_LEN(ipcp); - - hash = malloc(len); - if (hash == NULL) { - err = -ENOMEM; - goto fail; + hash.len = hash_len((enum hash_algo) layer.dir_hash_algo); + hash.data = malloc(hash.len); + if (hash.data == NULL) { + log_err("Failed to malloc hash."); + return -ENOMEM; } - str_hash(ipcp->dir_hash_algo, hash, name); - pthread_rwlock_unlock(&irmd.reg_lock); + str_hash((enum hash_algo) layer.dir_hash_algo, hash.data, name); - if (ipcp_reg(pid, hash, len)) { + if (ipcp_reg(pid, hash)) { log_err("Could not register " HASH_FMT32 " with IPCP %d.", - HASH_VAL32(hash), pid); - free(hash); + HASH_VAL32(hash.data), pid); + freebuf(hash); return -1; } log_info("Registered %s with IPCP %d as " HASH_FMT32 ".", - name, pid, HASH_VAL32(hash)); + name, pid, HASH_VAL32(hash.data)); - free(hash); + freebuf(hash); return 0; - -fail: - pthread_rwlock_unlock(&irmd.reg_lock); - return err; } static int name_unreg(const char * name, pid_t pid) { - struct reg_ipcp * ipcp; - int err; - uint8_t * hash; - size_t len; + struct ipcp_info info; + struct layer_info layer; + buffer_t hash; assert(name); - pthread_rwlock_wrlock(&irmd.reg_lock); + info.pid = pid; - ipcp = registry_get_ipcp_by_pid(pid); - if (ipcp == NULL) { - err = -EIPCP; - goto fail; + if (!reg_has_name(name)) { + log_err("Failed to get name %s.", name); + return -ENAME; } - if (ipcp->layer == NULL) { - err = -EPERM; - goto fail; + if (reg_get_ipcp(&info, &layer) < 0) { + log_err("Failed to get IPCP %d.", pid); + return -EIPCP; } - len = IPCP_HASH_LEN(ipcp); - - hash = malloc(len); - if (hash == NULL) { - err = -ENOMEM; - goto fail; + hash.len = hash_len((enum hash_algo) layer.dir_hash_algo); + hash.data = malloc(hash.len); + if (hash.data == NULL) { + log_err("Failed to malloc hash."); + return -ENOMEM; } - str_hash(ipcp->dir_hash_algo, hash, name); + str_hash((enum hash_algo) layer.dir_hash_algo, hash.data, name); - pthread_rwlock_unlock(&irmd.reg_lock); - - if (ipcp_unreg(pid, hash, len)) { + if (ipcp_unreg(pid, hash)) { log_err("Could not unregister %s with IPCP %d.", name, pid); - free(hash); + freebuf(hash); return -1; } log_info("Unregistered %s from %d.", name, pid); - free(hash); + freebuf(hash); return 0; - - fail: - pthread_rwlock_unlock(&irmd.reg_lock); - return err; } -static int proc_announce(pid_t pid, - const char * prog) +static int proc_announce(const struct proc_info * info) { - struct reg_proc * rpc; - struct reg_prog * rpg; + if (reg_create_proc(info) < 0) { + log_err("Failed to add process %d.", info->pid); + goto fail_proc; + } - assert(prog); + log_info("Process added: %d (%s).", info->pid, info->prog); - rpc = reg_proc_create(pid, prog); - if (rpc == NULL) - return -ENOMEM; + return 0; - pthread_rwlock_wrlock(&irmd.reg_lock); + fail_proc: + return -1; +} - registry_add_proc(rpc); +static int proc_exit(pid_t pid) +{ + if (reg_has_ipcp(pid) && reg_destroy_ipcp(pid) < 0) + log_warn("Failed to remove IPCP %d.", pid); - /* Copy listen names from program if it exists. */ - rpg = registry_get_prog(rpc->prog); - if (rpg != NULL) { - struct list_head * p; - list_for_each(p, &rpg->names) { - struct str_el * s = list_entry(p, struct str_el, next); - struct str_el * n = malloc(sizeof(*n)); - if (n == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - return -ENOMEM; - } + if (reg_destroy_proc(pid) < 0) + log_err("Failed to remove process %d.", pid); - n->str = strdup(s->str); - if (n->str == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - free(n); - return -ENOMEM; - } + log_info("Process removed: %d.", pid); - list_add(&n->next, &rpc->names); - log_dbg("Process %d inherits name %s from program %s.", - pid, n->str, rpc->prog); - } - } + return 0; +} - pthread_rwlock_unlock(&irmd.reg_lock); +static void __cleanup_pkp(void * pkp) +{ + if (pkp != NULL) + crypt_dh_pkp_destroy(pkp); +} - return 0; +static void __cleanup_flow(void * flow) +{ + reg_destroy_flow(((struct flow_info *) flow)->id); } -static int flow_accept(pid_t pid, - struct timespec * dl, - struct reg_flow * f_out, - buffer_t * data) +static int flow_accept(struct flow_info * flow, + buffer_t * data, + struct timespec * abstime) { - struct reg_flow * f; - struct reg_proc * rp; - struct reg_name * n; - struct list_head * p; - pid_t pid_n; - pid_t pid_n_1; - int flow_id; - int ret; - buffer_t tmp = {NULL, 0}; - void * pkp; /* my public key pair */ - ssize_t key_len; - uint8_t buf[MSGBUFSZ]; - uint8_t * s = NULL; - int err; + uint8_t buf[MSGBUFSZ]; + buffer_t lpk; /* local public key */ + buffer_t rpk; /* remote public key */ + void * pkp; /* my public/private key pair */ + ssize_t key_len; + uint8_t * s; + int err; /* piggyback of user data not yet implemented */ assert(data != NULL && data->len == 0 && data->data == NULL); - pthread_rwlock_wrlock(&irmd.reg_lock); - - rp = registry_get_proc(pid); - if (rp == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Unknown process %d calling accept.", pid); - return -EINVAL; - } - - log_dbg("New instance (%d) of %s added.", pid, rp->prog); - log_dbg("This process accepts flows for:"); - - list_for_each(p, &rp->names) { - struct str_el * s = list_entry(p, struct str_el, next); - log_dbg(" %s", s->str); - n = registry_get_name(s->str); - if (n != NULL) - reg_name_add_pid(n, pid); - } - - pthread_rwlock_unlock(&irmd.reg_lock); - - ret = reg_proc_sleep(rp, dl); - if (ret == -ETIMEDOUT) - return -ETIMEDOUT; - - if (ret == -1) - return -EPIPE; - - if (irmd_get_state() != IRMD_RUNNING) { - log_dbg("Terminating accept: IRMd shutting down."); - return -EIRMD; + if (!reg_has_proc(flow->n_pid)) { + log_err("Unknown process %d calling accept.", flow->n_pid); + err = -EINVAL; + goto fail; } s = malloc(SYMMKEYSZ); if (s == NULL) { - log_err("Failed to malloc symmetric key."); + log_err("Failed to malloc symmkey."); err = -ENOMEM; - goto fail_malloc_s; + goto fail; } key_len = crypt_dh_pkp_create(&pkp, buf); @@ -1410,242 +798,245 @@ static int flow_accept(pid_t pid, goto fail_pkp; } - log_dbg("Generated ephemeral keys for %d.", pid); - - pthread_rwlock_wrlock(&irmd.reg_lock); - - f = registry_get_pending_flow_for_pid(pid); - if (f == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_warn("Port_id was not created yet."); - err = -EPERM; - goto fail_rp; - } - - pid_n = f->n_pid; - pid_n_1 = f->n_1_pid; - flow_id = f->flow_id; - - rp = registry_get_proc(pid); - if (rp == NULL) { - list_del(&f->next); - bmp_release(irmd.flow_ids, f->flow_id); - pthread_rwlock_unlock(&irmd.reg_lock); - ipcp_flow_alloc_resp(pid_n_1, flow_id, pid_n, -1, tmp); - clear_reg_flow(f); - reg_flow_set_state(f, FLOW_NULL); - reg_flow_destroy(f); - log_dbg("Process gone while accepting flow."); - err = -EPERM; - goto fail_rp; - } - - pthread_mutex_lock(&rp->lock); - - n = rp->name; - - pthread_mutex_unlock(&rp->lock); - - if (reg_name_get_state(n) != NAME_FLOW_ARRIVED) { - list_del(&f->next); - bmp_release(irmd.flow_ids, f->flow_id); - pthread_rwlock_unlock(&irmd.reg_lock); - ipcp_flow_alloc_resp(pid_n_1, flow_id, pid_n, -1, tmp); - clear_reg_flow(f); - reg_flow_set_state(f, FLOW_NULL); - reg_flow_destroy(f); - log_err("Entry in wrong state."); - err = -EPERM; - goto fail_rp; - } - - registry_names_del_proc(pid); - - f_out->flow_id = f->flow_id; - f_out->n_pid = f->n_pid; - f_out->n_1_pid = f->n_1_pid; - f_out->qs = f->qs; - f_out->mpl = f->mpl; - - if (f->qs.cypher_s != 0) { /* crypto requested */ - tmp.len = key_len; /* send this pubkey */ - tmp.data = (uint8_t *) buf; - - if (crypt_dh_derive(pkp, f->data.data, f->data.len, s) < 0) { - list_del(&f->next); - bmp_release(irmd.flow_ids, f->flow_id); - freebuf(f->data); - pthread_rwlock_unlock(&irmd.reg_lock); - clear_reg_flow(f); - reg_flow_set_state(f, FLOW_NULL); - reg_flow_destroy(f); - log_err("Failed to derive common secret for %d.", - flow_id); - err = -ECRYPT; - goto fail_rp; - } + lpk.data = buf; + lpk.len = (size_t) key_len; + + log_dbg("Generated ephemeral keys for %d.", flow->n_pid); + + if (reg_create_flow(flow) < 0) { + log_err("Failed to create flow."); + err = -EBADF; + goto fail_flow; + } + + if (reg_prepare_flow_accept(flow, &lpk) < 0) { + log_err("Failed to prepare accept."); + err = -EBADF; + goto fail_wait; } - freebuf(f->data); + pthread_cleanup_push(__cleanup_flow, flow); + pthread_cleanup_push(__cleanup_pkp, pkp); + pthread_cleanup_push(free, s); - pthread_rwlock_unlock(&irmd.reg_lock); + err = reg_wait_flow_accepted(flow, &rpk, abstime); - if (ipcp_flow_alloc_resp(pid_n_1, flow_id, pid_n, 0, tmp)) { - pthread_rwlock_wrlock(&irmd.reg_lock); - list_del(&f->next); - pthread_rwlock_unlock(&irmd.reg_lock); - log_dbg("Failed to respond to alloc. Port_id invalidated."); - clear_reg_flow(f); - reg_flow_set_state(f, FLOW_NULL); - reg_flow_destroy(f); - err = -EPERM; - goto fail_rp; + pthread_cleanup_pop(false); + pthread_cleanup_pop(false); + pthread_cleanup_pop(false); + + if (err == -ETIMEDOUT) { + log_err("Flow accept timed out."); + goto fail_wait; } - reg_flow_set_state(f, FLOW_ALLOCATED); + if (err == -1) { + log_dbg("Flow accept terminated."); + err = -EPIPE; + goto fail_wait; + } - crypt_dh_pkp_destroy(pkp); + assert(err == 0); - if (f_out->qs.cypher_s > 0) { + if (flow->qs.cypher_s != 0) { /* crypto requested */ + if (crypt_dh_derive(pkp, rpk, s) < 0) { + log_err("Failed to derive secret for %d.", flow->id); + err = -ECRYPT; + goto fail_derive; + } + freebuf(rpk); data->data = s; data->len = SYMMKEYSZ; + s= NULL; } else { - free(s); + clrbuf(lpk); + } + + if (ipcp_flow_alloc_resp(flow, 0, lpk) < 0) { + log_err("Failed to respond to flow allocation."); + err = -EIPCP; + goto fail_alloc_resp; } - log_info("Flow on flow_id %d allocated.", f->flow_id); + crypt_dh_pkp_destroy(pkp); + free(s); return 0; - fail_rp: + fail_derive: + freebuf(rpk); + clrbuf(lpk); + ipcp_flow_alloc_resp(flow, err, lpk); + fail_alloc_resp: + flow->state = FLOW_NULL; + fail_wait: + reg_destroy_flow(flow->id); + fail_flow: crypt_dh_pkp_destroy(pkp); fail_pkp: free(s); - fail_malloc_s: + fail: return err; } -static int flow_join(pid_t pid, +static int flow_join(struct flow_info * flow, const char * dst, - qosspec_t qs, - struct timespec * dl, - struct reg_flow * f_out) + struct timespec * abstime) { - struct reg_flow * f; - struct reg_ipcp * ipcp; - int flow_id; - int state; - uint8_t * hash; + struct ipcp_info ipcp; + struct layer_info layer; + buffer_t hash; + buffer_t pbuf = {NULL, 0}; /* nothing to piggyback */ + int err; - log_info("Allocating flow for %d to %s.", pid, dst); + log_info("Allocating flow for %d to %s.", flow->n_pid, dst); - ipcp = registry_get_ipcp_by_layer(dst); - if (ipcp == NULL) { - log_info("Layer %s unreachable.", dst); - return -1; + if (reg_create_flow(flow) < 0) { + log_err("Failed to create flow."); + err = -EBADF; + goto fail_flow; } - pthread_rwlock_wrlock(&irmd.reg_lock); - - flow_id = bmp_allocate(irmd.flow_ids); - if (!bmp_is_id_valid(irmd.flow_ids, flow_id)) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Could not allocate flow_id."); - return -EBADF; + strcpy(layer.name, dst); + if (reg_get_ipcp_by_layer(&ipcp, &layer) < 0) { + log_err("Failed to get IPCP for layer %s.", dst); + err = -EIPCP; + goto fail_ipcp; } - f = reg_flow_create(pid, ipcp->pid, flow_id, qs); - if (f == NULL) { - bmp_release(irmd.flow_ids, flow_id); - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Could not allocate flow_id."); - return -ENOMEM; + hash.len = hash_len((enum hash_algo) layer.dir_hash_algo); + hash.data = malloc(hash.len); + if (hash.data == NULL) { + log_err("Failed to malloc hash buffer."); + err = -ENOMEM; + goto fail_ipcp; } - list_add(&f->next, &irmd.flows); + reg_prepare_flow_alloc(flow); - pthread_rwlock_unlock(&irmd.reg_lock); + if (ipcp_flow_join(flow, hash)) { + log_err("Flow join with layer %s failed.", dst); + err = -ENOTALLOC; + goto fail_alloc; + } - assert(reg_flow_get_state(f) == FLOW_ALLOC_PENDING); + pthread_cleanup_push(__cleanup_flow, flow); + pthread_cleanup_push(free, hash.data); - hash = malloc(IPCP_HASH_LEN(ipcp)); - if (hash == NULL) - /* sanitizer cleans this */ - return -ENOMEM; + err = reg_wait_flow_allocated(flow, &pbuf, abstime); - str_hash(ipcp->dir_hash_algo, hash, dst); + pthread_cleanup_pop(false); + pthread_cleanup_pop(false); - if (ipcp_flow_join(ipcp->pid, flow_id, pid, hash, - IPCP_HASH_LEN(ipcp), qs)) { - reg_flow_set_state(f, FLOW_NULL); - /* sanitizer cleans this */ - log_info("Flow_join failed."); - free(hash); - return -EAGAIN; + if (err == -ETIMEDOUT) { + log_err("Flow join timed out."); + goto fail_alloc; } - free(hash); + if (err == -1) { + log_dbg("Flow join terminated."); + err = -EPIPE; + goto fail_alloc; + } - state = reg_flow_wait_state(f, FLOW_ALLOCATED, dl); - if (state != FLOW_ALLOCATED) { - if (state == -ETIMEDOUT) { - log_dbg("Flow allocation timed out"); - return -ETIMEDOUT; - } + assert(err == 0); - log_info("Pending flow to %s torn down.", dst); - return -EPIPE; - } + freebuf(hash); - pthread_rwlock_wrlock(&irmd.reg_lock); + return 0; - assert(reg_flow_get_state(f) == FLOW_ALLOCATED); + fail_alloc: + freebuf(hash); + fail_ipcp: + reg_destroy_flow(flow->id); + fail_flow: + return err; +} - f_out->flow_id = f->flow_id; - f_out->n_pid = f->n_pid; - f_out->n_1_pid = f->n_1_pid; - f_out->mpl = f->mpl; +static int get_ipcp_by_dst(const char * dst, + pid_t * pid, + buffer_t * hash) +{ + ipcp_list_msg_t ** ipcps; + int n; + int i; + int err = -EIPCP; - assert(f->data.data == NULL); - assert(f->data.len == 0); + n = reg_list_ipcps(&ipcps); - pthread_rwlock_unlock(&irmd.reg_lock); + /* Clean up the ipcp_msgs in this loop */ + for (i = 0; i < n; ++i) { + enum hash_algo algo; + enum ipcp_type type; + pid_t tmp; + bool enrolled; - log_info("Flow on flow_id %d allocated.", flow_id); + type = ipcps[i]->type; + algo = ipcps[i]->hash_algo; + hash->len = hash_len(algo); - return 0; + tmp = ipcps[i]->pid; + + enrolled = strcmp(ipcps[i]->layer, "Not enrolled.") != 0; + + ipcp_list_msg__free_unpacked(ipcps[i], NULL); + + if (type == IPCP_BROADCAST) + continue; + + if (err == 0 /* solution found */ || !enrolled) + continue; + + hash->data = malloc(hash->len); + if (hash->data == NULL) { + log_warn("Failed to malloc hash for query."); + err = -ENOMEM; + continue; + } + + str_hash(algo, hash->data, dst); + + if (ipcp_query(tmp, *hash) < 0) { + freebuf(*hash); + continue; + } + + *pid = tmp; + + err = 0; + } + + free(ipcps); + + return err; } -static int flow_alloc(pid_t pid, +static int flow_alloc(struct flow_info * flow, const char * dst, - qosspec_t qs, - struct timespec * dl, - struct reg_flow * f_out, - buffer_t * data) + buffer_t * data, + struct timespec * abstime) { - struct reg_flow * f; - struct reg_ipcp * ipcp; - int flow_id; - int state; - uint8_t * hash; - ssize_t key_len; - void * pkp; /* my public key pair */ - buffer_t tmp = {NULL, 0}; /* buffer for public key */ - uint8_t buf[MSGBUFSZ]; - uint8_t * s = NULL; - int err; - - log_info("Allocating flow for %d to %s.", pid, dst); + uint8_t buf[MSGBUFSZ]; + buffer_t lpk ={NULL, 0}; /* local public key */ + buffer_t rpk; /* remote public key */ + void * pkp = NULL; /* my public/private key pair */ + uint8_t * s = NULL; + buffer_t hash; + int err; /* piggyback of user data not yet implemented */ assert(data != NULL && data->len == 0 && data->data == NULL); - if (qs.cypher_s > 0) { + log_info("Allocating flow for %d to %s.", flow->n_pid, dst); + + if (flow->qs.cypher_s > 0) { + ssize_t key_len; + s = malloc(SYMMKEYSZ); if (s == NULL) { - log_err("Failed to malloc symmetric key."); + log_err("Failed to malloc symmetric key"); err = -ENOMEM; - goto fail_malloc_s; + goto fail_malloc; } key_len = crypt_dh_pkp_create(&pkp, buf); @@ -1655,502 +1046,230 @@ static int flow_alloc(pid_t pid, goto fail_pkp; } - log_dbg("Generated ephemeral keys for %d.", pid); - - tmp.data = (uint8_t *) buf; - tmp.len = (size_t) key_len; - } + lpk.data = buf; + lpk.len = (size_t) key_len; - ipcp = registry_get_ipcp_by_dst_name(dst, pid); - if (ipcp == NULL) { - log_info("Destination %s unreachable.", dst); - err = -ENOTALLOC; - goto fail_ipcp; + log_dbg("Generated ephemeral keys for %d.", flow->n_pid); } - pthread_rwlock_wrlock(&irmd.reg_lock); - - flow_id = bmp_allocate(irmd.flow_ids); - if (!bmp_is_id_valid(irmd.flow_ids, flow_id)) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Could not allocate flow_id."); + if (reg_create_flow(flow) < 0) { + log_err("Failed to create flow."); err = -EBADF; - goto fail_ipcp; - } - - f = reg_flow_create(pid, ipcp->pid, flow_id, qs); - if (f == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Could not allocate flow_id."); - err = -ENOMEM; goto fail_flow; } - list_add(&f->next, &irmd.flows); - - pthread_rwlock_unlock(&irmd.reg_lock); - - assert(reg_flow_get_state(f) == FLOW_ALLOC_PENDING); - - hash = malloc(IPCP_HASH_LEN(ipcp)); - if (hash == NULL) { - /* sanitizer cleans regflow */ - err = -ENOMEM; - goto fail_flow; + if (get_ipcp_by_dst(dst, &flow->n_1_pid, &hash) < 0) { + log_err("Failed to find IPCP for %s.", dst); + err = -EIPCP; + goto fail_ipcp; } - str_hash(ipcp->dir_hash_algo, hash, dst); + reg_prepare_flow_alloc(flow); - if (ipcp_flow_alloc(ipcp->pid, flow_id, pid, hash, - IPCP_HASH_LEN(ipcp), qs, tmp)) { - reg_flow_set_state(f, FLOW_NULL); - /* sanitizer cleans this */ - log_warn("Flow_allocation %d failed.", flow_id); + if (ipcp_flow_alloc(flow, hash, lpk)) { + log_err("Flow allocation %d failed.", flow->id); err = -ENOTALLOC; goto fail_alloc; } - state = reg_flow_wait_state(f, FLOW_ALLOCATED, dl); - if (state != FLOW_ALLOCATED) { - if (state == -ETIMEDOUT) { - log_err("Flow allocation timed out"); - err = -ETIMEDOUT; - goto fail_alloc; - } + pthread_cleanup_push(__cleanup_flow, flow); + pthread_cleanup_push(__cleanup_pkp, pkp); + pthread_cleanup_push(free, hash.data); + pthread_cleanup_push(free, s); - log_warn("Pending flow to %s torn down.", dst); - err = -EPIPE; - goto fail_alloc; - } - - pthread_rwlock_wrlock(&irmd.reg_lock); + err = reg_wait_flow_allocated(flow, &rpk, abstime); - assert(reg_flow_get_state(f) == FLOW_ALLOCATED); + pthread_cleanup_pop(false); + pthread_cleanup_pop(false); + pthread_cleanup_pop(false); + pthread_cleanup_pop(false); - f_out->flow_id = f->flow_id; - f_out->n_pid = f->n_pid; - f_out->n_1_pid = f->n_1_pid; - f_out->mpl = f->mpl; - if (qs.cypher_s > 0 && - crypt_dh_derive(pkp, f->data.data, f->data.len, s) < 0) { - freebuf(f->data); - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Failed to derive common secret for %d.", flow_id); - err = -ECRYPT; + if (err == -ETIMEDOUT) { + log_err("Flow allocation timed out."); goto fail_alloc; } - freebuf(f->data); - - pthread_rwlock_unlock(&irmd.reg_lock); + if (err == -1) { + log_dbg("Flow allocation terminated."); + err = -EPIPE; + goto fail_alloc; + } - free(hash); + assert(err == 0); - if (qs.cypher_s > 0) { + if (flow->qs.cypher_s != 0) { /* crypto requested */ + if (crypt_dh_derive(pkp, rpk, s) < 0) { + log_err("Failed to derive secret for %d.", flow->id); + err = -ECRYPT; + goto fail_derive; + } crypt_dh_pkp_destroy(pkp); + freebuf(rpk); data->data = s; data->len = SYMMKEYSZ; + s = NULL; } - log_info("Flow on flow_id %d allocated.", flow_id); + freebuf(hash); + free(s); return 0; + fail_derive: + freebuf(rpk); + flow->state = FLOW_DEALLOCATED; fail_alloc: - free(hash); - fail_flow: - /* Sanitize cleans bmp_release(irmd.flow_ids, flow_id); */ + freebuf(hash); fail_ipcp: - if (qs.cypher_s > 0) - crypt_dh_pkp_destroy(pkp); + reg_destroy_flow(flow->id); + fail_flow: + if (flow->qs.cypher_s > 0) + crypt_dh_pkp_destroy(pkp); fail_pkp: free(s); - fail_malloc_s: + fail_malloc: return err; - } -static int flow_dealloc(pid_t pid, - int flow_id, - time_t timeo) +static int wait_for_accept(enum hash_algo algo, + const uint8_t * hash) { - pid_t n_1_pid = -1; - int ret = 0; + struct timespec timeo = TIMESPEC_INIT_MS(IRMD_REQ_ARR_TIMEOUT); + struct timespec abstime; + char ** exec; + int ret; - struct reg_flow * f = NULL; + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); - log_dbg("Deallocating flow %d for process %d.", - flow_id, pid); - - pthread_rwlock_wrlock(&irmd.reg_lock); - - f = registry_get_flow(flow_id); - if (f == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_dbg("Deallocate unknown port %d by %d.", flow_id, pid); - return 0; - } - - if (pid == f->n_pid) { - f->n_pid = -1; - n_1_pid = f->n_1_pid; - } else if (pid == f->n_1_pid) { - f->n_1_pid = -1; - } else { - pthread_rwlock_unlock(&irmd.reg_lock); - log_dbg("Dealloc called by wrong process."); - return -EPERM; - } + ret = reg_wait_flow_accepting(algo, hash, &abstime); + if (ret == -ETIMEDOUT) { + if (reg_get_exec(algo, hash, &exec) < 0) { + log_dbg("No program bound to " HASH_FMT32 ".", + HASH_VAL32(hash)); + goto fail; + } - if (reg_flow_get_state(f) == FLOW_DEALLOC_PENDING) { - list_del(&f->next); - if ((kill(f->n_pid, 0) < 0 && f->n_1_pid == -1) || - (kill(f->n_1_pid, 0) < 0 && f->n_pid == -1)) - reg_flow_set_state(f, FLOW_NULL); - clear_reg_flow(f); - reg_flow_destroy(f); - bmp_release(irmd.flow_ids, flow_id); - log_info("Completed deallocation of flow_id %d by process %d.", - flow_id, pid); - } else { - reg_flow_set_state(f, FLOW_DEALLOC_PENDING); - log_dbg("Partial deallocation of flow_id %d by process %d.", - flow_id, pid); - } + log_info("Autostarting %s.", exec[0]); - pthread_rwlock_unlock(&irmd.reg_lock); + if (spawn_program(exec) < 0) { + log_dbg("Failed to autostart " HASH_FMT32 ".", + HASH_VAL32(hash)); + goto fail_spawn; + } - if (n_1_pid != -1) - ret = ipcp_flow_dealloc(n_1_pid, flow_id, timeo); + ts_add(&abstime, &timeo, &abstime); - return ret; -} + ret = reg_wait_flow_accepting(algo, hash, &abstime); + if (ret == -ETIMEDOUT) + goto fail_spawn; -static pid_t auto_execute(char ** argv) -{ - pid_t pid; - struct stat s; - - if (stat(argv[0], &s) != 0) { - log_warn("Program %s does not exist.", argv[0]); - return -1; + argvfree(exec); } - if (!(s.st_mode & S_IXUSR)) { - log_warn("Program %s is not executable.", argv[0]); - return -1; - } - - if (posix_spawn(&pid, argv[0], NULL, NULL, argv, NULL)) { - log_err("Failed to spawn new process"); - return -1; - } - - log_info("Instantiated %s as process %d.", argv[0], pid); + return ret; - return pid; + fail_spawn: + argvfree(exec); + fail: + return -1; } -static int flow_req_arr(pid_t pid, - struct reg_flow * f_out, - const uint8_t * hash, - time_t mpl, - qosspec_t qs, - buffer_t data) +static int flow_req_arr(struct flow_info * flow, + const uint8_t * hash, + buffer_t * data) { - struct reg_name * n; - struct reg_prog * rpg; - struct reg_proc * rpc; - struct reg_flow * f; - struct reg_ipcp * ipcp; - - struct pid_el * c_pid; - pid_t h_pid; - int flow_id; - - struct timespec wt = {IRMD_REQ_ARR_TIMEOUT / 1000, - (IRMD_REQ_ARR_TIMEOUT % 1000) * MILLION}; - - log_dbg("Flow req arrived from IPCP %d for " HASH_FMT32 ".", - pid, HASH_VAL32(hash)); - - pthread_rwlock_rdlock(&irmd.reg_lock); - - ipcp = registry_get_ipcp_by_pid(pid); - if (ipcp == NULL) { - log_err("IPCP died."); - return -EIPCP; - } - - n = registry_get_name_by_hash(ipcp->dir_hash_algo, - hash, IPCP_HASH_LEN(ipcp)); - if (n == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Unknown hash: " HASH_FMT32 ".", HASH_VAL32(hash)); - return -1; - } - - log_info("Flow request arrived for %s.", n->name); - - pthread_rwlock_unlock(&irmd.reg_lock); - - /* Give the process a bit of slop time to call accept */ - if (reg_name_leave_state(n, NAME_IDLE, &wt) == -1) { - log_err("No processes for " HASH_FMT32 ".", HASH_VAL32(hash)); - return -1; - } - - pthread_rwlock_wrlock(&irmd.reg_lock); - - switch (reg_name_get_state(n)) { - case NAME_IDLE: - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("No processes for " HASH_FMT32 ".", HASH_VAL32(hash)); - return -1; - case NAME_AUTO_ACCEPT: - c_pid = malloc(sizeof(*c_pid)); - if (c_pid == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - return -1; - } - - reg_name_set_state(n, NAME_AUTO_EXEC); - rpg = registry_get_prog(reg_name_get_prog(n)); - if (rpg == NULL - || (c_pid->pid = auto_execute(rpg->argv)) < 0) { - reg_name_set_state(n, NAME_AUTO_ACCEPT); - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Could not start program for reg_entry %s.", - n->name); - free(c_pid); - return -1; - } + struct ipcp_info info; + struct layer_info layer; + enum hash_algo algo; + int ret; - list_add(&c_pid->next, &irmd.spawned_pids); + info.pid = flow->n_1_pid; - pthread_rwlock_unlock(&irmd.reg_lock); + log_info("Flow req arrived from IPCP %d for " HASH_FMT32 ".", + info.pid, HASH_VAL32(hash)); - if (reg_name_leave_state(n, NAME_AUTO_EXEC, NULL)) - return -1; - - pthread_rwlock_wrlock(&irmd.reg_lock); - /* FALLTHRU */ - case NAME_FLOW_ACCEPT: - h_pid = reg_name_get_pid(n); - if (h_pid == -1) { - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Invalid process id returned."); - return -1; - } - - break; - default: - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("IRMd in wrong state."); - return -1; + if (reg_get_ipcp(&info, &layer) < 0) { + log_err("No IPCP with pid %d.", info.pid); + ret = -EIPCP; + goto fail; } - flow_id = bmp_allocate(irmd.flow_ids); - if (!bmp_is_id_valid(irmd.flow_ids, flow_id)) { - log_err("Out of flow ids."); - pthread_rwlock_unlock(&irmd.reg_lock); - return -1; - } + algo = (enum hash_algo) layer.dir_hash_algo; - f = reg_flow_create(h_pid, pid, flow_id, qs); - if (f == NULL) { - bmp_release(irmd.flow_ids, flow_id); - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Could not allocate flow_id."); - return -1; + ret = wait_for_accept(algo, hash); + if (ret < 0) { + log_err("No activeprocess for " HASH_FMT32 ".", + HASH_VAL32(hash)); + goto fail; } - f->state = FLOW_ALLOC_REQ_PENDING; - f->mpl = mpl; - f->data = data; - - list_add(&f->next, &irmd.flows); - - reg_name_set_state(n, NAME_FLOW_ARRIVED); + flow->id = ret; + flow->state = FLOW_ALLOCATED; - rpc = registry_get_proc(h_pid); - if (rpc == NULL) { - clear_reg_flow(f); - bmp_release(irmd.flow_ids, f->flow_id); - list_del(&f->next); - pthread_rwlock_unlock(&irmd.reg_lock); - log_err("Could not get process table entry for %d.", h_pid); - freebuf(f->data); - reg_flow_destroy(f); - return -1; + ret = reg_respond_accept(flow, data); + if (ret < 0) { + log_err("Failed to respond to flow %d.", flow->id); + goto fail; } - reg_proc_wake(rpc, n); - - pthread_rwlock_unlock(&irmd.reg_lock); - - reg_name_leave_state(n, NAME_FLOW_ARRIVED, NULL); - - f_out->flow_id = flow_id; - f_out->n_pid = h_pid; - return 0; + fail: + return ret; } -static int flow_alloc_reply(int flow_id, - int response, - time_t mpl, - buffer_t data) +static int flow_alloc_reply(struct flow_info * flow, + int response, + buffer_t * data) { - struct reg_flow * f; - - pthread_rwlock_wrlock(&irmd.reg_lock); + flow->state = response ? FLOW_DEALLOCATED : FLOW_ALLOCATED; - f = registry_get_flow(flow_id); - if (f == NULL) { - pthread_rwlock_unlock(&irmd.reg_lock); - return -1; + if (reg_respond_alloc(flow, data) < 0) { + log_err("Failed to reply to flow %d.", flow->id); + flow->state = FLOW_DEALLOCATED; + return -EBADF; } - f->mpl = mpl; - - if (!response) - reg_flow_set_state(f, FLOW_ALLOCATED); - else - reg_flow_set_state(f, FLOW_NULL); - - f->data = data; - - pthread_rwlock_unlock(&irmd.reg_lock); - return 0; } -void * irm_sanitize(void * o) +static int flow_dealloc(struct flow_info * flow, + time_t timeo) { - struct timespec now; - struct list_head * p = NULL; - struct list_head * h = NULL; - - struct timespec timeout = {IRMD_CLEANUP_TIMER / BILLION, - IRMD_CLEANUP_TIMER % BILLION}; - int s; - - (void) o; - - while (true) { - if (clock_gettime(CLOCK_MONOTONIC, &now) < 0) - log_warn("Failed to get time."); - - if (irmd_get_state() != IRMD_RUNNING) - return (void *) 0; - - pthread_rwlock_wrlock(&irmd.reg_lock); - pthread_cleanup_push(__cleanup_rwlock_unlock, &irmd.reg_lock); - - list_for_each_safe(p, h, &irmd.spawned_pids) { - struct pid_el * e = list_entry(p, struct pid_el, next); - waitpid(e->pid, &s, WNOHANG); - if (kill(e->pid, 0) >= 0) - continue; - log_dbg("Child process %d died, error %d.", e->pid, s); - list_del(&e->next); - free(e); - } - - list_for_each_safe(p, h, &irmd.procs) { - struct reg_proc * e = - list_entry(p, struct reg_proc, next); - if (kill(e->pid, 0) >= 0) - continue; - log_dbg("Dead process removed: %d.", e->pid); - list_del(&e->next); - reg_proc_destroy(e); - } + log_info("Deallocating flow %d for process %d.", + flow->id, flow->n_pid); - list_for_each_safe(p, h, &irmd.ipcps) { - struct reg_ipcp * e = - list_entry(p, struct reg_ipcp, next); - if (kill(e->pid, 0) >= 0) - continue; - log_dbg("Dead IPCP removed: %d.", e->pid); - list_del(&e->next); - reg_ipcp_destroy(e); - } + reg_dealloc_flow(flow); - list_for_each_safe(p, h, &irmd.names) { - struct list_head * p2; - struct list_head * h2; - struct reg_name * e = - list_entry(p, struct reg_name, next); - list_for_each_safe(p2, h2, &e->reg_pids) { - struct pid_el * a = - list_entry(p2, struct pid_el, next); - if (kill(a->pid, 0) >= 0) - continue; - log_dbg("Dead process removed from: %d %s.", - a->pid, e->name); - reg_name_del_pid_el(e, a); - } - } + if (ipcp_flow_dealloc(flow->n_1_pid, flow->id, timeo) < 0) { + log_err("Failed to request dealloc from %d.", flow->n_1_pid); + return -EIPCP; + } - pthread_cleanup_pop(true); + return 0; +} - pthread_rwlock_wrlock(&irmd.reg_lock); - pthread_cleanup_push(__cleanup_rwlock_unlock, &irmd.reg_lock); - - list_for_each_safe(p, h, &irmd.flows) { - int ipcpi; - int flow_id; - struct reg_flow * f = - list_entry(p, struct reg_flow, next); - - if (reg_flow_get_state(f) == FLOW_ALLOC_PENDING - && ts_diff_ms(&f->t0, &now) > IRMD_FLOW_TIMEOUT) { - log_dbg("Pending flow_id %d timed out.", - f->flow_id); - reg_flow_set_state(f, FLOW_DEALLOC_PENDING); - continue; - } +static int flow_dealloc_resp(struct flow_info * flow) +{ + reg_dealloc_flow_resp(flow); - if (kill(f->n_pid, 0) < 0) { - log_dbg("Process %d gone, deallocating " - "flow %d.", - f->n_pid, f->flow_id); - f->n_pid = -1; - reg_flow_set_state(f, FLOW_DEALLOC_PENDING); - ipcpi = f->n_1_pid; - flow_id = f->flow_id; - ipcp_flow_dealloc(ipcpi, flow_id, DEALLOC_TIME); - continue; - } + assert(flow->state == FLOW_DEALLOCATED); - if (kill(f->n_1_pid, 0) < 0) { - struct shm_flow_set * set; - log_err("IPCP %d gone, flow %d removed.", - f->n_1_pid, f->flow_id); - set = shm_flow_set_open(f->n_pid); - if (set != NULL) - shm_flow_set_destroy(set); - f->n_1_pid = -1; - reg_flow_set_state(f, FLOW_DEALLOC_PENDING); - } - } + reg_destroy_flow(flow->id); - pthread_cleanup_pop(true); + log_info("Completed deallocation of flow_id %d by process %d.", + flow->id, flow->n_1_pid); - nanosleep(&timeout, NULL); - } + return 0; } -__attribute__((no_sanitize_address)) static void * acceptloop(void * o) { int csockfd; (void) o; - while (irmd_get_state() == IRMD_RUNNING) { + while (true) { struct cmd * cmd; csockfd = accept(irmd.sockfd, 0, 0); @@ -2201,16 +1320,17 @@ static void free_msg(void * o) static irm_msg_t * do_command_msg(irm_msg_t * msg) { struct ipcp_config conf; - struct ipcp_info info; + struct ipcp_info ipcp; + struct flow_info flow; + struct proc_info proc; + struct name_info name; + struct timespec * abstime = NULL; + struct timespec ts; + int res; irm_msg_t * ret_msg; buffer_t data; - struct reg_flow f; - struct qos_spec qs; - struct timespec * dl = NULL; - struct timespec ts = {0, 0}; - int res; - memset(&f, 0, sizeof(f)); + memset(&flow, 0, sizeof(flow)); ret_msg = malloc(sizeof(*ret_msg)); if (ret_msg == NULL) { @@ -2224,6 +1344,7 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg) if (msg->has_timeo_sec) { struct timespec now; + clock_gettime(PTHREAD_COND_CLOCK, &now); assert(msg->has_timeo_nsec); @@ -2232,18 +1353,19 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg) ts_add(&ts, &now, &ts); - dl = &ts; + abstime = &ts; } pthread_cleanup_push(free_msg, ret_msg); switch (msg->code) { case IRM_MSG_CODE__IRM_CREATE_IPCP: - info = ipcp_info_msg_to_s(msg->ipcp_info); - res = create_ipcp(&info); + ipcp = ipcp_info_msg_to_s(msg->ipcp_info); + res = create_ipcp(&ipcp); break; case IRM_MSG_CODE__IPCP_CREATE_R: - res = create_ipcp_r(msg->pid, msg->result); + ipcp = ipcp_info_msg_to_s(msg->ipcp_info); + res = create_ipcp_r(&ipcp); break; case IRM_MSG_CODE__IRM_DESTROY_IPCP: res = destroy_ipcp(msg->pid); @@ -2256,21 +1378,28 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg) res = enroll_ipcp(msg->pid, msg->dst); break; case IRM_MSG_CODE__IRM_CONNECT_IPCP: - qs = qos_spec_msg_to_s(msg->qosspec); - res = connect_ipcp(msg->pid, msg->dst, msg->comp, qs); + flow.qs = qos_spec_msg_to_s(msg->qosspec); + res = connect_ipcp(msg->pid, msg->dst, msg->comp, flow.qs); break; case IRM_MSG_CODE__IRM_DISCONNECT_IPCP: res = disconnect_ipcp(msg->pid, msg->dst, msg->comp); break; case IRM_MSG_CODE__IRM_BIND_PROGRAM: - res = bind_program(msg->prog, msg->name, msg->opts, - msg->n_args, msg->args); + /* Make exec NULL terminated instead of empty string terminated */ + free(msg->exec[msg->n_exec - 1]); + msg->exec[msg->n_exec - 1] = NULL; + res = bind_program(msg->exec, msg->name, msg->opts); break; case IRM_MSG_CODE__IRM_UNBIND_PROGRAM: res = unbind_program(msg->prog, msg->name); break; case IRM_MSG_CODE__IRM_PROC_ANNOUNCE: - res = proc_announce(msg->pid, msg->prog); + proc.pid = msg->pid; + strcpy(proc.prog, msg->prog); + res = proc_announce(&proc); + break; + case IRM_MSG_CODE__IRM_PROC_EXIT: + res = proc_exit(msg->pid); break; case IRM_MSG_CODE__IRM_BIND_PROCESS: res = bind_process(msg->pid, msg->name); @@ -2282,8 +1411,9 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg) res = list_ipcps(&ret_msg->ipcps, &ret_msg->n_ipcps); break; case IRM_MSG_CODE__IRM_CREATE_NAME: - res = name_create(msg->names[0]->name, - msg->names[0]->pol_lb); + strcpy(name.name, msg->names[0]->name); + name.pol_lb = msg->names[0]->pol_lb; + res = name_create(&name); break; case IRM_MSG_CODE__IRM_DESTROY_NAME: res = name_destroy(msg->name); @@ -2301,17 +1431,19 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg) data.len = msg->pk.len; data.data = msg->pk.data; assert(data.len > 0 ? data.data != NULL : data.data == NULL); - res = flow_accept(msg->pid, dl, &f, &data); + flow.n_pid = msg->pid; + flow.qs = qos_raw; + res = flow_accept(&flow, &data, abstime); if (res == 0) { qosspec_msg_t * qs_msg; - qs_msg = qos_spec_s_to_msg(&f.qs); + qs_msg = qos_spec_s_to_msg(&flow.qs); ret_msg->has_flow_id = true; - ret_msg->flow_id = f.flow_id; + ret_msg->flow_id = flow.id; ret_msg->has_pid = true; - ret_msg->pid = f.n_1_pid; - ret_msg->qosspec = qs_msg; + ret_msg->pid = flow.n_1_pid; ret_msg->has_mpl = true; - ret_msg->mpl = f.mpl; + ret_msg->qosspec = qs_msg; + ret_msg->mpl = flow.mpl; ret_msg->has_symmkey = data.len != 0; ret_msg->symmkey.data = data.data; ret_msg->symmkey.len = data.len; @@ -2321,16 +1453,17 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg) data.len = msg->pk.len; data.data = msg->pk.data; msg->has_pk = false; - qs = qos_spec_msg_to_s(msg->qosspec); + flow.n_pid = msg->pid; + flow.qs = qos_spec_msg_to_s(msg->qosspec); assert(data.len > 0 ? data.data != NULL : data.data == NULL); - res = flow_alloc(msg->pid, msg->dst, qs, dl, &f, &data); + res = flow_alloc(&flow, msg->dst, &data, abstime); if (res == 0) { ret_msg->has_flow_id = true; - ret_msg->flow_id = f.flow_id; + ret_msg->flow_id = flow.id; ret_msg->has_pid = true; - ret_msg->pid = f.n_1_pid; + ret_msg->pid = flow.n_1_pid; ret_msg->has_mpl = true; - ret_msg->mpl = f.mpl; + ret_msg->mpl = flow.mpl; ret_msg->has_symmkey = data.len != 0; ret_msg->symmkey.data = data.data; ret_msg->symmkey.len = data.len; @@ -2338,19 +1471,18 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg) break; case IRM_MSG_CODE__IRM_FLOW_JOIN: assert(msg->pk.len == 0 && msg->pk.data == NULL); - qs = qos_spec_msg_to_s(msg->qosspec); - res = flow_join(msg->pid, msg->dst, qs, dl, &f); - if (res == 0) { - ret_msg->has_flow_id = true; - ret_msg->flow_id = f.flow_id; - ret_msg->has_pid = true; - ret_msg->pid = f.n_1_pid; - ret_msg->has_mpl = true; - ret_msg->mpl = f.mpl; - } + flow.qs = qos_spec_msg_to_s(msg->qosspec); + res = flow_join(&flow, msg->dst, abstime); break; case IRM_MSG_CODE__IRM_FLOW_DEALLOC: - res = flow_dealloc(msg->pid, msg->flow_id, msg->timeo_sec); + flow.n_pid = msg->pid; + flow.id = msg->flow_id; + res = flow_dealloc(&flow, msg->timeo_sec); + break; + case IRM_MSG_CODE__IPCP_FLOW_DEALLOC: + flow.n_1_pid = msg->pid; + flow.id = msg->flow_id; + res = flow_dealloc_resp(&flow); break; case IRM_MSG_CODE__IPCP_FLOW_REQ_ARR: data.len = msg->pk.len; @@ -2359,14 +1491,15 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg) msg->pk.data = NULL; msg->pk.len = 0; assert(data.len > 0 ? data.data != NULL : data.data == NULL); - qs = qos_spec_msg_to_s(msg->qosspec); - res = flow_req_arr(msg->pid, &f, msg->hash.data, - msg->mpl, qs, data); + flow.n_1_pid = msg->pid; + flow.mpl = msg->mpl; + flow.qs = qos_spec_msg_to_s(msg->qosspec); + res = flow_req_arr(&flow, msg->hash.data, &data); if (res == 0) { ret_msg->has_flow_id = true; - ret_msg->flow_id = f.flow_id; + ret_msg->flow_id = flow.id; ret_msg->has_pid = true; - ret_msg->pid = f.n_pid; + ret_msg->pid = flow.n_pid; } break; case IRM_MSG_CODE__IPCP_FLOW_ALLOC_REPLY: @@ -2376,8 +1509,9 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg) msg->pk.data = NULL; msg->pk.len = 0; assert(data.len > 0 ? data.data != NULL : data.data == NULL); - res = flow_alloc_reply(msg->flow_id, msg->response, - msg->mpl, data); + flow.id = msg->flow_id; + flow.mpl = msg->mpl; + res = flow_alloc_reply(&flow, msg->response, &data); break; default: log_err("Don't know that message code."); @@ -2493,75 +1627,20 @@ static void * mainloop(void * o) static void irm_fini(void) { - struct list_head * p; - struct list_head * h; - +#ifdef HAVE_FUSE + struct timespec wait = TIMESPEC_INIT_MS(1); + int retries = 5; +#endif if (irmd_get_state() != IRMD_NULL) log_warn("Unsafe destroy."); tpm_destroy(irmd.tpm); - pthread_rwlock_wrlock(&irmd.reg_lock); - - /* Clear the lists. */ - list_for_each_safe(p, h, &irmd.ipcps) { - struct reg_ipcp * e = list_entry(p, struct reg_ipcp, next); - list_del(&e->next); - reg_ipcp_destroy(e); - } - - list_for_each(p, &irmd.spawned_pids) { - struct pid_el * e = list_entry(p, struct pid_el, next); - if (kill(e->pid, SIGTERM)) - log_dbg("Could not send kill signal to %d.", e->pid); - } - - list_for_each_safe(p, h, &irmd.spawned_pids) { - struct pid_el * e = list_entry(p, struct pid_el, next); - int status; - if (waitpid(e->pid, &status, 0) < 0) - log_dbg("Error waiting for %d to exit.", e->pid); - list_del(&e->next); - registry_names_del_proc(e->pid); - free(e); - } - - list_for_each_safe(p, h, &irmd.progs) { - struct reg_prog * e = list_entry(p, struct reg_prog, next); - list_del(&e->next); - reg_prog_destroy(e); - } - - list_for_each_safe(p, h, &irmd.procs) { - struct reg_proc * e = list_entry(p, struct reg_proc, next); - list_del(&e->next); - e->state = PROC_INIT; /* sanitizer already joined */ - reg_proc_destroy(e); - } - - registry_destroy_names(); - - pthread_rwlock_unlock(&irmd.reg_lock); - close(irmd.sockfd); if (unlink(IRM_SOCK_PATH)) log_dbg("Failed to unlink %s.", IRM_SOCK_PATH); - pthread_rwlock_wrlock(&irmd.reg_lock); - - if (irmd.flow_ids != NULL) - bmp_destroy(irmd.flow_ids); - - list_for_each_safe(p, h, &irmd.flows) { - struct reg_flow * f = list_entry(p, struct reg_flow, next); - list_del(&f->next); - reg_flow_destroy(f); - } - - pthread_rwlock_unlock(&irmd.reg_lock); - - if (irmd.rdrb != NULL) shm_rdrbuff_destroy(irmd.rdrb); @@ -2570,16 +1649,44 @@ static void irm_fini(void) pthread_mutex_destroy(&irmd.cmd_lock); pthread_cond_destroy(&irmd.cmd_cond); - pthread_rwlock_destroy(&irmd.reg_lock); pthread_rwlock_destroy(&irmd.state_lock); #ifdef HAVE_FUSE - sleep(1); - if (rmdir(FUSE_PREFIX)) - log_warn("Failed to remove " FUSE_PREFIX); + while (rmdir(FUSE_PREFIX) < 0 && retries-- > 0) + nanosleep(&wait, NULL); + if (retries < 0) + log_err("Failed to remove " FUSE_PREFIX); #endif } +#ifdef HAVE_FUSE +static void destroy_mount(char * mnt) +{ + struct stat st; + + log_dbg("Destroying mountpoint %s.", mnt); + + if (stat(mnt, &st) == -1){ + switch(errno) { + case ENOENT: + log_dbg("Fuse mountpoint %s not found: %s", + mnt, strerror(errno)); + break; + case ENOTCONN: + /* FALLTHRU */ + case ECONNABORTED: + log_dbg("Cleaning up fuse mountpoint %s.", + mnt); + rib_cleanup(mnt); + break; + default: + log_err("Unhandled fuse error on mnt %s: %s.", + mnt, strerror(errno)); + } + } +} +#endif + static int ouroboros_reset(void) { shm_rdrbuff_purge(); @@ -2588,6 +1695,48 @@ static int ouroboros_reset(void) return 0; } +static void cleanup_pid(pid_t pid) +{ +#ifdef HAVE_FUSE + char mnt[RIB_PATH_LEN + 1]; + + if (reg_has_ipcp(pid)) { + struct ipcp_info info; + info.pid = pid; + reg_get_ipcp(&info, NULL); + sprintf(mnt, FUSE_PREFIX "/%s", info.name); + } else { + sprintf(mnt, FUSE_PREFIX "/proc.%d", pid); + } + + destroy_mount(mnt); + +#else + (void) pid; +#endif +} + +void * irm_sanitize(void * o) +{ + pid_t pid; + struct timespec ts = TIMESPEC_INIT_MS(IRMD_FLOW_TIMEOUT / 20); + + (void) o; + + while (true) { + while((pid = reg_get_dead_proc()) != -1) { + log_info("Process %d died.", pid); + cleanup_pid(pid); + reg_destroy_proc(pid); + } + + nanosleep(&ts, NULL); + } + + return (void *) 0; +} + + static int irm_init(void) { struct stat st; @@ -2635,11 +1784,6 @@ static int irm_init(void) goto fail_state_lock; } - if (pthread_rwlock_init(&irmd.reg_lock, NULL)) { - log_err("Failed to initialize rwlock."); - goto fail_reg_lock; - } - if (pthread_mutex_init(&irmd.cmd_lock, NULL)) { log_err("Failed to initialize mutex."); goto fail_cmd_lock; @@ -2661,20 +1805,8 @@ static int irm_init(void) pthread_condattr_destroy(&cattr); - list_head_init(&irmd.ipcps); - list_head_init(&irmd.procs); - list_head_init(&irmd.progs); - list_head_init(&irmd.spawned_pids); - list_head_init(&irmd.names); - list_head_init(&irmd.flows); list_head_init(&irmd.cmds); - irmd.flow_ids = bmp_create(SYS_MAX_FLOWS, 0); - if (irmd.flow_ids == NULL) { - log_err("Failed to create flow_ids bitmap."); - goto fail_flow_ids; - } - if (stat(SOCK_PATH, &st) == -1) { if (mkdir(SOCK_PATH, 0777)) { log_err("Failed to create sockets directory."); @@ -2745,14 +1877,10 @@ static int irm_init(void) fail_sock_path: unlink(IRM_SOCK_PATH); fail_stat: - bmp_destroy(irmd.flow_ids); - fail_flow_ids: pthread_cond_destroy(&irmd.cmd_cond); fail_cmd_cond: pthread_mutex_destroy(&irmd.cmd_lock); fail_cmd_lock: - pthread_rwlock_destroy(&irmd.reg_lock); - fail_reg_lock: pthread_rwlock_destroy(&irmd.state_lock); fail_state_lock: lockfile_destroy(irmd.lf); @@ -2797,14 +1925,13 @@ static int irm_start(void) tpm_stop(irmd.tpm); fail_tpm_start: return -1; - } static void irm_sigwait(sigset_t sigset) { int sig; - while (irmd_get_state() != IRMD_NULL) { + while (irmd_get_state() != IRMD_SHUTDOWN) { if (sigwait(&sigset, &sig) != 0) { log_warn("Bad signal."); continue; @@ -2816,7 +1943,7 @@ static void irm_sigwait(sigset_t sigset) case SIGTERM: case SIGHUP: log_info("IRMd shutting down..."); - irmd_set_state(IRMD_NULL); + irmd_set_state(IRMD_SHUTDOWN); break; case SIGPIPE: log_dbg("Ignored SIGPIPE."); @@ -2830,11 +1957,14 @@ static void irm_sigwait(sigset_t sigset) static void irm_stop(void) { pthread_cancel(irmd.acceptor); + pthread_cancel(irmd.irm_sanitize); pthread_join(irmd.acceptor, NULL); pthread_join(irmd.irm_sanitize, NULL); tpm_stop(irmd.tpm); + + irmd_set_state(IRMD_NULL); } static void irm_argparse(int argc, @@ -2869,6 +1999,62 @@ static void irm_argparse(int argc, } } +static void * kill_dash_nine(void * o) +{ + time_t slept = 0; +#ifdef IRMD_KILL_ALL_PROCESSES + struct timespec ts = TIMESPEC_INIT_MS(IRMD_FLOW_TIMEOUT / 19); +#endif + (void) o; + + while (slept < IRMD_PKILL_TIMEOUT) { + time_t intv = 1; + if (reg_first_spawned() == -1) + goto finish; + sleep(intv); + slept += intv; + } + + log_dbg("I am become Death, destroyer of hung processes."); + +#ifdef IRMD_KILL_ALL_PROCESSES + reg_kill_all_proc(SIGKILL); + nanosleep(&ts, NULL); +#else + reg_kill_all_spawned(SIGKILL); +#endif + finish: + return (void *) 0; +} + +static void kill_all_spawned(void) +{ + pid_t pid; + pthread_t grimreaper; + +#ifdef IRMD_KILL_ALL_PROCESSES + reg_kill_all_proc(SIGTERM); +#else + reg_kill_all_spawned(SIGTERM); +#endif + pthread_create(&grimreaper, NULL, kill_dash_nine, NULL); + + pid = reg_first_spawned(); + while (pid != -1) { + int s; + if (kill(pid, 0) == 0) + waitpid(pid, &s, 0); + else { + log_warn("Child process %d died.", pid); + reg_destroy_spawned(pid); + cleanup_pid(pid); + } + pid = reg_first_spawned(); + } + + pthread_join(grimreaper, NULL); +} + int main(int argc, char ** argv) { @@ -2895,6 +2081,11 @@ int main(int argc, if (irm_init() < 0) goto fail_irm_init; + if (reg_init() < 0) { + log_err("Failed to initialize registry."); + goto fail_reg; + } + pthread_sigmask(SIG_BLOCK, &sigset, NULL); if (irm_start() < 0) @@ -2908,19 +2099,27 @@ int main(int argc, #endif irm_sigwait(sigset); + kill_all_spawned(); + irm_stop(); pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + reg_clear(); + + reg_fini(); + irm_fini(); - log_info("Bye."); + log_info("Ouroboros IPC Resource Manager daemon exited. Bye."); log_fini(); exit(ret); fail_irm_start: + reg_fini(); + fail_reg: irm_fini(); fail_irm_init: exit(EXIT_FAILURE); diff --git a/src/irmd/reg/CMakeLists.txt b/src/irmd/reg/CMakeLists.txt new file mode 100644 index 00000000..64fc3bee --- /dev/null +++ b/src/irmd/reg/CMakeLists.txt @@ -0,0 +1,7 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +include_directories(${CMAKE_SOURCE_DIR}/include) +include_directories(${CMAKE_BINARY_DIR}/include) + +add_subdirectory(tests)
\ No newline at end of file diff --git a/src/irmd/reg/flow.c b/src/irmd/reg/flow.c index 66bd25a3..4d091b23 100644 --- a/src/irmd/reg/flow.c +++ b/src/irmd/reg/flow.c @@ -20,199 +20,189 @@ * Foundation, Inc., http://www.fsf.org/about/contact/. */ -#define _POSIX_C_SOURCE 200112L +#define _POSIX_C_SOURCE 200809L -#include "config.h" +#define OUROBOROS_PREFIX "reg/flow" -#define OUROBOROS_PREFIX "reg-flow" - -#include <ouroboros/errno.h> -#include <ouroboros/flow.h> #include <ouroboros/logs.h> -#include <ouroboros/time_utils.h> -#include <ouroboros/pthread.h> #include "flow.h" #include <assert.h> +#include <errno.h> #include <stdbool.h> #include <stdlib.h> -#include <string.h> -struct reg_flow * reg_flow_create(pid_t n_pid, - pid_t n_1_pid, - int flow_id, - qosspec_t qs) +struct reg_flow * reg_flow_create(const struct flow_info * info) { - pthread_condattr_t cattr; - struct reg_flow * f; - - f = malloc(sizeof(*f)); - if (f == NULL) + struct reg_flow * flow; + + assert(info != NULL); + assert(info->id > 0); + assert(info->n_pid != 0); + assert(info->n_1_pid == 0); + assert(info->mpl == 0); + assert(info->state == FLOW_INIT); + + flow = malloc(sizeof(*flow)); + if (flow == NULL) { + log_err("Failed to malloc flow."); goto fail_malloc; - - memset(f, 0, sizeof(*f)); - - if (pthread_condattr_init(&cattr)) - goto fail_cattr; - -#ifndef __APPLE__ - pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); -#endif - if (pthread_cond_init(&f->cond, &cattr)) - goto fail_cond; - - if (pthread_mutex_init(&f->mtx, NULL)) - goto fail_mutex; - - f->n_rb = shm_rbuff_create(n_pid, flow_id); - if (f->n_rb == NULL) { - log_err("Could not create N ringbuffer flow %d, pid %d.", - flow_id, n_pid); - goto fail_n_rbuff; } - f->n_1_rb = shm_rbuff_create(n_1_pid, flow_id); - if (f->n_1_rb == NULL) { - log_err("Could not create N - 1 ringbuffer flow %d, pid %d.", - flow_id, n_1_pid); - goto fail_n_1_rbuff; - } - - if (clock_gettime(CLOCK_MONOTONIC, &f->t0) < 0) - log_warn("Failed to set timestamp."); + memset(flow, 0, sizeof(*flow)); - pthread_condattr_destroy(&cattr); + clock_gettime(PTHREAD_COND_CLOCK, &flow->t0); + list_head_init(&flow->next); - f->n_pid = n_pid; - f->n_1_pid = n_1_pid; - f->flow_id = flow_id; - f->qs = qs; + flow->info = *info; - f->state = FLOW_ALLOC_PENDING; + return flow; - return f; - - fail_n_1_rbuff: - shm_rbuff_destroy(f->n_rb); - fail_n_rbuff: - pthread_mutex_destroy(&f->mtx); - fail_mutex: - pthread_cond_destroy(&f->cond); - fail_cond: - pthread_condattr_destroy(&cattr); - fail_cattr: - free(f); fail_malloc: return NULL; } -static void cancel_irm_destroy(void * o) +static void destroy_rbuffs(struct reg_flow * flow) { - struct reg_flow * f = (struct reg_flow *) o; - - pthread_mutex_unlock(&f->mtx); + if (flow->n_rb != NULL) + shm_rbuff_destroy(flow->n_rb); + flow->n_rb = NULL; - pthread_cond_destroy(&f->cond); - pthread_mutex_destroy(&f->mtx); - - shm_rbuff_destroy(f->n_rb); - shm_rbuff_destroy(f->n_1_rb); - - free(f); + if (flow->n_1_rb != NULL) + shm_rbuff_destroy(flow->n_1_rb); + flow->n_1_rb = NULL; } -void reg_flow_destroy(struct reg_flow * f) +void reg_flow_destroy(struct reg_flow * flow) { - assert(f); - - pthread_mutex_lock(&f->mtx); - - assert(f->data.len == 0); - - if (f->state == FLOW_DESTROY) { - pthread_mutex_unlock(&f->mtx); - return; + assert(flow != NULL); + + switch(flow->info.state) { + case FLOW_ACCEPT_PENDING: + clrbuf(flow->data); + /* FALLTHRU */ + default: + destroy_rbuffs(flow); + break; } - if (f->state == FLOW_ALLOC_PENDING) - f->state = FLOW_DESTROY; - else - f->state = FLOW_NULL; - - pthread_cond_broadcast(&f->cond); - - pthread_cleanup_push(cancel_irm_destroy, f); + assert(flow->n_rb == NULL); + assert(flow->n_1_rb == NULL); + assert(flow->data.data == NULL); + assert(flow->data.len == 0); - while (f->state != FLOW_NULL) - pthread_cond_wait(&f->cond, &f->mtx); + assert(list_is_empty(&flow->next)); - pthread_cleanup_pop(true); + free(flow); } -enum flow_state reg_flow_get_state(struct reg_flow * f) +static int create_rbuffs(struct reg_flow * flow, + struct flow_info * info) { - enum flow_state state; + assert(flow != NULL); + assert(info != NULL); - assert(f); + flow->n_rb = shm_rbuff_create(info->n_pid, info->id); + if (flow->n_rb == NULL) + goto fail_n_rb; - pthread_mutex_lock(&f->mtx); + assert(flow->info.n_1_pid == 0); + assert(flow->n_1_rb == NULL); - state = f->state; + flow->info.n_1_pid = info->n_1_pid; + flow->n_1_rb = shm_rbuff_create(info->n_1_pid, info->id); + if (flow->n_1_rb == NULL) + goto fail_n_1_rb; - pthread_mutex_unlock(&f->mtx); + return 0; - return state; + fail_n_1_rb: + shm_rbuff_destroy(flow->n_rb); + fail_n_rb: + return -ENOMEM; } -void reg_flow_set_state(struct reg_flow * f, - enum flow_state state) +int reg_flow_update(struct reg_flow * flow, + struct flow_info * info) { - assert(f); - assert(state != FLOW_DESTROY); + assert(flow != NULL); + assert(info != NULL); + + assert(flow->info.id == info->id); + + switch(info->state) { + case FLOW_ACCEPT_PENDING: + assert(flow->info.state == FLOW_INIT); + flow->info.n_pid = info->n_pid; + break; + case FLOW_ALLOC_PENDING: + assert(flow->info.state == FLOW_INIT); + assert(info->n_1_pid != 0); + + if (create_rbuffs(flow, info) < 0) + goto fail; + + break; + case FLOW_ALLOCATED: + assert(info->n_1_pid != 0); + assert(flow->info.state > FLOW_INIT); + assert(flow->info.state < FLOW_ALLOCATED); + assert(flow->info.n_pid != 0); + assert(info->mpl != 0); + + flow->info.mpl = info->mpl; + + if (flow->info.state == FLOW_ALLOC_PENDING) + break; + + flow->info.qs = info->qs; + + if (create_rbuffs(flow, info) < 0) + goto fail; + break; + case FLOW_DEALLOCATED: + destroy_rbuffs(flow); + break; + case FLOW_DEALLOC_PENDING: + break; + default: + assert(false); + return -EPERM; + } - pthread_mutex_lock(&f->mtx); + flow->info.state = info->state; - f->state = state; - pthread_cond_broadcast(&f->cond); + *info = flow->info; - pthread_mutex_unlock(&f->mtx); + return 0; + fail: + return -ENOMEM; } -int reg_flow_wait_state(struct reg_flow * f, - enum flow_state state, - struct timespec * dl) +void reg_flow_set_data(struct reg_flow * flow, + const buffer_t * buf) { - int ret = 0; - int s; - - assert(f); - assert(state != FLOW_NULL); - assert(state != FLOW_DESTROY); - assert(state != FLOW_DEALLOC_PENDING); - - pthread_mutex_lock(&f->mtx); + assert(flow != NULL); + assert(buf != NULL); + assert(flow->data.data == NULL); + assert(flow->data.len == 0); - assert(f->state != FLOW_NULL); - - pthread_cleanup_push(__cleanup_mutex_unlock, &f->mtx); - - while (!(f->state == state || - f->state == FLOW_DESTROY || - f->state == FLOW_DEALLOC_PENDING) && - ret != -ETIMEDOUT) - ret = -__timedwait(&f->cond, &f->mtx, dl); + flow->data = *buf; +} - if (f->state == FLOW_DESTROY || - f->state == FLOW_DEALLOC_PENDING || - ret == -ETIMEDOUT) { - f->state = FLOW_NULL; - pthread_cond_broadcast(&f->cond); - } +void reg_flow_get_data(struct reg_flow * flow, + buffer_t * buf) +{ + assert(flow != NULL); + assert(buf != NULL); - s = f->state; + *buf = flow->data; - pthread_cleanup_pop(true); + clrbuf(flow->data); +} - return ret ? ret : s; +void reg_flow_free_data(struct reg_flow * flow) +{ + freebuf(flow->data); } diff --git a/src/irmd/reg/flow.h b/src/irmd/reg/flow.h index 22e191be..75ada971 100644 --- a/src/irmd/reg/flow.h +++ b/src/irmd/reg/flow.h @@ -24,51 +24,40 @@ #define OUROBOROS_IRMD_REG_FLOW_H #include <ouroboros/list.h> +#include <ouroboros/flow.h> +#include <ouroboros/pthread.h> #include <ouroboros/qos.h> #include <ouroboros/shm_rbuff.h> #include <ouroboros/utils.h> #include <sys/types.h> -#include <pthread.h> #include <time.h> struct reg_flow { - struct list_head next; + struct list_head next; - int flow_id; + struct flow_info info; - pid_t n_pid; - pid_t n_1_pid; - - qosspec_t qs; - time_t mpl; - buffer_t data; + buffer_t data; + struct timespec t0; struct shm_rbuff * n_rb; struct shm_rbuff * n_1_rb; - - struct timespec t0; - - enum flow_state state; - pthread_cond_t cond; - pthread_mutex_t mtx; }; -struct reg_flow * reg_flow_create(pid_t n_pid, - pid_t n_1_pid, - int flow_id, - qosspec_t qs); +struct reg_flow * reg_flow_create(const struct flow_info * info); -void reg_flow_destroy(struct reg_flow * f); +void reg_flow_destroy(struct reg_flow * flow); -enum flow_state reg_flow_get_state(struct reg_flow * f); +int reg_flow_update(struct reg_flow * flow, + struct flow_info * info); +void reg_flow_set_data(struct reg_flow * flow, + const buffer_t * buf); -void reg_flow_set_state(struct reg_flow * f, - enum flow_state state); +void reg_flow_get_data(struct reg_flow * flow, + buffer_t * buf); -int reg_flow_wait_state(struct reg_flow * f, - enum flow_state state, - struct timespec * timeo); +void reg_flow_free_data(struct reg_flow * flow); #endif /* OUROBOROS_IRMD_REG_FLOW_H */ diff --git a/src/irmd/reg/ipcp.c b/src/irmd/reg/ipcp.c index c1d06d94..6580cb5b 100644 --- a/src/irmd/reg/ipcp.c +++ b/src/irmd/reg/ipcp.c @@ -20,126 +20,73 @@ * Foundation, Inc., http://www.fsf.org/about/contact/. */ -#if defined(__linux__) || defined(__CYGWIN__) -#define _DEFAULT_SOURCE -#else #define _POSIX_C_SOURCE 200809L -#endif -#include "config.h" +#define OUROBOROS_PREFIX "reg/ipcp" -#include <ouroboros/errno.h> -#include <ouroboros/hash.h> -#include <ouroboros/ipcp.h> -#include <ouroboros/pthread.h> -#include <ouroboros/time_utils.h> +#include <ouroboros/logs.h> +#include <ouroboros/time.h> #include "ipcp.h" #include <assert.h> -#include <signal.h> +#include <errno.h> #include <stdbool.h> #include <stdlib.h> #include <string.h> struct reg_ipcp * reg_ipcp_create(const struct ipcp_info * info) { - struct reg_ipcp * ipcp; - pthread_condattr_t cattr; + struct reg_ipcp * ipcp; + + assert(info != NULL); + assert(info->state == IPCP_BOOT); ipcp = malloc(sizeof(*ipcp)); - if (ipcp == NULL) + if (ipcp == NULL) { + log_err("Failed to malloc ipcp."); goto fail_malloc; + } - if (pthread_mutex_init(&ipcp->mtx, NULL)) - goto fail_mutex; - - if (pthread_condattr_init(&cattr)) - goto fail_cattr; -#ifndef __APPLE__ - pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); -#endif - if (pthread_cond_init(&ipcp->cond, &cattr)) - goto fail_cond; - - memcpy(&ipcp->info, info, sizeof(*info)); + memset(ipcp, 0, sizeof(*ipcp)); + memset(&ipcp->layer, 0, sizeof(ipcp->layer)); - pthread_condattr_destroy(&cattr); + list_head_init(&ipcp->next); - ipcp->layer = NULL; - ipcp->state = IPCP_BOOT; + ipcp->info = *info; + ipcp->info.state = IPCP_BOOT; - list_head_init(&ipcp->next); + strcpy(ipcp->layer.name, "Not enrolled."); return ipcp; - fail_cond: - pthread_condattr_destroy(&cattr); - fail_cattr: - pthread_mutex_destroy(&ipcp->mtx); - fail_mutex: - free(ipcp); fail_malloc: return NULL; } void reg_ipcp_destroy(struct reg_ipcp * ipcp) { - assert(ipcp); - - pthread_mutex_lock(&ipcp->mtx); - - while (ipcp->state == IPCP_BOOT) - pthread_cond_wait(&ipcp->cond, &ipcp->mtx); - - free(ipcp->layer); + assert(ipcp != NULL); - pthread_mutex_unlock(&ipcp->mtx); - - pthread_cond_destroy(&ipcp->cond); - pthread_mutex_destroy(&ipcp->mtx); + assert(list_is_empty(&ipcp->next)); free(ipcp); } -void reg_ipcp_set_state(struct reg_ipcp * ipcp, - enum ipcp_state state) +void reg_ipcp_update(struct reg_ipcp * ipcp, + const struct ipcp_info * info) { - pthread_mutex_lock(&ipcp->mtx); - - ipcp->state = state; - pthread_cond_broadcast(&ipcp->cond); + assert(ipcp != NULL); + assert(info->state != IPCP_INIT); - pthread_mutex_unlock(&ipcp->mtx); + ipcp->info = *info; } -int reg_ipcp_wait_boot(struct reg_ipcp * ipcp) +void reg_ipcp_set_layer(struct reg_ipcp * ipcp, + const struct layer_info * info) { - int ret = 0; - struct timespec dl; - struct timespec to = {SOCKET_TIMEOUT / 1000, - (SOCKET_TIMEOUT % 1000) * MILLION}; - - clock_gettime(PTHREAD_COND_CLOCK, &dl); - ts_add(&dl, &to, &dl); - - pthread_mutex_lock(&ipcp->mtx); - - while (ipcp->state == IPCP_BOOT && ret != ETIMEDOUT) - ret = pthread_cond_timedwait(&ipcp->cond, &ipcp->mtx, &dl); - - if (ret == ETIMEDOUT) { - kill(ipcp->pid, SIGTERM); - ipcp->state = IPCP_NULL; - pthread_cond_signal(&ipcp->cond); - } - - if (ipcp->state != IPCP_OPERATIONAL) { - pthread_mutex_unlock(&ipcp->mtx); - return -1; - } - - pthread_mutex_unlock(&ipcp->mtx); + assert(ipcp != NULL); + assert(ipcp->info.state == IPCP_OPERATIONAL); - return 0; + ipcp->layer = *info; } diff --git a/src/irmd/reg/ipcp.h b/src/irmd/reg/ipcp.h index 6dfdfb6b..375973a7 100644 --- a/src/irmd/reg/ipcp.h +++ b/src/irmd/reg/ipcp.h @@ -24,28 +24,24 @@ #define OUROBOROS_IRMD_REG_IPCP_H #include <ouroboros/list.h> +#include <ouroboros/ipcp.h> struct reg_ipcp { struct list_head next; struct ipcp_info info; - pid_t pid; - enum hash_algo dir_hash_algo; - char * layer; - - enum ipcp_state state; - pthread_cond_t cond; - pthread_mutex_t mtx; + struct layer_info layer; }; struct reg_ipcp * reg_ipcp_create(const struct ipcp_info * info); -void reg_ipcp_destroy(struct reg_ipcp * i); +void reg_ipcp_destroy(struct reg_ipcp * ipcp); -void reg_ipcp_set_state(struct reg_ipcp * i, - enum ipcp_state state); +void reg_ipcp_update(struct reg_ipcp * ipcp, + const struct ipcp_info * info); -int reg_ipcp_wait_boot(struct reg_ipcp * i); +void reg_ipcp_set_layer(struct reg_ipcp * ipcp, + const struct layer_info * info); -#endif /* OUROBOROS_IRMD_REG_IPCP_H */
\ No newline at end of file +#endif /* OUROBOROS_IRMD_REG_IPCP_H */ diff --git a/src/irmd/reg/name.c b/src/irmd/reg/name.c index db9842d1..1ac939a5 100644 --- a/src/irmd/reg/name.c +++ b/src/irmd/reg/name.c @@ -1,3 +1,4 @@ + /* * Ouroboros - Copyright (C) 2016 - 2024 * @@ -20,428 +21,355 @@ * Foundation, Inc., http://www.fsf.org/about/contact/. */ -#if defined(__linux__) || defined(__CYGWIN__) -#define _DEFAULT_SOURCE -#else #define _POSIX_C_SOURCE 200809L -#endif - -#include "config.h" -#define OUROBOROS_PREFIX "reg_name" +#define OUROBOROS_PREFIX "reg/name" -#include <ouroboros/errno.h> #include <ouroboros/logs.h> -#include <ouroboros/time_utils.h> -#include <ouroboros/pthread.h> +#include <ouroboros/utils.h> #include "name.h" -#include "utils.h" +#include <assert.h> +#include <errno.h> #include <stdlib.h> -#include <stdbool.h> #include <string.h> -#include <signal.h> -#include <unistd.h> -#include <limits.h> -#include <assert.h> - -struct reg_name * reg_name_create(const char * name, - enum pol_balance lb) -{ - pthread_condattr_t cattr; - struct reg_name * n; - - assert(name != NULL); - n = malloc(sizeof(*n)); - if (n == NULL) - goto fail_malloc; - - if (pthread_condattr_init(&cattr)) - goto fail_cattr; - -#ifndef __APPLE__ - pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); -#endif - if (pthread_cond_init(&n->cond, &cattr)) - goto fail_cond; - - if (pthread_mutex_init(&n->mtx, NULL)) - goto fail_mutex; +struct prog_entry { + struct list_head next; + char ** exec; +}; - n->name = strdup(name); - if (n->name == NULL) - goto fail_name; +struct proc_entry { + struct list_head next; + pid_t pid; +}; - pthread_condattr_destroy(&cattr); - - list_head_init(&n->next); - list_head_init(&n->reg_progs); - list_head_init(&n->reg_pids); - - n->pol_lb = lb; - n->state = NAME_IDLE; - - return n; +static void __free_prog_entry(struct prog_entry * entry) +{ + assert(entry != NULL); + assert(entry->exec != NULL); - fail_name: - pthread_mutex_destroy(&n->mtx); - fail_mutex: - pthread_cond_destroy(&n->cond); - fail_cond: - pthread_condattr_destroy(&cattr); - fail_cattr: - free(n); - fail_malloc: - return NULL; + argvfree(entry->exec); + free(entry); } -static void cancel_reg_name_destroy(void * o) +struct reg_name * reg_name_create(const struct name_info * info) { struct reg_name * name; - struct list_head * p; - struct list_head * h; - name = (struct reg_name *) o; + assert(info != NULL); - pthread_mutex_unlock(&name->mtx); - - pthread_cond_destroy(&name->cond); - pthread_mutex_destroy(&name->mtx); + name = malloc(sizeof(*name)); + if (name == NULL) { + log_err("Failed to malloc name."); + goto fail_malloc; + } - if (name->name != NULL) - free(name->name); + list_head_init(&name->next); + list_head_init(&name->progs); + list_head_init(&name->procs); + list_head_init(&name->active); - list_for_each_safe(p, h, &name->reg_pids) { - struct pid_el * pe = list_entry(p, struct pid_el, next); - list_del(&pe->next); - free(pe); - } + name->info = *info; + name->n_progs = 0; + name->n_procs = 0; + name->n_active = 0; - list_for_each_safe(p, h, &name->reg_progs) { - struct str_el * se = list_entry(p, struct str_el, next); - list_del(&se->next); - free(se->str); - free(se); - } + return name; - free(name); + fail_malloc: + return NULL; } void reg_name_destroy(struct reg_name * name) { - if (name == NULL) - return; - - pthread_mutex_lock(&name->mtx); - - if (name->state == NAME_DESTROY) { - pthread_mutex_unlock(&name->mtx); - return; - } - - if (name->state != NAME_FLOW_ACCEPT) - name->state = NAME_NULL; - else - name->state = NAME_DESTROY; + assert(name != NULL); - pthread_cond_broadcast(&name->cond); + assert(list_is_empty(&name->next)); - pthread_cleanup_push(cancel_reg_name_destroy, name); + assert(name->n_progs == 0); + assert(name->n_procs == 0); + assert(name->n_active == 0); - while (name->state != NAME_NULL) - pthread_cond_wait(&name->cond, &name->mtx); + assert(list_is_empty(&name->progs)); + assert(list_is_empty(&name->procs)); + assert(list_is_empty(&name->active)); - pthread_cleanup_pop(true); + free(name); } -static bool reg_name_has_prog(struct reg_name * name, - const char * prog) +static struct proc_entry * __reg_name_get_active(const struct reg_name * name, + pid_t pid) { struct list_head * p; - list_for_each(p, &name->reg_progs) { - struct str_el * name = list_entry(p, struct str_el, next); - if (!strcmp(name->str, prog)) - return true; - } - - return false; -} - -int reg_name_add_prog(struct reg_name * name, - struct reg_prog * a) -{ - struct str_el * n; - - if (reg_name_has_prog(name, a->prog)) { - log_warn("Program %s already accepting flows for %s.", - a->prog, name->name); - return 0; - } - - if (!(a->flags & BIND_AUTO)) { - log_dbg("Program %s cannot be auto-instantiated.", a->prog); - return 0; - } - - n = malloc(sizeof(*n)); - if (n == NULL) - return -ENOMEM; + assert(name != NULL); + assert(pid > 0); - n->str = strdup(a->prog); - if (n->str == NULL) { - free(n); - return -ENOMEM; + list_for_each(p, &name->active) { + struct proc_entry * entry; + entry = list_entry(p, struct proc_entry, next); + if (entry->pid == pid) + return entry; } - list_add(&n->next, &name->reg_progs); - - pthread_mutex_lock(&name->mtx); - - if (name->state == NAME_IDLE) - name->state = NAME_AUTO_ACCEPT; - - pthread_mutex_unlock(&name->mtx); - - return 0; + return NULL; } -void reg_name_del_prog(struct reg_name * name, - const char * prog) +static void __reg_name_del_all_active(struct reg_name * name, + pid_t pid) { struct list_head * p; struct list_head * h; - list_for_each_safe(p, h, &name->reg_progs) { - struct str_el * se = list_entry(p, struct str_el, next); - if (strcmp(prog, se->str) == 0) { - list_del(&se->next); - free(se->str); - free(se); + list_for_each_safe(p, h, &name->active) { + struct proc_entry * entry; + entry = list_entry(p, struct proc_entry, next); + if (entry->pid == pid) { + list_del(&entry->next); + free(entry); + name->n_active--; } } - - pthread_mutex_lock(&name->mtx); - - if (name->state == NAME_AUTO_ACCEPT && list_is_empty(&name->reg_progs)) { - name->state = NAME_IDLE; - pthread_cond_broadcast(&name->cond); - } - - pthread_mutex_unlock(&name->mtx); } -char * reg_name_get_prog(struct reg_name * name) +static struct proc_entry * __reg_name_get_proc(const struct reg_name * name, + pid_t pid) { - if (!list_is_empty(&name->reg_pids) || list_is_empty(&name->reg_progs)) - return NULL; + struct list_head * p; + + assert(name != NULL); + assert(pid > 0); - return list_first_entry(&name->reg_progs, struct str_el, next)->str; + list_for_each(p, &name->procs) { + struct proc_entry * entry; + entry = list_entry(p, struct proc_entry, next); + if (entry->pid == pid) + return entry; + } + + return NULL; } -static bool reg_name_has_pid(struct reg_name * name, - pid_t pid) +static struct prog_entry * __reg_name_get_prog(const struct reg_name * name, + const char * prog) { struct list_head * p; - list_for_each(p, &name->reg_progs) { - struct pid_el * name = list_entry(p, struct pid_el, next); - if (name->pid == pid) - return true; + assert(name != NULL); + assert(prog != NULL); + + list_for_each(p, &name->progs) { + struct prog_entry * entry; + entry = list_entry(p, struct prog_entry, next); + if (strcmp(entry->exec[0], prog) == 0) + return entry; } - return false; + return NULL; } -int reg_name_add_pid(struct reg_name * name, - pid_t pid) +int reg_name_add_active(struct reg_name * name, + pid_t pid) { - struct pid_el * i; + struct proc_entry * entry; - assert(name); + assert(name != NULL); + assert(pid > 0); - if (reg_name_has_pid(name, pid)) { - log_dbg("Process already registered with this name."); - return -EPERM; - } + assert(__reg_name_get_proc(name, pid) != NULL); - pthread_mutex_lock(&name->mtx); + log_dbg("Process %d accepting flows for %s.", pid, name->info.name); - if (name->state == NAME_NULL) { - pthread_mutex_unlock(&name->mtx); - log_dbg("Tried to add instance in NULL state."); - return -EPERM; - } + if (__reg_name_get_active(name, pid) != NULL) + log_dbg("Process calling accept from multiple threads."); - i = malloc(sizeof(*i)); - if (i == NULL) { - pthread_mutex_unlock(&name->mtx); - return -ENOMEM; + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc active."); + goto fail_malloc; } - i->pid = pid; + entry->pid = pid; - /* load balancing policy assigns queue order for this process. */ - switch(name->pol_lb) { + switch (name->info.pol_lb) { case LB_RR: /* Round robin policy. */ - list_add_tail(&i->next, &name->reg_pids); + list_add_tail(&entry->next, &name->active); break; case LB_SPILL: /* Keep accepting flows on the current process */ - list_add(&i->next, &name->reg_pids); + list_add(&entry->next, &name->active); break; default: - free(i); - assert(false); - }; - - if (name->state == NAME_IDLE || - name->state == NAME_AUTO_ACCEPT || - name->state == NAME_AUTO_EXEC) { - name->state = NAME_FLOW_ACCEPT; - pthread_cond_broadcast(&name->cond); + goto fail_unreachable; } - pthread_mutex_unlock(&name->mtx); + name->n_active++; return 0; -} -void reg_name_set_policy(struct reg_name * name, - enum pol_balance lb) -{ - name->pol_lb = lb; + fail_unreachable: + free(entry); + assert(false); + fail_malloc: + return -1; } -static void reg_name_check_state(struct reg_name * name) +void reg_name_del_active(struct reg_name * name, + pid_t pid) { - assert(name); + struct proc_entry * entry; - if (name->state == NAME_DESTROY) { - name->state = NAME_NULL; - pthread_cond_broadcast(&name->cond); + entry = __reg_name_get_active(name, pid); + if (entry == NULL) return; - } - if (list_is_empty(&name->reg_pids)) { - if (!list_is_empty(&name->reg_progs)) - name->state = NAME_AUTO_ACCEPT; - else - name->state = NAME_IDLE; - } else { - name->state = NAME_FLOW_ACCEPT; - } + list_del(&entry->next); + + name->n_active--; - pthread_cond_broadcast(&name->cond); + free(entry); } -void reg_name_del_pid_el(struct reg_name * name, - struct pid_el * p) +pid_t reg_name_get_active(struct reg_name * name) { - assert(name); - assert(p); + assert(name != NULL); - list_del(&p->next); - free(p); + if (list_is_empty(&name->active)) + return -1; - reg_name_check_state(name); + return list_first_entry(&name->active, struct proc_entry, next)->pid; } -void reg_name_del_pid(struct reg_name * name, +int reg_name_add_proc(struct reg_name * name, pid_t pid) { - struct list_head * p; - struct list_head * h; + struct proc_entry * entry; - assert(name); + assert(name != NULL); + assert(pid > 0); - if (name == NULL) - return; + assert(__reg_name_get_proc(name, pid) == NULL); - list_for_each_safe(p, h, &name->reg_pids) { - struct pid_el * a = list_entry(p, struct pid_el, next); - if (a->pid == pid) { - list_del(&a->next); - free(a); - } + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc proc."); + goto fail_malloc; } - reg_name_check_state(name); -} + entry->pid = pid; -pid_t reg_name_get_pid(struct reg_name * name) -{ - if (name == NULL) - return -1; + list_add(&entry->next, &name->procs); - if (list_is_empty(&name->reg_pids)) - return -1; + name->n_procs++; - return list_first_entry(&name->reg_pids, struct pid_el, next)->pid; + return 0; + + fail_malloc: + return -1; } -enum name_state reg_name_get_state(struct reg_name * name) +void reg_name_del_proc(struct reg_name * name, + pid_t pid) { - enum name_state state; + struct proc_entry * entry; - assert(name); + assert(name != NULL); + assert(pid > 0); - pthread_mutex_lock(&name->mtx); + entry = __reg_name_get_proc(name, pid); + if (entry == NULL) + return; + + __reg_name_del_all_active(name, pid); - state = name->state; + list_del(&entry->next); - pthread_mutex_unlock(&name->mtx); + free(entry); - return state; + name->n_procs--; + + assert(__reg_name_get_proc(name, pid) == NULL); } -int reg_name_set_state(struct reg_name * name, - enum name_state state) +bool reg_name_has_proc(const struct reg_name * name, + pid_t pid) +{ + return __reg_name_get_proc(name, pid) != NULL; +} char ** exec; + + +int reg_name_add_prog(struct reg_name * name, + char ** exec) { - assert(state != NAME_DESTROY); + struct prog_entry * entry; + + assert(name != NULL); + assert(exec != NULL); + assert(exec[0] != NULL); + + assert(__reg_name_get_prog(name, exec[0]) == NULL); + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc prog."); + goto fail_malloc; + } + + entry->exec = argvdup(exec); + if (entry->exec == NULL) { + log_err("Failed to argvdup prog."); + goto fail_exec; + } - pthread_mutex_lock(&name->mtx); + list_add(&entry->next, &name->progs); - name->state = state; - pthread_cond_broadcast(&name->cond); + log_dbg("Add prog %s to name %s.", exec[0], name->info.name); - pthread_mutex_unlock(&name->mtx); + name->n_progs++; return 0; + + fail_exec: + free(entry); + fail_malloc: + return -1; } -int reg_name_leave_state(struct reg_name * name, - enum name_state state, - struct timespec * timeout) +void reg_name_del_prog(struct reg_name * name, + const char * prog) { - struct timespec ts; - struct timespec * abstime = NULL; - int ret = 0; + struct prog_entry * entry; - assert(name); - assert(state != NAME_DESTROY); + assert(name != NULL); + assert(prog != NULL); - if (timeout != NULL) { - clock_gettime(PTHREAD_COND_CLOCK, &ts); - ts_add(&ts, timeout, &ts); - abstime = &ts; - } + entry = __reg_name_get_prog(name, prog); + if (entry == NULL) + return; - pthread_mutex_lock(&name->mtx); + list_del(&entry->next); - pthread_cleanup_push(__cleanup_mutex_unlock, &name->mtx); + __free_prog_entry(entry); - while (name->state == state && ret != -ETIMEDOUT) - ret = -__timedwait(&name->cond,&name->mtx, abstime); + name->n_progs--; - if (name->state == NAME_DESTROY) { - ret = -1; - name->state = NAME_NULL; - pthread_cond_broadcast(&name->cond); - } + assert(__reg_name_get_prog(name, prog) == NULL); +} - pthread_cleanup_pop(true); +bool reg_name_has_prog(const struct reg_name * name, + const char * prog) +{ + assert(name != NULL); + assert(prog != NULL); + + return __reg_name_get_prog(name, prog) != NULL; +} + +char ** reg_name_get_exec(const struct reg_name * name) +{ + if (list_is_empty(&name->progs)) + return NULL; - return ret; + return list_first_entry(&name->progs, struct prog_entry, next)->exec; } diff --git a/src/irmd/reg/name.h b/src/irmd/reg/name.h index 3b570424..97ca7f04 100644 --- a/src/irmd/reg/name.h +++ b/src/irmd/reg/name.h @@ -23,81 +23,56 @@ #ifndef OUROBOROS_IRMD_REG_NAME_H #define OUROBOROS_IRMD_REG_NAME_H -#include <ouroboros/hash.h> -#include <ouroboros/ipcp.h> #include <ouroboros/list.h> -#include <ouroboros/irm.h> - -#include "proc.h" -#include "prog.h" - -#include <stdint.h> -#include <stdbool.h> -#include <pthread.h> -#include <string.h> -#include <sys/types.h> - -enum name_state { - NAME_NULL = 0, - NAME_IDLE, - NAME_AUTO_ACCEPT, - NAME_AUTO_EXEC, - NAME_FLOW_ACCEPT, - NAME_FLOW_ARRIVED, - NAME_DESTROY -}; +#include <ouroboros/name.h> + +#define BIND_AUTO 0x01 -/* An entry in the registry */ struct reg_name { - struct list_head next; - char * name; - - /* Policies for this name. */ - enum pol_balance pol_lb; /* Load balance incoming flows. */ - /* Programs that can be instantiated by the irmd. */ - struct list_head reg_progs; - /* Processes that are listening for this name. */ - struct list_head reg_pids; - - enum name_state state; - pthread_cond_t cond; - pthread_mutex_t mtx; -}; + struct list_head next; -struct reg_name * reg_name_create(const char * name, - enum pol_balance lb); + struct name_info info; -void reg_name_destroy(struct reg_name * n); + struct list_head progs; /* autostart programs for this name */ + size_t n_progs; /* number of programs */ -int reg_name_add_prog(struct reg_name * n, - struct reg_prog * p); + struct list_head procs; /* processes bound to this name */ + size_t n_procs; /* number of processes */ -void reg_name_del_prog(struct reg_name * n, - const char * prog); + struct list_head active; /* processes actively calling accept */ + size_t n_active; /* number of processes accepting */ +}; -char * reg_name_get_prog(struct reg_name * n); +struct reg_name * reg_name_create(const struct name_info * info); -int reg_name_add_pid(struct reg_name * n, - pid_t pid); +void reg_name_destroy(struct reg_name * name); -void reg_name_del_pid(struct reg_name * n, - pid_t pid); +int reg_name_add_proc(struct reg_name * name, + pid_t proc); -void reg_name_del_pid_el(struct reg_name * n, - struct pid_el * p); +void reg_name_del_proc(struct reg_name * name, + pid_t proc); + +bool reg_name_has_proc(const struct reg_name * name, + pid_t proc); + +int reg_name_add_prog(struct reg_name * name, + char ** exec); + +void reg_name_del_prog(struct reg_name * name, + const char * prog); -pid_t reg_name_get_pid(struct reg_name * n); +bool reg_name_has_prog(const struct reg_name * name, + const char * prog); -void reg_name_set_policy(struct reg_name * n, - enum pol_balance lb); +char ** reg_name_get_exec(const struct reg_name * name); -enum name_state reg_name_get_state(struct reg_name * n); +int reg_name_add_active(struct reg_name * name, + pid_t proc); -int reg_name_set_state(struct reg_name * n, - enum name_state state); +pid_t reg_name_get_active(struct reg_name * name); -int reg_name_leave_state(struct reg_name * n, - enum name_state state, - struct timespec * timeout); +void reg_name_del_active(struct reg_name * name, + pid_t proc); #endif /* OUROBOROS_IRMD_REG_NAME_H */ diff --git a/src/irmd/reg/proc.c b/src/irmd/reg/proc.c index ede69b8a..24d10fc1 100644 --- a/src/irmd/reg/proc.c +++ b/src/irmd/reg/proc.c @@ -6,256 +6,178 @@ * Dimitri Staessens <dimitri@ouroboros.rocks> * Sander Vrijders <sander@ouroboros.rocks> * - * This program is free software; you can redistribute it and/or modify + * This procram is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * This program is distributed in the hope that it will be useful, + * This procram is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software + * along with this procram; if not, write to the Free Software * Foundation, Inc., http://www.fsf.org/about/contact/. */ -#if defined(__linux__) || defined(__CYGWIN__) -#define _DEFAULT_SOURCE -#else -#define _POSIX_C_SOURCE 200112L -#endif +#define _POSIX_C_SOURCE 200809L -#include "config.h" +#define OUROBOROS_PREFIX "reg/proc" -#include <ouroboros/list.h> -#include <ouroboros/errno.h> -#include <ouroboros/time_utils.h> +#include <ouroboros/logs.h> #include "proc.h" -#include "name.h" -#include <stdlib.h> -#include <unistd.h> -#include <limits.h> #include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +struct name_entry { + struct list_head next; + char * name; +}; -struct reg_proc * reg_proc_create(pid_t pid, - const char * prog) +static void __free_name_entry(struct name_entry * entry) { - struct reg_proc * proc; - pthread_condattr_t cattr; + assert(entry != NULL); + assert(entry->name != NULL); - assert(prog); + free(entry->name); + free(entry); +} - proc = malloc(sizeof(*proc)); - if (proc == NULL) - goto fail_malloc; +static void __reg_proc_clear_names(struct reg_proc * proc) +{ + struct list_head * p; + struct list_head * h; - if (pthread_condattr_init(&cattr)) - goto fail_condattr; + assert(proc != NULL); -#ifndef __APPLE__ - pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); -#endif + list_for_each_safe(p, h, &proc->names) { + struct name_entry * entry; + entry = list_entry(p, struct name_entry, next); + list_del(&entry->next); + __free_name_entry(entry); + proc->n_names--; + } +} - if (pthread_mutex_init(&proc->lock, NULL)) - goto fail_mutex; +struct reg_proc * reg_proc_create(const struct proc_info * info) +{ + struct reg_proc * proc; - if (pthread_cond_init(&proc->cond, &cattr)) - goto fail_cond; + assert(info != NULL); - proc->set = shm_flow_set_create(pid); - if (proc->set == NULL) - goto fail_set; + proc = malloc(sizeof(*proc)); + if (proc == NULL) { + log_err("Failed to malloc proc."); + goto fail_malloc; + } - proc->prog = strdup(prog); - if(proc->prog == NULL) - goto fail_prog; + proc->set = shm_flow_set_create(info->pid); + if (proc->set == NULL) { + log_err("Failed to create flow set for %d.", info->pid); + goto fail_set; + } list_head_init(&proc->next); list_head_init(&proc->names); - proc->pid = pid; - proc->name = NULL; - proc->state = PROC_INIT; + proc->info = *info; + proc->n_names = 0; return proc; - fail_prog: - shm_flow_set_destroy(proc->set); fail_set: - pthread_cond_destroy(&proc->cond);; - fail_cond: - pthread_mutex_destroy(&proc->lock); - fail_mutex: - pthread_condattr_destroy(&cattr); - fail_condattr: free(proc); fail_malloc: return NULL; } -static void cancel_reg_proc(void * o) -{ - struct reg_proc * proc = (struct reg_proc *) o; - - proc->state = PROC_NULL; - - pthread_mutex_unlock(&proc->lock); -} - void reg_proc_destroy(struct reg_proc * proc) { - struct list_head * p; - struct list_head * h; + assert(proc != NULL); - assert(proc); - - pthread_mutex_lock(&proc->lock); - - if (proc->state == PROC_DESTROY) { - pthread_mutex_unlock(&proc->lock); - return; - } - - if (proc->state == PROC_SLEEP) - proc->state = PROC_DESTROY; - - pthread_cond_signal(&proc->cond); + shm_flow_set_destroy(proc->set); - pthread_cleanup_push(cancel_reg_proc, proc); + __reg_proc_clear_names(proc); - while (proc->state != PROC_INIT) - pthread_cond_wait(&proc->cond, &proc->lock); + assert(list_is_empty(&proc->next)); - pthread_cleanup_pop(false); + assert(proc->n_names == 0); - pthread_mutex_unlock(&proc->lock); + assert(list_is_empty(&proc->names)); - shm_flow_set_destroy(proc->set); + free(proc); +} - pthread_cond_destroy(&proc->cond); - pthread_mutex_destroy(&proc->lock); +static struct name_entry * __reg_proc_get_name(const struct reg_proc * proc, + const char * name) +{ + struct list_head * p; - list_for_each_safe(p, h, &proc->names) { - struct str_el * n = list_entry(p, struct str_el, next); - list_del(&n->next); - if (n->str != NULL) - free(n->str); - free(n); + list_for_each(p, &proc->names) { + struct name_entry * entry; + entry = list_entry(p, struct name_entry, next); + if (strcmp(entry->name, name) == 0) + return entry; } - free(proc->prog); - free(proc); + return NULL; } int reg_proc_add_name(struct reg_proc * proc, const char * name) { - struct str_el * s; + struct name_entry * entry; - assert(proc); - assert(name); + assert(__reg_proc_get_name(proc, name) == NULL); - s = malloc(sizeof(*s)); - if (s == NULL) + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc name."); goto fail_malloc; + } - s->str = strdup(name); - if (s->str == NULL) + entry->name = strdup(name); + if (entry == NULL) { + log_err("Failed to strdup name."); goto fail_name; + } + + list_add(&entry->next, &proc->names); - list_add(&s->next, &proc->names); + proc->n_names++; return 0; fail_name: - free(s); + free(entry); fail_malloc: - return -ENOMEM; + return -1; } void reg_proc_del_name(struct reg_proc * proc, const char * name) { - struct list_head * p = NULL; - struct list_head * h = NULL; - - assert(proc); - assert(name); - - list_for_each_safe(p, h, &proc->names) { - struct str_el * s = list_entry(p, struct str_el, next); - if (!strcmp(name, s->str)) { - list_del(&s->next); - free(s->str); - free(s); - } - } -} - -int reg_proc_sleep(struct reg_proc * proc, - struct timespec * dl) -{ - - int ret = 0; - - assert(proc); + struct name_entry * entry; - pthread_mutex_lock(&proc->lock); - - if (proc->state != PROC_WAKE && proc->state != PROC_DESTROY) - proc->state = PROC_SLEEP; - - pthread_cleanup_push(cancel_reg_proc, proc); - - while (proc->state == PROC_SLEEP && ret != -ETIMEDOUT) - ret = -__timedwait(&proc->cond, &proc->lock, dl); + entry = __reg_proc_get_name(proc, name); + if(entry == NULL) + return; - pthread_cleanup_pop(false); + list_del(&entry->next); - if (proc->state == PROC_DESTROY) { - if (proc->name != NULL) - reg_name_del_pid(proc->name, proc->pid); - ret = -1; - } + __free_name_entry(entry); - proc->state = PROC_INIT; + proc->n_names--; - pthread_cond_broadcast(&proc->cond); - pthread_mutex_unlock(&proc->lock); - - return ret; + assert(__reg_proc_get_name(proc, name) == NULL); } -void reg_proc_wake(struct reg_proc * proc, - struct reg_name * name) +bool reg_proc_has_name(const struct reg_proc * proc, + const char * name) { - assert(proc); - assert(name); - - pthread_mutex_lock(&proc->lock); - - if (proc->state != PROC_SLEEP) { - pthread_mutex_unlock(&proc->lock); - return; - } - - proc->state = PROC_WAKE; - proc->name = name; - - pthread_cond_broadcast(&proc->cond); - - pthread_cleanup_push(cancel_reg_proc, proc); - - while (proc->state == PROC_WAKE) - pthread_cond_wait(&proc->cond, &proc->lock); - - pthread_cleanup_pop(false); - - if (proc->state == PROC_DESTROY) - proc->state = PROC_INIT; - - pthread_mutex_unlock(&proc->lock); -} + return __reg_proc_get_name(proc, name) != NULL; +}
\ No newline at end of file diff --git a/src/irmd/reg/proc.h b/src/irmd/reg/proc.h index 03a6cf96..99f74fef 100644 --- a/src/irmd/reg/proc.h +++ b/src/irmd/reg/proc.h @@ -23,48 +23,26 @@ #ifndef OUROBOROS_IRMD_REG_PROC_H #define OUROBOROS_IRMD_REG_PROC_H +#include <ouroboros/list.h> +#include <ouroboros/proc.h> #include <ouroboros/shm_flow_set.h> -#include "utils.h" - -#include <unistd.h> -#include <ouroboros/pthread.h> - -enum proc_state { - PROC_NULL = 0, - PROC_INIT, - PROC_SLEEP, - PROC_WAKE, - PROC_DESTROY -}; - struct reg_proc { struct list_head next; - pid_t pid; - char * prog; /* program instantiated */ - struct list_head names; /* names for which process accepts flows */ - struct shm_flow_set * set; - struct reg_name * name; /* name for which a flow arrived */ + struct proc_info info; - /* The process will block on this */ - enum proc_state state; - pthread_cond_t cond; - pthread_mutex_t lock; + struct list_head names; /* names for which process accepts flows */ + size_t n_names; /* number of names */ + + struct shm_flow_set * set; }; -struct reg_proc * reg_proc_create(pid_t proc, - const char * prog); +struct reg_proc * reg_proc_create(const struct proc_info * info); void reg_proc_destroy(struct reg_proc * proc); -int reg_proc_sleep(struct reg_proc * proc, - struct timespec * timeo); - -void reg_proc_wake(struct reg_proc * proc, - struct reg_name * name); - -void reg_proc_cancel(struct reg_proc * proc); +void reg_proc_clear(struct reg_proc * proc); int reg_proc_add_name(struct reg_proc * proc, const char * name); @@ -72,4 +50,7 @@ int reg_proc_add_name(struct reg_proc * proc, void reg_proc_del_name(struct reg_proc * proc, const char * name); +bool reg_proc_has_name(const struct reg_proc * proc, + const char * name); + #endif /* OUROBOROS_IRMD_REG_PROC_H */ diff --git a/src/irmd/reg/prog.c b/src/irmd/reg/prog.c index d1003d80..5429774a 100644 --- a/src/irmd/reg/prog.c +++ b/src/irmd/reg/prog.c @@ -20,155 +20,155 @@ * Foundation, Inc., http://www.fsf.org/about/contact/. */ -#if defined(__linux__) || defined(__CYGWIN__) -#define _DEFAULT_SOURCE -#else #define _POSIX_C_SOURCE 200809L -#endif -#include <ouroboros/errno.h> -#include <ouroboros/irm.h> +#define OUROBOROS_PREFIX "reg/prog" + +#include <ouroboros/logs.h> #include <ouroboros/utils.h> #include "prog.h" -#include "utils.h" #include <assert.h> +#include <errno.h> #include <stdlib.h> #include <string.h> +struct name_entry { + struct list_head next; + char * name; +}; -static char ** create_argv(const char * prog, - size_t argc, - char ** argv) +static void __free_name_entry(struct name_entry * entry) { - char ** argv2; - size_t i; - - argv2 = malloc((argc + 2) * sizeof(*argv2)); /* prog + args + NULL */ - if (argv2 == 0) - goto fail_malloc; + assert(entry != NULL); + assert(entry->name != NULL); - argv2[0] = strdup(prog); - if (argv2[0] == NULL) - goto fail_prog; - - for (i = 1; i <= argc; ++i) { - argv2[i] = strdup(argv[i - 1]); - if (argv2[i] == NULL) - goto fail_arg; - } + free(entry->name); + free(entry); +} - argv2[argc + 1] = NULL; +static void __reg_prog_clear_names(struct reg_prog * prog) +{ + struct list_head * p; + struct list_head * h; - return argv2; + assert(prog != NULL); - fail_arg: - argvfree(argv2); - fail_prog: - free(argv2); - fail_malloc: - return NULL; + list_for_each_safe(p, h, &prog->names) { + struct name_entry * entry; + entry = list_entry(p, struct name_entry, next); + list_del(&entry->next); + __free_name_entry(entry); + prog->n_names--; + } } -struct reg_prog * reg_prog_create(const char * prog, - uint32_t flags, - int argc, - char ** argv) +struct reg_prog * reg_prog_create(const struct prog_info * info) { struct reg_prog * p; - assert(prog); + assert(info != NULL); p = malloc(sizeof(*p)); - if (p == NULL) + if (p == NULL) { + log_err("Failed to malloc prog."); goto fail_malloc; - - memset(p, 0, sizeof(*p)); - - p->prog = strdup(path_strip(prog)); - if (p->prog == NULL) - goto fail_prog; - - if (flags & BIND_AUTO) { - p->argv = create_argv(prog, argc, argv); - if (p->argv == NULL) - goto fail_argv; } list_head_init(&p->next); list_head_init(&p->names); - p->flags = flags; + p->info = *info; + p->n_names = 0; return p; - fail_argv: - free(p->prog); - fail_prog: - free(p); fail_malloc: return NULL; } void reg_prog_destroy(struct reg_prog * prog) { - struct list_head * p; - struct list_head * h; + assert(prog != NULL); - if (prog == NULL) - return; + __reg_prog_clear_names(prog); - list_for_each_safe(p, h, &prog->names) { - struct str_el * s = list_entry(p, struct str_el, next); - list_del(&s->next); - free(s->str); - free(s); - } + assert(list_is_empty(&prog->next)); + + assert(prog->n_names == 0); + + assert(list_is_empty(&prog->names)); - argvfree(prog->argv); - free(prog->prog); free(prog); } +static struct name_entry * __reg_prog_get_name(const struct reg_prog * prog, + const char * name) +{ + struct list_head * p; + + list_for_each(p, &prog->names) { + struct name_entry * entry; + entry = list_entry(p, struct name_entry, next); + if (strcmp(entry->name, name) == 0) + return entry; + } + + return NULL; +} + int reg_prog_add_name(struct reg_prog * prog, const char * name) { - struct str_el * s; + struct name_entry * entry; - if (prog == NULL || name == NULL) - return -EINVAL; + assert(__reg_prog_get_name(prog, name) == NULL); - s = malloc(sizeof(*s)); - if (s == NULL) + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc name."); goto fail_malloc; + } - s->str = strdup(name); - if(s->str == NULL) + entry->name = strdup(name); + if (entry == NULL) { + log_err("Failed to strdup name."); goto fail_name; + } + + list_add(&entry->next, &prog->names); - list_add(&s->next, &prog->names); + prog->n_names++; return 0; fail_name: - free(s); + free(entry); fail_malloc: - return -ENOMEM; + return -1; } void reg_prog_del_name(struct reg_prog * prog, const char * name) { - struct list_head * p; - struct list_head * h; + struct name_entry * entry; - list_for_each_safe(p, h, &prog->names) { - struct str_el * s = list_entry(p, struct str_el, next); - if (!strcmp(name, s->str)) { - list_del(&s->next); - free(s->str); - free(s); - } - } + entry = __reg_prog_get_name(prog, name); + if (entry == NULL) + return; + + list_del(&entry->next); + + __free_name_entry(entry); + + prog->n_names--; + + assert(__reg_prog_get_name(prog, name) == NULL); } + +bool reg_prog_has_name(const struct reg_prog * prog, + const char * name) +{ + return __reg_prog_get_name(prog, name) != NULL; +}
\ No newline at end of file diff --git a/src/irmd/reg/prog.h b/src/irmd/reg/prog.h index fc25c29e..a98fc6a1 100644 --- a/src/irmd/reg/prog.h +++ b/src/irmd/reg/prog.h @@ -24,22 +24,20 @@ #define OUROBOROS_IRMD_REG_PROG_H #include <ouroboros/list.h> +#include <ouroboros/proc.h> -#include <unistd.h> #include <stdint.h> struct reg_prog { struct list_head next; - char * prog; /* name of binary */ - uint32_t flags; - char ** argv; - struct list_head names; /* names that all instances will listen for */ -}; -struct reg_prog * reg_prog_create(const char * prog, - uint32_t flags, - int argc, - char ** argv); + struct prog_info info; + + struct list_head names; /* names to listen for */ + size_t n_names; /* number of names in list */ + }; + +struct reg_prog * reg_prog_create(const struct prog_info * info); void reg_prog_destroy(struct reg_prog * prog); @@ -49,4 +47,7 @@ int reg_prog_add_name(struct reg_prog * prog, void reg_prog_del_name(struct reg_prog * prog, const char * name); +bool reg_prog_has_name(const struct reg_prog * prog, + const char * name); + #endif /* OUROBOROS_IRMD_REG_PROG_H */ diff --git a/src/irmd/reg/reg.c b/src/irmd/reg/reg.c new file mode 100644 index 00000000..29d2a7ba --- /dev/null +++ b/src/irmd/reg/reg.c @@ -0,0 +1,2161 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * +The IPC Resource Manager - Registry + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200809L + +#define OUROBOROS_PREFIX "reg" + +#include <ouroboros/bitmap.h> +#include <ouroboros/errno.h> +#include <ouroboros/list.h> +#include <ouroboros/logs.h> +#include <ouroboros/pthread.h> + +#include "reg.h" +#include "flow.h" +#include "ipcp.h" +#include "name.h" +#include "proc.h" +#include "prog.h" + +#include <assert.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> + +#define ID_OFFT 1 /* reserve some flow_ids */ + +struct { + struct bmp * flow_ids; /* flow_ids for flows */ + struct list_head flows; /* flow information */ + size_t n_flows; /* number of flows */ + + struct list_head ipcps; /* list of ipcps in system */ + size_t n_ipcps; /* number of ipcps */ + + struct list_head names; /* registered names known */ + size_t n_names; /* number of names */ + + struct list_head procs; /* processes */ + size_t n_procs; /* number of processes */ + + struct list_head progs; /* programs known */ + size_t n_progs; /* number of programs */ + + struct list_head spawned; /* child processes */ + size_t n_spawned; /* number of child processes */ + + pthread_mutex_t mtx; /* registry lock */ + pthread_cond_t cond; /* condvar for reg changes */ +} reg; + +struct pid_entry { + struct list_head next; + pid_t pid; +}; + +static struct reg_flow * __reg_get_flow(int flow_id) +{ + struct list_head * p; + + assert(flow_id >= ID_OFFT); + + list_for_each(p, ®.flows) { + struct reg_flow * entry; + entry = list_entry(p, struct reg_flow, next); + if (entry->info.id == flow_id) + return entry; + } + + return NULL; +} + +static struct reg_flow * __reg_get_accept_flow(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.flows) { + struct reg_flow * entry; + entry = list_entry(p, struct reg_flow, next); + if (entry->info.state != FLOW_ACCEPT_PENDING) + continue; + if (entry->info.n_pid == pid) + return entry; + } + + return NULL; +} + +static struct list_head * __reg_after_flow(int flow_id) +{ + struct list_head * p; + + assert(flow_id >= ID_OFFT); + + list_for_each(p, ®.flows) { + struct reg_flow * entry; + entry = list_entry(p, struct reg_flow, next); + if (entry->info.id > flow_id) + break; + } + + return p; +} + +static struct reg_ipcp * __reg_get_ipcp(pid_t pid) +{ + struct list_head * p; + + assert(pid > 0); + + list_for_each(p, ®.ipcps) { + struct reg_ipcp * entry; + entry = list_entry(p, struct reg_ipcp, next); + if (entry->info.pid == pid) + return entry; + } + + return NULL; +} + +static struct reg_ipcp * __reg_get_ipcp_by_layer(const char * layer) +{ + struct list_head * p; + + list_for_each(p, ®.ipcps) { + struct reg_ipcp * entry; + entry = list_entry(p, struct reg_ipcp, next); + if (strcmp(entry->layer.name, layer) == 0) + return entry; + } + + return NULL; +} + +static struct list_head * __reg_after_ipcp(pid_t pid) +{ + struct list_head * p; + + assert(pid > 0); + + list_for_each(p, ®.ipcps) { + struct reg_ipcp * entry; + entry = list_entry(p, struct reg_ipcp, next); + if (entry->info.pid > pid) + break; + } + + return p; +} + +static struct reg_name * __reg_get_name(const char * name) +{ + struct list_head * p; + + assert(name != NULL); + + list_for_each(p, ®.names) { + struct reg_name * entry; + entry = list_entry(p, struct reg_name, next); + if (strcmp(entry->info.name, name) == 0) + return entry; + } + + return NULL; +} + +static struct reg_name * __reg_get_name_by_hash(enum hash_algo algo, + const uint8_t * hash) +{ + struct list_head * p; + uint8_t * thash; + size_t len; + + len = hash_len(algo); + + thash = malloc(len); + if (thash == NULL) + return NULL; + + list_for_each(p, ®.names) { + struct reg_name * n = list_entry(p, struct reg_name, next); + str_hash(algo, thash, n->info.name); + if (memcmp(thash, hash, len) == 0) { + free(thash); + return n; + } + } + + free(thash); + + return NULL; +} + +static int __reg_get_pending_flow_id_for_hash(enum hash_algo algo, + const uint8_t * hash) +{ + struct reg_name * entry; + struct reg_flow * flow; + pid_t pid; + + entry =__reg_get_name_by_hash(algo, hash); + if (entry == NULL) + return -ENAME; + + pid = reg_name_get_active(entry); + if (pid < 0) + return -EAGAIN; + + flow = __reg_get_accept_flow(pid); + assert(flow != NULL); + + return flow->info.id; +} + +static struct list_head * __reg_after_name(const char * name) +{ + struct list_head * p; + + assert(name != NULL); + + list_for_each(p, ®.names) { + struct reg_name * entry; + entry = list_entry(p, struct reg_name, next); + if (strcmp(entry->info.name, name) > 0) + break; + } + + return p; +} + +static struct reg_proc * __reg_get_proc(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.procs) { + struct reg_proc * entry; + entry = list_entry(p, struct reg_proc, next); + if (entry->info.pid == pid) + return entry; + } + + return NULL; +} + +static struct list_head * __reg_after_proc(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.procs) { + struct reg_proc * entry; + entry = list_entry(p, struct reg_proc, next); + if (entry->info.pid > pid) + break; + } + + return p; +} + +static void __reg_kill_all_proc(int signal) +{ + struct list_head * p; + + list_for_each(p, ®.procs) { + struct reg_proc * entry; + entry = list_entry(p, struct reg_proc, next); + kill(entry->info.pid, signal); + } +} + +static pid_t __reg_get_dead_proc(void) +{ + struct list_head * p; + + list_for_each(p, ®.procs) { + struct reg_proc * entry; + entry = list_entry(p, struct reg_proc, next); + if (kill(entry->info.pid, 0) < 0) + return entry->info.pid; + } + + return -1; +} + +static void __reg_cancel_flows_for_proc(pid_t pid) +{ + struct list_head * p; + bool changed = false; + + list_for_each(p, ®.flows) { + struct reg_flow * entry; + entry = list_entry(p, struct reg_flow, next); + if (entry->info.n_pid != pid) + continue; + + switch (entry->info.state) { + case FLOW_ALLOC_PENDING: + /* FALLTHRU */ + case FLOW_ACCEPT_PENDING: + entry->info.state = FLOW_DEALLOCATED; + changed = true; + break; + default: + continue; + } + } + + if (changed) + pthread_cond_broadcast(®.cond); +} + +static struct pid_entry * __reg_get_spawned(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.spawned) { + struct pid_entry * entry; + entry = list_entry(p, struct pid_entry, next); + if (entry->pid == pid) + return entry; + } + + return NULL; +} + +static struct list_head * __reg_after_spawned(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.spawned) { + struct pid_entry * entry; + entry = list_entry(p, struct pid_entry, next); + if (entry->pid > pid) + break; + } + + return p; +} + +static void __reg_kill_all_spawned(int signal) +{ + struct list_head * p; + + list_for_each(p, ®.spawned) { + struct pid_entry * entry; + entry = list_entry(p, struct pid_entry, next); + kill(entry->pid, signal); + } +} + +static pid_t __reg_first_spawned(void) +{ + if (list_is_empty(®.spawned)) + return -1; + + return list_first_entry(®.spawned, struct pid_entry, next)->pid; +} + +static struct reg_prog * __reg_get_prog(const char * name) +{ + struct list_head * p; + + list_for_each(p, ®.progs) { + struct reg_prog * entry; + entry = list_entry(p, struct reg_prog, next); + if (strcmp(entry->info.name, name) == 0) + return entry; + } + + return NULL; +} + +static char ** __reg_get_exec(enum hash_algo algo, + const uint8_t * hash) +{ + struct list_head * p; + uint8_t * buf; + + buf = malloc(hash_len(algo)); + if (buf == NULL) { + log_err("Failed to malloc hash buffer."); + return NULL; + } + + list_for_each(p, ®.names) { + struct reg_name * entry; + entry = list_entry(p, struct reg_name, next); + str_hash(algo, buf, entry->info.name); + if (memcmp(buf, hash, hash_len(algo)) == 0) { + free(buf); + return reg_name_get_exec(entry); + } + } + + free(buf); + + return NULL; +} + +static struct list_head * __reg_after_prog(const char * name) +{ + struct list_head * p; + + list_for_each(p, ®.progs) { + struct reg_prog * entry; + entry = list_entry(p, struct reg_prog, next); + if (strcmp(entry->info.name, name) > 0) + break; + } + + return p; +} + +static void __reg_del_name_from_procs(const char * name) +{ + struct list_head * p; + + list_for_each(p, ®.procs) { + struct reg_proc * proc; + proc = list_entry(p, struct reg_proc, next); + reg_proc_del_name(proc, name); + } +} + +static void __reg_del_name_from_progs(const char * name) +{ + struct list_head * p; + + list_for_each(p, ®.progs) { + struct reg_prog * prog; + prog = list_entry(p, struct reg_prog, next); + reg_prog_del_name(prog, name); + } +} + +static void __reg_proc_update_names(struct reg_proc * proc) +{ + struct list_head * p; + struct reg_prog * prog; + + assert(list_is_empty(&proc->names)); + + prog = __reg_get_prog(proc->info.prog); + if (prog == NULL) + return; + + list_for_each(p, ®.names) { + struct reg_name * name; + name = list_entry(p, struct reg_name, next); + assert(!reg_name_has_proc(name, proc->info.pid)); + if (reg_prog_has_name(prog, name->info.name)) { + reg_proc_add_name(proc, name->info.name); + reg_name_add_proc(name, proc->info.pid); + } + } +} + +static void __reg_del_proc_from_names(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.names) { + struct reg_name * name; + name = list_entry(p, struct reg_name, next); + reg_name_del_proc(name, pid); + } +} + +static void __reg_del_prog_from_names(const char * prog) +{ + struct list_head * p; + + list_for_each(p, ®.names) { + struct reg_name * name; + name = list_entry(p, struct reg_name, next); + reg_name_del_prog(name, prog); + } +} + +static int __reg_add_active_proc(pid_t pid) +{ + struct list_head * p; + size_t n_names = 0; + size_t failed = 0; + + assert(pid > 0); + + list_for_each(p, ®.names) { + struct reg_name * name; + name = list_entry(p, struct reg_name, next); + if (reg_name_has_proc(name, pid)) { + if (reg_name_add_active(name, pid) < 0) + failed++; + n_names++; + } + } + + if (n_names > 0 && failed == n_names) + return -1; + + return 0; /* some were marked */ +} + +static void __reg_del_active_proc(pid_t pid) +{ + struct list_head * p; + + assert(pid > 0); + + list_for_each(p, ®.names) { + struct reg_name * name; + name = list_entry(p, struct reg_name, next); + reg_name_del_active(name, pid); + } +} + +int reg_init(void) +{ + pthread_condattr_t cattr; + + if (pthread_mutex_init(®.mtx, NULL) != 0) { + log_err("Failed to initialize mutex."); + goto fail_mtx; + } + + if (pthread_condattr_init(&cattr) != 0) { + log_err("Failed to initialize condattr."); + goto fail_cattr; + } + +#ifndef __APPLE__ + pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); +#endif + if (pthread_cond_init(®.cond, &cattr) != 0) { + log_err("Failed to initialize condvar."); + goto fail_cond; + } + + reg.flow_ids = bmp_create(SYS_MAX_FLOWS -ID_OFFT, ID_OFFT); + if (reg.flow_ids == NULL) { + log_err("Failed to create flow_ids bitmap."); + goto fail_flow_ids; + } + + pthread_condattr_destroy(&cattr); + + list_head_init(®.flows); + list_head_init(®.ipcps); + list_head_init(®.names); + list_head_init(®.procs); + list_head_init(®.progs); + list_head_init(®.spawned); + + return 0; + + fail_flow_ids: + pthread_cond_destroy(®.cond); + fail_cond: + pthread_condattr_destroy(&cattr); + fail_cattr: + pthread_mutex_destroy(®.mtx); + fail_mtx: + return -1; +} + +void reg_clear(void) +{ + struct list_head * p; + struct list_head * h; + + pthread_mutex_lock(®.mtx); + + list_for_each_safe(p, h, ®.spawned) { + struct pid_entry * entry; + entry = list_entry(p, struct pid_entry, next); + list_del(&entry->next); + free(entry); + reg.n_spawned--; + } + + list_for_each_safe(p, h, ®.progs) { + struct reg_prog * entry; + entry = list_entry(p, struct reg_prog, next); + list_del(&entry->next); + __reg_del_prog_from_names(entry->info.path); + reg_prog_destroy(entry); + reg.n_progs--; + } + + list_for_each_safe(p, h, ®.procs) { + struct reg_proc * entry; + entry = list_entry(p, struct reg_proc, next); + list_del(&entry->next); + __reg_del_proc_from_names(entry->info.pid); + reg_proc_destroy(entry); + reg.n_procs--; + } + + list_for_each_safe(p, h, ®.names) { + struct reg_name * entry; + entry = list_entry(p, struct reg_name, next); + list_del(&entry->next); + reg_name_destroy(entry); + reg.n_names--; + } + + list_for_each_safe(p, h, ®.ipcps) { + struct reg_ipcp * entry; + entry = list_entry(p, struct reg_ipcp, next); + list_del(&entry->next); + reg_ipcp_destroy(entry); + reg.n_ipcps--; + } + + list_for_each_safe(p, h, ®.flows) { + struct reg_flow * entry; + entry = list_entry(p, struct reg_flow, next); + list_del(&entry->next); + reg_flow_destroy(entry); + reg.n_flows--; + } + + pthread_mutex_unlock(®.mtx); +} + +void reg_fini(void) +{ + assert(list_is_empty(®.spawned)); + assert(list_is_empty(®.progs)); + assert(list_is_empty(®.procs)); + assert(list_is_empty(®.names)); + assert(list_is_empty(®.ipcps)); + assert(list_is_empty(®.flows)); + + assert(reg.n_spawned == 0); + assert(reg.n_progs == 0); + assert(reg.n_procs == 0); + assert(reg.n_names == 0); + assert(reg.n_ipcps == 0); + assert(reg.n_flows == 0); + + bmp_destroy(reg.flow_ids); + + if (pthread_cond_destroy(®.cond) != 0) + log_warn("Failed to destroy condvar."); + + if (pthread_mutex_destroy(®.mtx) != 0) + log_warn("Failed to destroy mutex."); +} + +int reg_create_flow(struct flow_info * info) +{ + struct reg_flow * f; + + assert(info != NULL); + assert(info->id == 0); + assert(info->n_pid != 0); + assert(info->state == FLOW_INIT); + + pthread_mutex_lock(®.mtx); + + info->id = bmp_allocate(reg.flow_ids); + if (!bmp_is_id_valid(reg.flow_ids, info->id)) { + log_err("Failed to allocate flow id."); + goto fail_id; + } + + f = reg_flow_create(info); + if (f == NULL) { + log_err("Failed to create flow %d.", info->id); + goto fail_flow; + } + + list_add(&f->next, __reg_after_flow(info->id)); + + reg.n_flows++; + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_flow: + bmp_release(reg.flow_ids, info->id); + info->id = 0; + fail_id: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_destroy_flow(int flow_id) +{ + struct reg_flow * f; + + pthread_mutex_lock(®.mtx); + + f = __reg_get_flow(flow_id); + if (f == NULL) { + log_err("Flow %d does not exist.", flow_id); + goto no_flow; + } + + list_del(&f->next); + + reg.n_flows--; + + bmp_release(reg.flow_ids, flow_id); + + pthread_mutex_unlock(®.mtx); + + pthread_cond_broadcast(®.cond); + + reg_flow_destroy(f); + + return 0; + + no_flow: + pthread_mutex_unlock(®.mtx); + return -1; + +} + +bool reg_has_flow(int flow_id) +{ + bool ret; + + pthread_mutex_lock(®.mtx); + + ret = __reg_get_flow(flow_id) != NULL; + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +int reg_create_ipcp(const struct ipcp_info * info) +{ + struct reg_ipcp * ipcp; + struct pid_entry * entry; + + assert(info != NULL); + assert(info->pid != 0); + assert(info->state == IPCP_BOOT); + + pthread_mutex_lock(®.mtx); + + if (__reg_get_ipcp(info->pid) != NULL) { + log_err("IPCP %d already exists.", info->pid); + goto fail_ipcp; + } + + ipcp = reg_ipcp_create(info); + if (ipcp == NULL) { + log_err("Failed to create ipcp %s.", info->name); + goto fail_ipcp; + } + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to create spawn entry.\n"); + goto fail_spawn; + } + + entry->pid = info->pid; + + list_add(&ipcp->next, __reg_after_ipcp(info->pid)); + list_add(&entry->next, __reg_after_spawned(info->pid)); + + reg.n_ipcps++; + reg.n_spawned++; + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_spawn: + reg_ipcp_destroy(ipcp); + fail_ipcp: + pthread_mutex_unlock(®.mtx); + return -1; + +} + +int reg_destroy_ipcp(pid_t pid) +{ + struct reg_ipcp * ipcp; + struct pid_entry * entry; + + pthread_mutex_lock(®.mtx); + + ipcp = __reg_get_ipcp(pid); + if (ipcp == NULL) { + log_err("IPCP %d does not exist.", pid); + goto no_ipcp; + } + + list_del(&ipcp->next); + + reg.n_ipcps--; + + entry = __reg_get_spawned(pid); + assert(entry != NULL); + + list_del(&entry->next); + free(entry); + reg.n_spawned--; + + pthread_mutex_unlock(®.mtx); + + reg_ipcp_destroy(ipcp); + + return 0; + + no_ipcp: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_update_ipcp(struct ipcp_info * info) +{ + struct reg_ipcp * ipcp; + + pthread_mutex_lock(®.mtx); + + ipcp = __reg_get_ipcp(info->pid); + if (ipcp == NULL) { + log_err("IPCP %d does not exist.", info->pid); + goto no_ipcp; + + } + + reg_ipcp_update(ipcp, info); + + pthread_mutex_unlock(®.mtx); + + reg_ipcp_destroy(ipcp); + + return 0; + + no_ipcp: + pthread_mutex_unlock(®.mtx); + return -1; +} + +bool reg_has_ipcp(pid_t pid) +{ + bool ret; + + pthread_mutex_lock(®.mtx); + + ret = __reg_get_ipcp(pid) != NULL; + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +static int __get_ipcp_info(ipcp_list_msg_t ** msg, + struct reg_ipcp * ipcp) +{ + *msg = malloc(sizeof(**msg)); + if (*msg == NULL) + goto fail; + + ipcp_list_msg__init(*msg); + + (*msg)->name = strdup(ipcp->info.name); + if ((*msg)->name == NULL) + goto fail_name; + + (*msg)->layer = strdup(ipcp->layer.name); + if ((*msg)->layer == NULL) + goto fail_layer; + + (*msg)->pid = ipcp->info.pid; + (*msg)->type = ipcp->info.type; + (*msg)->hash_algo = ipcp->layer.dir_hash_algo; + + return 0; + + fail_layer: + free((*msg)->name); + fail_name: + free(*msg); + *msg = NULL; + fail: + return -1; +} + +int reg_list_ipcps(ipcp_list_msg_t *** ipcps) +{ + struct list_head * p; + int i = 0; + + pthread_mutex_lock(®.mtx); + + if (reg.n_ipcps == 0) + goto finish; + + *ipcps = malloc(reg.n_ipcps * sizeof(**ipcps)); + if (*ipcps == NULL) { + log_err("Failed to malloc ipcps."); + goto fail_malloc; + } + + list_for_each(p, ®.ipcps) { + struct reg_ipcp * entry; + entry = list_entry(p, struct reg_ipcp, next); + if (__get_ipcp_info(&((*ipcps)[i]), entry) < 0) { + log_err("Failed to create ipcp list info."); + goto fail; + } + + ++i; + } + + assert(i == (int) reg.n_ipcps); + finish: + pthread_mutex_unlock(®.mtx); + + return i; + + fail: + while (i > 0) + ipcp_list_msg__free_unpacked((*ipcps)[--i], NULL); + + free(*ipcps); + fail_malloc: + pthread_mutex_unlock(®.mtx); + *ipcps = NULL; + return -ENOMEM; +} + +int reg_create_name(const struct name_info * info) +{ + struct reg_name * n; + + assert(info != NULL); + + pthread_mutex_lock(®.mtx); + + if (__reg_get_name(info->name) != NULL) { + log_dbg("Name %s already exists.", info->name); + goto exists; + } + + n = reg_name_create(info); + if (n == NULL) { + log_err("Failed to create name %s.", info->name); + goto fail_name; + } + + list_add(&n->next, __reg_after_name(info->name)); + + reg.n_names++; + + pthread_mutex_unlock(®.mtx); + return 0; + exists: + pthread_mutex_unlock(®.mtx); + return -EEXIST; + + fail_name: + pthread_mutex_unlock(®.mtx); + return -1; + +} + +int reg_destroy_name(const char * name) +{ + struct reg_name * n; + + pthread_mutex_lock(®.mtx); + + n = __reg_get_name(name); + if (n == NULL) { + log_err("Name %s does not exist.", name); + goto no_name; + } + + __reg_del_name_from_procs(name); + __reg_del_name_from_progs(name); + + list_del(&n->next); + + reg.n_names--; + + pthread_mutex_unlock(®.mtx); + + reg_name_destroy(n); + + return 0; + + no_name: + pthread_mutex_unlock(®.mtx); + return -1; +} + +bool reg_has_name(const char * name) +{ + bool ret; + + pthread_mutex_lock(®.mtx); + + ret = __reg_get_name(name) != NULL; + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +static int __get_name_info(name_info_msg_t ** msg, + struct reg_name * n) +{ + *msg = malloc(sizeof(**msg)); + if (*msg == NULL) + goto fail; + + name_info_msg__init(*msg); + + (*msg)->name = strdup(n->info.name); + if ((*msg)->name == NULL) + goto fail_name; + + (*msg)->pol_lb = n->info.pol_lb; + + return 0; + + fail_name: + free(*msg); + *msg = NULL; + fail: + return -1; +} + +int reg_list_names(name_info_msg_t *** names) +{ + struct list_head * p; + int i = 0; + + pthread_mutex_lock(®.mtx); + + if (reg.n_names == 0) + goto finish; + + *names = malloc(reg.n_names * sizeof(**names)); + if (*names == NULL) { + log_err("Failed to malloc names."); + goto fail_malloc; + } + + list_for_each(p, ®.names) { + struct reg_name * entry; + entry = list_entry(p, struct reg_name, next); + if (__get_name_info(&((*names)[i]), entry) < 0) { + log_err("Failed to create name list info."); + goto fail; + } + + ++i; + } + + assert(i == (int) reg.n_names); + finish: + pthread_mutex_unlock(®.mtx); + + return i; + + fail: + while (i > 0) + name_info_msg__free_unpacked((*names)[--i], NULL); + + free(*names); + fail_malloc: + pthread_mutex_unlock(®.mtx); + *names = NULL; + return -ENOMEM; +} + +int reg_create_proc(const struct proc_info * info) +{ + struct reg_proc * proc; + + assert(info != NULL); + + pthread_mutex_lock(®.mtx); + + if (__reg_get_proc(info->pid) != NULL) { + log_err("Process %d already exists.", info->pid); + goto fail_proc; + } + + proc = reg_proc_create(info); + if (proc == NULL) { + log_err("Failed to create process %d.", info->pid); + goto fail_proc; + } + + __reg_proc_update_names(proc); + + list_add(&proc->next, __reg_after_proc(info->pid)); + + reg.n_procs++; + + pthread_cond_broadcast(®.cond); + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_proc: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_destroy_proc(pid_t pid) +{ + struct reg_proc * proc; + + pthread_mutex_lock(®.mtx); + + proc = __reg_get_proc(pid); + if (proc == NULL) { + log_err("Process %d does not exist.", pid); + goto no_proc; + } + + __reg_del_proc_from_names(pid); + + list_del(&proc->next); + + reg.n_procs--; + + __reg_cancel_flows_for_proc(pid); + + pthread_mutex_unlock(®.mtx); + + reg_proc_destroy(proc); + + return 0; + + no_proc: + pthread_mutex_unlock(®.mtx); + return -1; +} + +bool reg_has_proc(pid_t pid) +{ + bool ret; + + pthread_mutex_lock(®.mtx); + + ret = __reg_get_proc(pid) != NULL; + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +void reg_kill_all_proc(int signal) +{ + pthread_mutex_lock(®.mtx); + + __reg_kill_all_proc(signal); + + pthread_mutex_unlock(®.mtx); +} + +pid_t reg_get_dead_proc(void) +{ + pid_t ret; + + pthread_mutex_lock(®.mtx); + + ret = __reg_get_dead_proc(); + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +int reg_create_spawned(pid_t pid) +{ + struct pid_entry * entry; + + pthread_mutex_lock(®.mtx); + + if (__reg_get_spawned(pid) != NULL) { + log_err("Spawned process %d already exists.", pid); + goto fail_proc; + } + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to create pid_entry %d.", pid); + goto fail_proc; + } + + entry->pid = pid; + + list_add(&entry->next, __reg_after_spawned(pid)); + + reg.n_spawned++; + + pthread_mutex_unlock(®.mtx); + + return 0; + fail_proc: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_destroy_spawned(pid_t pid) +{ + struct pid_entry * entry; + + pthread_mutex_lock(®.mtx); + + entry = __reg_get_spawned(pid); + if (entry == NULL) { + log_err("Spawned process %d does not exist.", pid); + goto no_proc; + } + + list_del(&entry->next); + + reg.n_spawned--; + + pthread_mutex_unlock(®.mtx); + + free(entry); + + return 0; + + no_proc: + pthread_mutex_unlock(®.mtx); + return -1; +} + +bool reg_has_spawned(pid_t pid) +{ + bool ret; + + pthread_mutex_lock(®.mtx); + + ret = __reg_get_spawned(pid) != NULL; + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +void reg_kill_all_spawned(int signal) +{ + pthread_mutex_lock(®.mtx); + + __reg_kill_all_spawned(signal); + + pthread_mutex_unlock(®.mtx); +} + +pid_t reg_first_spawned(void) +{ + pid_t pid; + + pthread_mutex_lock(®.mtx); + + pid = __reg_first_spawned(); + + pthread_mutex_unlock(®.mtx); + + return pid; +} + +int reg_bind_proc(const char * name, + pid_t pid) +{ + struct reg_name * n; + struct reg_proc * p; + + assert(name != NULL); + assert(pid > 0); + + pthread_mutex_lock(®.mtx); + + n = __reg_get_name(name); + if (n == NULL) { + log_err("Could not find name %s.", name); + goto fail; + } + + p = __reg_get_proc(pid); + if (p == NULL) { + log_err("Could not find process %d.", pid); + goto fail; + } + + if (reg_name_has_proc(n, pid)) { + log_err("Process %d already bound to name %s.", pid, name); + goto fail; + } + + if (reg_proc_has_name(p, name)) { + log_err("Name %s already bound to process %d.", name, pid); + } + + if (reg_name_add_proc(n, pid) < 0) { + log_err("Failed to add process %d to name %s.", pid, name); + goto fail; + } + + if (reg_proc_add_name(p, name) < 0) { + log_err("Failed to add name %s to process %d.", name, pid); + goto fail_proc; + } + + if (__reg_get_accept_flow(pid) != NULL) { + if (reg_name_add_active(n, pid) < 0) { + log_warn("Failed to update name %s with active %d", + name, pid); + } + } + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_proc: + reg_name_del_proc(n, pid); + fail: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_unbind_proc(const char * name, + pid_t pid) +{ + struct reg_name * n; + struct reg_proc * p; + + assert(name != NULL); + assert(pid > 0); + + pthread_mutex_lock(®.mtx); + + n = __reg_get_name(name); + if (n == NULL) { + log_err("Could not find name %s.", name); + goto fail; + } + + p = __reg_get_proc(pid); + if (p == NULL) { + log_err("Could not find process %d.", pid); + goto fail; + } + + if (!reg_name_has_proc(n, pid)) { + log_err("Process %d not bound to name %s.", pid, name); + goto fail; + } + + if (!reg_proc_has_name(p, name)) { + log_err("Name %s not bound to process %d.", name, pid); + goto fail; + } + + reg_name_del_proc(n, pid); + + reg_proc_del_name(p, name); + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_create_prog(const struct prog_info * info) +{ + struct reg_prog * prog; + + assert(info != NULL); + + pthread_mutex_lock(®.mtx); + + if (__reg_get_prog(info->name) != NULL) { + log_dbg("Program %s already exists.", info->name); + goto exists; + } + + prog = reg_prog_create(info); + if (prog == NULL) { + log_err("Failed to create program %s.", info->name); + goto fail_prog; + } + + list_add(&prog->next, __reg_after_prog(info->name)); + + reg.n_progs++; + exists: + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_prog: + pthread_mutex_unlock(®.mtx); + return -1; + +} + +int reg_destroy_prog(const char * name) +{ + struct reg_prog * prog; + + pthread_mutex_lock(®.mtx); + + prog = __reg_get_prog(name); + if (prog == NULL) { + log_err("Program %s does not exist.", name); + goto no_prog; + } + + log_err("Removing %s from names.", prog->info.path); + + __reg_del_prog_from_names(prog->info.path); + + list_del(&prog->next); + + reg.n_progs--; + + pthread_mutex_unlock(®.mtx); + + reg_prog_destroy(prog); + + return 0; + + no_prog: + pthread_mutex_unlock(®.mtx); + return -1; +} + +bool reg_has_prog(const char * name) +{ + bool ret; + + assert(name != NULL); + + pthread_mutex_lock(®.mtx); + + ret = __reg_get_prog(name) != NULL; + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +int reg_get_exec(enum hash_algo algo, + const uint8_t * hash, + char *** prog) +{ + char ** exec; + int ret = 0; + + assert(hash != NULL); + assert(prog != NULL); + + pthread_mutex_lock(®.mtx); + + exec = __reg_get_exec(algo, hash); + if (exec == NULL) { + ret = 0; + goto finish; + } + + *prog = argvdup(exec); + if (*prog == NULL) { + log_err("Failed to argvdup exec."); + ret = -ENOMEM; + goto finish; + } + + pthread_mutex_unlock(®.mtx); + + return 0; + + finish: + pthread_mutex_unlock(®.mtx); + return ret; +} + +int reg_bind_prog(const char * name, + char ** exec, + uint8_t flags) +{ + struct reg_name * n; + struct reg_prog * p; + + assert(name != NULL); + assert(exec != NULL); + assert(exec[0] != NULL); + + pthread_mutex_lock(®.mtx); + + n = __reg_get_name(name); + if (n == NULL) { + log_err("Could not find name %s.", name); + goto fail; + } + + p = __reg_get_prog(path_strip(exec[0])); + if (p == NULL) { + log_err("Could not find program %s.", exec[0]); + goto fail; + } + + if (reg_name_has_prog(n, exec[0])) { + log_err("Program %s already bound to %s.", exec[0], name); + goto fail; + } + + if (reg_prog_has_name(p, name)) { + log_err("Name %s already bound to program %s.", name, exec[0]); + goto fail; + } + + + if (flags & BIND_AUTO && reg_name_add_prog(n, exec) < 0) { + log_err("Failed to set autostart %s for %s.", exec[0], name); + goto fail; + } + + if (reg_prog_add_name(p, name) < 0) { + log_err("Failed to add %s to program %s.", name, exec[0]); + goto fail_prog; + } + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_prog: + reg_name_del_prog(n, exec[0]); + fail: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_unbind_prog(const char * name, + const char * prog) +{ + struct reg_name * n; + struct reg_prog * p; + + assert(name != NULL); + assert(prog != NULL); + + pthread_mutex_lock(®.mtx); + + n = __reg_get_name(name); + if (n == NULL) { + log_err("Could not find name %s.", name); + goto fail; + } + + p = __reg_get_prog(prog); + if (p == NULL) { + log_err("Could not find program %s.", prog); + goto fail; + } + + if (!reg_prog_has_name(p, name)) { + log_err("Name %s not bound to program %s.", name, prog); + goto fail; + } + + reg_name_del_prog(n, prog); + + reg_prog_del_name(p, name); + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_set_layer_for_ipcp(struct ipcp_info * info, + const struct layer_info * layer) +{ + struct reg_ipcp * ipcp; + + assert(info != NULL); + assert(info->state > IPCP_BOOT); + assert(info->state < IPCP_SHUTDOWN); + + pthread_mutex_lock(®.mtx); + + ipcp = __reg_get_ipcp(info->pid); + if (ipcp == NULL) { + log_err("IPCP %d not found.", info->pid); + goto fail_ipcp; + } + + reg_ipcp_set_layer(ipcp, layer); + + ipcp->info.state = info->state; + + pthread_mutex_unlock(®.mtx); + + return 0; + fail_ipcp: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_get_ipcp(struct ipcp_info * info, + struct layer_info * layer) +{ + struct reg_ipcp * ipcp; + + assert(info != NULL); + + pthread_mutex_lock(®.mtx); + + ipcp = __reg_get_ipcp(info->pid); + if (ipcp == NULL) { + log_err("IPCP %d not found.", info->pid); + goto fail_ipcp; + } + + *info = ipcp->info; + if (layer != NULL) + *layer = ipcp->layer; + + pthread_mutex_unlock(®.mtx); + + return 0; + fail_ipcp: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_get_ipcp_by_layer(struct ipcp_info * info, + struct layer_info * layer) +{ + struct reg_ipcp * ipcp; + + assert(info != NULL); + assert(layer != NULL); + + pthread_mutex_lock(®.mtx); + + ipcp = __reg_get_ipcp_by_layer(layer->name); + if (ipcp == NULL) { + log_err("No IPCP for %s not found.", layer->name); + goto fail_ipcp; + } + + *info = ipcp->info; + *layer = ipcp->layer; + + pthread_mutex_unlock(®.mtx); + + return 0; + fail_ipcp: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_prepare_flow_alloc(struct flow_info * info) +{ + struct reg_flow * flow; + int ret; + + assert(info != NULL); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + + assert(flow != NULL); + assert(flow->info.state == FLOW_INIT); + + info->state = FLOW_ALLOC_PENDING; + + ret = reg_flow_update(flow, info); + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +int reg_wait_flow_allocated(struct flow_info * info, + buffer_t * pbuf, + const struct timespec * abstime) +{ + struct reg_flow * flow; + int ret = -1; + bool stop = false; + + assert(info != NULL); + assert(info->id >= ID_OFFT); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + + assert(flow != NULL); + assert(info->id == flow->info.id); + assert(info->n_pid == flow->info.n_pid); + + assert(info->state == FLOW_ALLOC_PENDING); + + pthread_cleanup_push(__cleanup_mutex_unlock, ®.mtx); + + while (!stop) { + switch(flow->info.state) { + case FLOW_ALLOC_PENDING: + ret = -__timedwait(®.cond, ®.mtx, abstime); + break; + case FLOW_ALLOCATED: + ret = 0; + stop = true; + break; + case FLOW_DEALLOCATED: + ret = -1; + stop = true; + break; + default: + assert(false); + } + + if (ret == -ETIMEDOUT) { + info->state = FLOW_DEALLOCATED; + reg_flow_update(flow, info); + break; + } + + flow = __reg_get_flow(flow->info.id); + assert(flow != NULL); + } + + reg_flow_get_data(flow, pbuf); + + *info = flow->info; + + pthread_cleanup_pop(true); /* __cleanup_mutex_unlock */ + + return ret; +} + +int reg_respond_alloc(struct flow_info * info, + buffer_t * pbuf) +{ + struct reg_flow * flow; + + assert(info != NULL); + assert(info->state == FLOW_ALLOCATED || + info->state == FLOW_DEALLOCATED); + assert(pbuf != NULL); + assert(!(info->state == FLOW_DEALLOCATED && pbuf->data != NULL)); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + if (flow == NULL) { + log_err("Flow not found for allocation: %d", info->id); + goto fail_flow; + } + + assert(flow->info.state == FLOW_ALLOC_PENDING); + assert(flow->data.len == 0); + assert(flow->data.data == NULL); + + info->n_pid = flow->info.n_pid; + info->n_1_pid = flow->info.n_pid; + + if (reg_flow_update(flow, info) < 0) { + log_err("Failed to create flow structs."); + goto fail_flow; + }; + + if (info->state == FLOW_ALLOCATED) + reg_flow_set_data(flow, pbuf); + + pthread_cond_broadcast(®.cond); + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_flow: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_prepare_flow_accept(struct flow_info * info, + buffer_t * pbuf) +{ + struct reg_flow * flow; + int ret; + + assert(info != NULL); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + + assert(flow != NULL); + assert(info->n_pid != 0); + + info->state = FLOW_ACCEPT_PENDING; + + ret = reg_flow_update(flow, info); + + reg_flow_set_data(flow, pbuf); + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +void __cleanup_wait_accept(void * o) +{ + struct reg_flow * flow; + + flow = (struct reg_flow *) o; + + __reg_del_active_proc(flow->info.n_pid); +} + +int reg_wait_flow_accepted(struct flow_info * info, + buffer_t * pbuf, + const struct timespec * abstime) +{ + struct reg_flow * flow; + int ret = -1; + bool stop = false; + + assert(info != NULL); + assert(info->id >= ID_OFFT); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + + assert(flow != NULL); + assert(info->id == flow->info.id); + assert(info->n_pid == flow->info.n_pid); + assert(info->state == flow->info.state); + assert(flow->info.state == FLOW_ACCEPT_PENDING); + + if (__reg_add_active_proc(info->n_pid) < 0) { + log_err("Failed to mark pid %d active.", info->n_pid); + goto fail; + } + + pthread_cond_broadcast(®.cond); + + pthread_cleanup_push(__cleanup_mutex_unlock, ®.mtx); + pthread_cleanup_push(__cleanup_wait_accept, flow); + + while (!stop) { + switch(flow->info.state) { + case FLOW_ACCEPT_PENDING: + ret = -__timedwait(®.cond, ®.mtx, abstime); + break; + case FLOW_ALLOCATED: + ret = 0; + stop = true; + break; + case FLOW_DEALLOCATED: + ret = -1; + stop = true; + break; + default: + assert(false); + } + + if (ret == -ETIMEDOUT) { + info->state = FLOW_DEALLOCATED; + reg_flow_update(flow, info); + break; + } + + flow = __reg_get_flow(flow->info.id); + } + + pthread_cleanup_pop(true); /* __cleanup_wait_accept */ + + reg_flow_get_data(flow, pbuf); + + *info = flow->info; + + pthread_cleanup_pop(true); /* __cleanup_mutex_unlock */ + + return ret; + fail: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_wait_flow_accepting(enum hash_algo algo, + const uint8_t * hash, + const struct timespec * abstime) +{ + int ret; + + assert(hash != NULL); + assert(abstime != NULL); + + pthread_mutex_lock(®.mtx); + + pthread_cleanup_push(__cleanup_mutex_unlock, ®.mtx); + + while (true) { + ret = __reg_get_pending_flow_id_for_hash(algo, hash); + if (ret != -EAGAIN) + break; + + ret = -__timedwait(®.cond, ®.mtx, abstime); + if (ret == -ETIMEDOUT) + break; + } + + pthread_cleanup_pop(true); + + return ret; +} + +int reg_respond_accept(struct flow_info * info, + buffer_t * pbuf) +{ + struct reg_flow * flow; + buffer_t temp; + + assert(info != NULL); + assert(info->state == FLOW_ALLOCATED); + assert(pbuf != NULL); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + if (flow == NULL) { + log_err("Flow not found for request: %d", info->id); + goto fail_flow; + } + + assert(flow->info.state == FLOW_ACCEPT_PENDING); + + info->n_pid = flow->info.n_pid; + + if (info->qs.cypher_s > 0) { + reg_flow_get_data(flow, &temp); + reg_flow_set_data(flow, pbuf); + *pbuf = temp; + } + + if (reg_flow_update(flow, info) < 0) { + log_err("Failed to create flow structs."); + goto fail_flow; + } + + pthread_cond_broadcast(®.cond); + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_flow: + pthread_mutex_unlock(®.mtx); + return -1; +} + +void reg_dealloc_flow(struct flow_info * info) +{ + struct reg_flow * flow; + + assert(info != NULL); + assert(info->id != 0); + assert(info->n_pid != 0); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + + assert(flow != NULL); + assert(flow->data.data == NULL); + assert(flow->data.len == 0); + + assert(flow->info.state == FLOW_ALLOCATED); + flow->info.state = FLOW_DEALLOC_PENDING; + info->state = FLOW_DEALLOC_PENDING; + info->n_1_pid = flow->info.n_1_pid; + + reg_flow_update(flow, info); + + pthread_mutex_unlock(®.mtx); +} + +void reg_dealloc_flow_resp(struct flow_info * info) +{ + struct reg_flow * flow; + + assert(info != NULL); + assert(info->id != 0); + assert(info->n_1_pid != 0); + + pthread_mutex_lock(®.mtx); + + flow = __reg_get_flow(info->id); + + assert(flow != NULL); + assert(flow->data.data == NULL); + assert(flow->data.len == 0); + + assert(flow->info.state == FLOW_DEALLOC_PENDING); + flow->info.state = FLOW_DEALLOCATED; + info->state = FLOW_DEALLOCATED; + + reg_flow_update(flow, info); + + pthread_mutex_unlock(®.mtx); +} + +int reg_wait_proc(pid_t pid, + const struct timespec * abstime) +{ + struct reg_proc * proc = NULL; + int ret; + + assert(pid > 0); + assert(abstime != NULL); + + pthread_mutex_lock(®.mtx); + + pthread_cleanup_push(__cleanup_mutex_unlock, ®.mtx); + + while (true) { + proc = __reg_get_proc(pid); + if (proc != NULL) { + ret = 0; + break; + } + + ret = -__timedwait(®.cond, ®.mtx, abstime); + if (ret == -ETIMEDOUT) + break; + } + + pthread_cleanup_pop(true); + + return ret; +} + +int reg_wait_ipcp_boot(struct ipcp_info * info, + const struct timespec * abstime) +{ + struct reg_ipcp * ipcp; + int ret; + bool stop = false; + + assert(info->state == IPCP_BOOT); + + pthread_mutex_lock(®.mtx); + + ipcp = __reg_get_ipcp(info->pid); + + /* Potential race with the reg_respond_flow. */ + if (ipcp->info.state == IPCP_INIT) + reg_ipcp_update(ipcp, info); + + pthread_cleanup_push(__cleanup_mutex_unlock, ®.mtx); + + while (!stop) { + if (ipcp == NULL) + break; + + switch(ipcp->info.state) { + case IPCP_NULL: + ret = -1; + stop = true; + break; + case IPCP_OPERATIONAL: + ret = 0; + stop = true; + break; + case IPCP_BOOT: + ret = -__timedwait(®.cond, ®.mtx, abstime); + break; + default: + assert(false); + continue; /* Shut up static analyzer. */ + } + + if (ret == -ETIMEDOUT) + break; + + ipcp = __reg_get_ipcp(info->pid); + } + + if (ipcp != NULL) + *info = ipcp->info; + + pthread_cleanup_pop(true); + + return ipcp == NULL? -EIPCP : ret; +} + +int reg_respond_ipcp(const struct ipcp_info * info) +{ + struct reg_ipcp * ipcp; + + assert(info != NULL); + + pthread_mutex_lock(®.mtx); + + ipcp = __reg_get_ipcp(info->pid); + if (ipcp == NULL) { + log_err("IPCP %d not found for response.", info->pid); + goto fail_ipcp; + } + + assert(strcmp(info->name, ipcp->info.name) == 0); + assert(info->type == ipcp->info.type); + + reg_ipcp_update(ipcp, info); + + pthread_cond_broadcast(®.cond); + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_ipcp: + pthread_mutex_unlock(®.mtx); + return -EIPCP; +} + diff --git a/src/irmd/reg/reg.h b/src/irmd/reg/reg.h new file mode 100644 index 00000000..e6deb8e4 --- /dev/null +++ b/src/irmd/reg/reg.h @@ -0,0 +1,151 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_IRMD_REG_H +#define OUROBOROS_IRMD_REG_H + +#include <ouroboros/flow.h> +#include <ouroboros/ipcp.h> +#include <ouroboros/name.h> +#include <ouroboros/proc.h> +#include <ouroboros/protobuf.h> +#include <ouroboros/time.h> +#include <ouroboros/utils.h> + +int reg_init(void); + +void reg_clear(void); + +void reg_fini(void); + +int reg_create_flow(struct flow_info * info); + +int reg_destroy_flow(int flow_id); + +bool reg_has_flow(int flow_id); + +int reg_create_ipcp(const struct ipcp_info * info); + +int reg_destroy_ipcp(pid_t pid); + +bool reg_has_ipcp(pid_t pid); + +int reg_set_layer_for_ipcp(struct ipcp_info * info, + const struct layer_info * layer); + +int reg_get_ipcp(struct ipcp_info * info, + struct layer_info * layer); + +int reg_get_ipcp_by_layer(struct ipcp_info * info, + struct layer_info * layer); + +/* TODO don't rely on protobuf here */ +int reg_list_ipcps(ipcp_list_msg_t *** msg); + +int reg_create_name(const struct name_info * info); + +int reg_destroy_name(const char * name); + +bool reg_has_name(const char * name); + +/* TODO don't rely on protobuf here */ +int reg_list_names(name_info_msg_t *** names); + +int reg_create_proc(const struct proc_info * info); + +int reg_destroy_proc(pid_t pid); + +bool reg_has_proc(pid_t pid); + +void reg_kill_all_proc(int signal); + +pid_t reg_get_dead_proc(void); + +int reg_create_spawned(pid_t pid); + +int reg_destroy_spawned(pid_t pid); + +bool reg_has_spawned(pid_t pid); + +void reg_kill_all_spawned(int signal); + +int reg_first_spawned(void); + +int reg_bind_proc(const char * name, + pid_t proc); + +int reg_unbind_proc(const char * name, + pid_t proc); + +int reg_create_prog(const struct prog_info * info); + +int reg_destroy_prog(const char * name); + +bool reg_has_prog(const char * name); + +int reg_get_exec(enum hash_algo algo, + const uint8_t * hash, + char *** exec); + +int reg_bind_prog(const char * name, + char ** exec, + uint8_t flags); + +int reg_unbind_prog(const char * name, + const char * prog); + +int reg_prepare_flow_alloc(struct flow_info * info); + +int reg_wait_flow_allocated(struct flow_info * info, + buffer_t * pbuf, + const struct timespec * abstime); + +int reg_respond_alloc(struct flow_info * info, + buffer_t * pbuf); + +int reg_prepare_flow_accept(struct flow_info * info, + buffer_t * pbuf); + +int reg_wait_flow_accepted(struct flow_info * info, + buffer_t * pbuf, + const struct timespec * abstime); + +int reg_wait_flow_accepting(enum hash_algo algo, + const uint8_t * hash, + const struct timespec * abstime); + +int reg_respond_accept(struct flow_info * info, + buffer_t * pbuf); + +void reg_dealloc_flow(struct flow_info * info); + +void reg_dealloc_flow_resp(struct flow_info * info); + +int reg_wait_proc(pid_t pid, + const struct timespec * abstime); + +int reg_wait_ipcp_boot(struct ipcp_info * ipcp, + const struct timespec * abstime); + +int reg_respond_ipcp(const struct ipcp_info * info); + +#endif /* OUROBOROS_IRMD_REG_H */ diff --git a/src/irmd/reg/tests/CMakeLists.txt b/src/irmd/reg/tests/CMakeLists.txt new file mode 100644 index 00000000..bc1354ed --- /dev/null +++ b/src/irmd/reg/tests/CMakeLists.txt @@ -0,0 +1,29 @@ +get_filename_component(tmp ".." ABSOLUTE) +get_filename_component(src_folder "${tmp}" NAME) + +create_test_sourcelist(${src_folder}_tests test_suite.c + # Add new tests here + flow_test.c + ipcp_test.c + name_test.c + proc_test.c + prog_test.c + reg_test.c +) + +add_executable(${src_folder}_test EXCLUDE_FROM_ALL ${${src_folder}_tests}) +target_link_libraries(${src_folder}_test ouroboros-common) + +if (CMAKE_BUILD_TYPE MATCHES "Debug*") + add_compile_flags(${src_folder}_test -DCONFIG_OUROBOROS_DEBUG) +endif () + +add_dependencies(check ${src_folder}_test) + +set(tests_to_run ${${src_folder}_tests}) +remove(tests_to_run test_suite.c) + +foreach(test ${tests_to_run}) + get_filename_component(test_name ${test} NAME_WE) + add_test(irmd/reg/${test_name} ${C_TEST_PATH}/${src_folder}_test ${test_name}) +endforeach(test) diff --git a/src/irmd/reg/tests/flow_test.c b/src/irmd/reg/tests/flow_test.c new file mode 100644 index 00000000..efdf21d2 --- /dev/null +++ b/src/irmd/reg/tests/flow_test.c @@ -0,0 +1,294 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Flows - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include "../flow.c" + +#include <ouroboros/test.h> + +#include <string.h> + +#define TEST_DATA "testpiggybackdata" + +static int test_reg_flow_create(void) +{ + struct reg_flow * f; + + struct flow_info info = { + .id = 1, + .n_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + TEST_START(); + + f = reg_flow_create(&info); + if (f == NULL) { + printf("Failed to create flow.\n"); + goto fail; + } + + reg_flow_destroy(f); + + TEST_SUCCESS(); + + return 0; + fail: + TEST_FAIL(); + return -1; +} + +static int test_reg_flow_create_no_id(void) { + struct flow_info info = { + .id = 0, + .n_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + reg_flow_create(&info); /* assert fail */ + + return 0; +} + +static int test_reg_flow_create_no_pid(void) { + struct flow_info info = { + .id = 1, + .n_pid = 0, + .qs = qos_raw, + .state = FLOW_INIT + }; + + reg_flow_create(&info); /* assert fail */ + + return 0; +} + +static int test_reg_flow_create_has_n_1_pid(void) { + struct flow_info info = { + .id = 1, + .n_pid = 0, + .n_1_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + reg_flow_create(&info); /* assert fail */ + + return 0; +} + +static int test_reg_flow_create_wrong_state(void) { + struct flow_info info = { + .id = 1, + .n_pid = 0, + .n_1_pid = 1, + .qs = qos_raw, + .state = FLOW_ALLOC_PENDING + }; + + reg_flow_create(&info); /* assert fail */ + + return 0; +} + +static int test_reg_flow_create_has_mpl(void) { + struct flow_info info = { + .id = 1, + .n_pid = 1, + .n_1_pid = 0, + .mpl = 10, + .qs = qos_raw, + .state = FLOW_ALLOC_PENDING + }; + + reg_flow_create(&info); /* assert fail */ + + return 0; +} + +static int test_reg_flow_update(void) +{ + struct reg_flow * f; + + struct flow_info info = { + .id = 1, + .n_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + struct flow_info upd = { + .id = 1, + .n_pid = 1, + .qs = qos_data, + .state = FLOW_DEALLOCATED + }; + + TEST_START(); + + f = reg_flow_create(&info); + if (f == NULL) { + printf("Failed to create flow.\n"); + goto fail; + } + + reg_flow_update(f, &upd); + + if (memcmp(&f->info, &upd, sizeof(upd)) != 0) { + printf("Flow info not updated.\n"); + goto fail; + } + + reg_flow_destroy(f); + + TEST_SUCCESS(); + + return 0; + fail: + TEST_FAIL(); + return -1; +} + +static int test_reg_flow_update_wrong_id(void) +{ + struct reg_flow * f; + + struct flow_info info = { + .id = 1, + .n_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + struct flow_info upd = { + .id = 2, + .n_pid = 1, + .qs = qos_data, + .state = FLOW_DEALLOCATED + }; + + TEST_START(); + + f = reg_flow_create(&info); + if (f == NULL) { + printf("Failed to create flow.\n"); + goto fail; + } + + reg_flow_update(f, &upd); /* assert fail */ + + TEST_SUCCESS(); + + return 0; + fail: + TEST_FAIL(); + return -1; +} + +static int test_reg_flow_assert_fails(void) +{ + int ret = 0; + + ret |= test_assert_fail(test_reg_flow_create_no_id); + + ret |= test_assert_fail(test_reg_flow_create_no_pid); + + ret |= test_assert_fail(test_reg_flow_create_has_n_1_pid); + + ret |= test_assert_fail(test_reg_flow_create_wrong_state); + + ret |= test_assert_fail(test_reg_flow_create_has_mpl); + + ret |= test_assert_fail(test_reg_flow_update_wrong_id); + + return ret; +} + +static int test_flow_data(void) +{ + struct reg_flow * f; + + struct flow_info info = { + .id = 1, + .n_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + char * data; + buffer_t buf; + buffer_t rcv = {NULL, 0}; + + TEST_START(); + + data = strdup(TEST_DATA); + if (data == NULL) { + printf("Failed to strdup data.\n"); + goto fail; + } + + buf.data = (uint8_t *) data; + buf.len = strlen(data); + + f = reg_flow_create(&info); + if (f == NULL) { + printf("Failed to create flow.\n"); + goto fail; + } + + reg_flow_set_data(f, &buf); + + reg_flow_get_data(f, &rcv); + + freebuf(buf); + clrbuf(rcv); + + reg_flow_destroy(f); + + TEST_SUCCESS(); + + return 0; + fail: + free(data); + TEST_FAIL(); + return -1; +} + +int flow_test(int argc, + char ** argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + ret |= test_reg_flow_create(); + + ret |= test_reg_flow_update(); + + ret |= test_reg_flow_assert_fails(); + + ret |= test_flow_data(); + + return ret; +}
\ No newline at end of file diff --git a/src/irmd/reg/tests/ipcp_test.c b/src/irmd/reg/tests/ipcp_test.c new file mode 100644 index 00000000..4e0a764b --- /dev/null +++ b/src/irmd/reg/tests/ipcp_test.c @@ -0,0 +1,89 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - IPCPs - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include <ouroboros/test.h> + +#include "../ipcp.c" + +#define TEST_PID 65535 + +static int test_reg_ipcp_create(void) +{ + struct reg_ipcp * ipcp; + struct ipcp_info info = { + .pid = TEST_PID, + .state = IPCP_BOOT + }; + struct layer_info layer = { + .name = "testlayer", + .dir_hash_algo = DIR_HASH_SHA3_224 + }; + + TEST_START(); + + ipcp = reg_ipcp_create(&info); + if (ipcp == NULL) { + printf("Failed to create ipcp.\n"); + goto fail; + } + + if (strcmp(ipcp->layer.name, "Not enrolled.") != 0) { + printf("Layer name was not set.\n"); + goto fail; + } + + ipcp->info.state = IPCP_OPERATIONAL; + + reg_ipcp_set_layer(ipcp, &layer); + + if (strcmp(ipcp->layer.name, layer.name) != 0) { + printf("Layer name was not set.\n"); + goto fail; + } + + if (ipcp->info.state != IPCP_OPERATIONAL) { + printf("IPCP state was not set.\n"); + goto fail; + } + + reg_ipcp_destroy(ipcp); + + TEST_SUCCESS(); + + return 0; + fail: + TEST_FAIL(); + return -1; +} + +int ipcp_test(int argc, + char ** argv) +{ + int res = 0; + + (void) argc; + (void) argv; + + res |= test_reg_ipcp_create(); + + return res; +}
\ No newline at end of file diff --git a/src/irmd/reg/tests/name_test.c b/src/irmd/reg/tests/name_test.c new file mode 100644 index 00000000..e2ceb0fc --- /dev/null +++ b/src/irmd/reg/tests/name_test.c @@ -0,0 +1,283 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Names - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include "../name.c" + +#define TEST_PID 65534 +#define TEST_PROG "/usr/bin/testprog" +#define TEST_NAME "testservicename" + +static int test_reg_name_create(void) +{ + struct reg_name * n; + struct name_info info = { + .name = TEST_NAME, + .pol_lb = LB_RR, + }; + + n = reg_name_create(&info); + if (n == NULL) { + printf("Failed to create name %s.\n", info.name); + goto fail; + } + + reg_name_destroy(n); + + return 0; + fail: + return -1; +} + +static int test_reg_name_add_proc(void) +{ + struct reg_name * n; + struct name_info info = { + .name = TEST_NAME, + .pol_lb = LB_RR, + }; + + n = reg_name_create(&info); + if (n == NULL) { + printf("Failed to create name %s.\n", info.name); + goto fail; + } + + if (reg_name_add_proc(n, TEST_PID) < 0) { + printf("Failed to add proc.\n"); + goto fail; + } + + if (n->n_procs != 1) { + printf("n_procs not updated.\n"); + goto fail; + } + + if (!reg_name_has_proc(n, TEST_PID)) { + printf("Proc not found.\n"); + goto fail; + } + + reg_name_del_proc(n, TEST_PID); + + if (n->n_procs != 0) { + printf("n_procs not updated.\n"); + goto fail; + } + + reg_name_destroy(n); + + return 0; + fail: + return -1; +} + +static int test_reg_name_add_prog(void) +{ + struct reg_name * n; + struct name_info info = { + .name = TEST_NAME, + .pol_lb = LB_RR, + }; + + char * exec[] = { TEST_PROG, "--argswitch", "argvalue", NULL}; + + n = reg_name_create(&info); + if (n == NULL) { + printf("Failed to create name %s.\n", info.name); + goto fail; + } + + if (reg_name_add_prog(n, exec) < 0) { + printf("Failed to add prog.\n"); + goto fail; + } + + if (n->n_progs != 1) { + printf("n_progs not updated.\n"); + goto fail; + } + + if (!reg_name_has_prog(n, TEST_PROG)) { + printf("Prog not found.\n"); + goto fail; + } + + reg_name_del_prog(n, TEST_PROG); + + if (n->n_progs != 0) { + printf("n_progs not updated.\n"); + goto fail; + } + + reg_name_destroy(n); + + return 0; + fail: + return -1; +} + +static int test_reg_name_add_active(enum pol_balance lb) +{ + struct reg_name * n; + pid_t pid; + struct name_info info = { + .name = TEST_NAME, + .pol_lb = lb, + }; + + n = reg_name_create(&info); + if (n == NULL) { + printf("Failed to create name %s.\n", info.name); + goto fail; + } + + if (reg_name_get_active(n) != -1) { + printf("Got active from empty actives.\n"); + goto fail; + } + + if (reg_name_add_proc(n, TEST_PID) < 0) { + printf("Failed to add proc 0.\n"); + goto fail; + } + + if (reg_name_add_proc(n, TEST_PID + 1) < 0) { + printf("Failed to add proc 1.\n"); + goto fail; + } + + if (reg_name_add_proc(n, TEST_PID + 2) < 0) { + printf("Failed to add proc 2.\n"); + goto fail; + } + + if (reg_name_add_active(n, TEST_PID) < 0) { + printf("Failed to add active.\n"); + goto fail; + } + + if (n->n_active != 1) { + printf("n_active not updated.\n"); + goto fail; + } + + if (reg_name_get_active(n) != TEST_PID) { + printf("Failed to get active.\n"); + goto fail; + } + + if (reg_name_get_active(n) != TEST_PID) { + printf("Failed to get active.\n"); + goto fail; + } + + if (reg_name_add_active(n, TEST_PID + 1) < 0) { + printf("Failed to add active 3.\n"); + goto fail; + } + + if (reg_name_add_active(n, TEST_PID + 1) < 0) { + printf("Failed to add active 3.\n"); + goto fail; + } + + + if (reg_name_add_active(n, TEST_PID + 2) < 0) { + printf("Failed to add active 4.\n"); + goto fail; + } + + if (n->n_procs != 3) { + printf("n_procs not updated.\n"); + goto fail; + } + + if (n->n_active != 4) { + printf("n_active not updated.\n"); + goto fail; + } + + pid = info.pol_lb == LB_RR ? TEST_PID : TEST_PID + 2; + + if (reg_name_get_active(n) != pid) { + printf("Got wrong active pid 1.\n"); + goto fail; + } + + reg_name_del_active(n, pid); + + if (reg_name_add_active(n, pid) < 0) { + printf("Failed to add active 4.\n"); + goto fail; + } + + pid = info.pol_lb == LB_RR ? TEST_PID + 1 : TEST_PID + 2; + + if (reg_name_get_active(n) != pid) { + printf("Got wrong active pid 2 %d.\n", pid); + goto fail; + } + + reg_name_del_proc(n, TEST_PID + 2); + + reg_name_del_proc(n, TEST_PID + 1); + + reg_name_del_proc(n, TEST_PID); + + if (n->n_procs != 0) { + printf("n_procs not updated.\n"); + goto fail; + } + + if (n->n_active != 0) { + printf("n_active not updated.\n"); + goto fail; + } + + reg_name_destroy(n); + + return 0; + fail: + return -1; +} + + +int name_test(int argc, + char ** argv) +{ + int res = 0; + + (void) argc; + (void) argv; + + res |= test_reg_name_create(); + + res |= test_reg_name_add_proc(); + + res |= test_reg_name_add_prog(); + + res |= test_reg_name_add_active(LB_RR); + + res |= test_reg_name_add_active(LB_SPILL); + + return res; +}
\ No newline at end of file diff --git a/src/irmd/reg/tests/proc_test.c b/src/irmd/reg/tests/proc_test.c new file mode 100644 index 00000000..5c9dd865 --- /dev/null +++ b/src/irmd/reg/tests/proc_test.c @@ -0,0 +1,107 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Processes - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include "../proc.c" + +#define TEST_PID 65534 +#define TEST_PROG "usr/bin/testprog" + +static int test_reg_proc_create(void) +{ + struct reg_proc * proc; + struct proc_info info = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + proc = reg_proc_create(&info); + if (proc == NULL) { + printf("Failed to create proc.\n"); + goto fail; + } + + reg_proc_destroy(proc); + + return 0; + fail: + return -1; +} + +static int test_reg_proc_add_name(void) +{ + struct reg_proc * proc; + struct proc_info info = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + char * name = "testname"; + + proc = reg_proc_create(&info); + if (proc == NULL) { + printf("Failed to create proc.\n"); + goto fail; + } + + if (reg_proc_add_name(proc, name) < 0) { + printf("Failed to add name."); + goto fail; + } + + if (proc->n_names != 1) { + printf("n_names not updated.\n"); + goto fail; + } + + if (!reg_proc_has_name(proc, name)) { + printf("Name not found.\n"); + goto fail; + } + + reg_proc_del_name(proc, name); + + if (proc->n_names != 0) { + printf("n_names not updated.\n"); + goto fail; + } + + reg_proc_destroy(proc); + + return 0; + fail: + return -1; +} + +int proc_test(int argc, + char ** argv) +{ + int res = 0; + + (void) argc; + (void) argv; + + res |= test_reg_proc_create(); + + res |= test_reg_proc_add_name(); + + return res; +} diff --git a/src/irmd/reg/tests/prog_test.c b/src/irmd/reg/tests/prog_test.c new file mode 100644 index 00000000..2565204b --- /dev/null +++ b/src/irmd/reg/tests/prog_test.c @@ -0,0 +1,105 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Programs - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include "../prog.c" + +#define TEST_PROG "usr/bin/testprog" + + +static int test_reg_prog_create(void) +{ + struct reg_prog * prog; + struct prog_info info = { + .name = TEST_PROG + }; + + prog = reg_prog_create(&info); + if (prog == NULL) { + printf("Failed to create prog.\n"); + goto fail; + } + + reg_prog_destroy(prog); + + return 0; + fail: + return -1; +} + +static int test_reg_prog_add_name(void) +{ + struct reg_prog * prog; + struct prog_info info = { + .name = TEST_PROG + }; + + char * name = "testname"; + + prog = reg_prog_create(&info); + if (prog == NULL) { + printf("Failed to create prog.\n"); + goto fail; + } + + if (reg_prog_add_name(prog, name) < 0) { + printf("Failed to add name."); + goto fail; + } + + if (prog->n_names != 1) { + printf("n_names not updated.\n"); + goto fail; + } + + if (!reg_prog_has_name(prog, name)) { + printf("Name not found.\n"); + goto fail; + } + + reg_prog_del_name(prog, name); + + if (prog->n_names != 0) { + printf("n_names not updated.\n"); + goto fail; + } + + reg_prog_destroy(prog); + + return 0; + fail: + return -1; +} + +int prog_test(int argc, + char ** argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + ret |= test_reg_prog_create(); + + ret |= test_reg_prog_add_name(); + + return ret; +}
\ No newline at end of file diff --git a/src/irmd/reg/tests/reg_test.c b/src/irmd/reg/tests/reg_test.c new file mode 100644 index 00000000..35290caf --- /dev/null +++ b/src/irmd/reg/tests/reg_test.c @@ -0,0 +1,1583 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + + +#include "../reg.c" + +#include <ouroboros/test.h> + +#define TEST_PID 3666 +#define TEST_N_1_PID 3999 +#define TEST_FAKE_ID 9128349 +#define TEST_MPL 5 +#define TEST_PROG "reg_test" /* own binary for binary check */ +#define TEST_IPCP "testipcp" +#define TEST_NAME "testname" +#define TEST_DATA "testpbufdata" +#define TEST_DATA2 "testpbufdata2" +#define TEST_LAYER "testlayer" +#define REG_TEST_FAIL() \ + do { TEST_FAIL(); memset(®, 0, sizeof(reg)); } while(0) + +static int test_reg_init(void) +{ + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_create_flow(void) +{ + struct flow_info info = { + .n_pid = TEST_PID, + .qs = qos_raw, + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to create flow.\n"); + goto fail; + } + + if (info.id == 0) { + printf("Failed to update id.'n"); + goto fail; + } + + if (reg.n_flows != 1) { + printf("n_flows was not updated.\n"); + goto fail; + } + + if (!reg_has_flow(info.id)) { + printf("Failed to find flow.\n"); + goto fail; + } + + if (reg_destroy_flow(info.id) < 0) { + printf("Failed to destroy flow.\n"); + goto fail; + } + + if (reg.n_flows != 0) { + printf("n_flows was not updated.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_allocate_flow_timeout(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_MS(1); + buffer_t pbuf; + buffer_t rbuf = {NULL, 0}; + + struct flow_info info = { + .n_pid = TEST_PID, + .qs = qos_raw + }; + + TEST_START(); + + pbuf.data = (uint8_t *) strdup(TEST_DATA);; + if (pbuf.data == NULL) { + printf("Failed to strdup data.\n"); + goto fail; + } + + pbuf.len = strlen((char *) pbuf.data) + 1; + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + ts_add(&abstime, &timeo, &abstime); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to add flow.\n"); + goto fail; + } + + if (reg_prepare_flow_accept(&info, &pbuf) < 0) { + printf("Failed to prepare flow for accept.\n"); + goto fail; + } + + if (reg_wait_flow_accepted(&info, &rbuf, &abstime) != -ETIMEDOUT) { + printf("Wait allocated did not timeout.\n"); + goto fail; + } + + if (info.state != FLOW_DEALLOCATED) { + printf("Flow did not timeout in deallocated state.\n"); + goto fail; + } + + if (pbuf.data == NULL) { + printf("Flow data was updated on timeout."); + goto fail; + } + + freebuf(pbuf); + reg_destroy_flow(info.id); + + if (reg.n_flows != 0) { + printf("Flow did not destroy.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static void * test_flow_respond_alloc(void * o) +{ + struct flow_info * info = (struct flow_info *) o; + buffer_t pbuf = {NULL, 0}; + + if (info->state == FLOW_ALLOCATED) { + pbuf.data = (uint8_t *) strdup(TEST_DATA2); + if (pbuf.data == NULL) { + printf("Failed to strdup data2.\n"); + goto fail; + } + pbuf.len = strlen((char *) pbuf.data) + 1; + } + + reg_respond_alloc(info, &pbuf); + + return (void *) 0; + fail: + return (void *) -1; +} + +static void * test_flow_respond_accept(void * o) +{ + struct flow_info * info = (struct flow_info *) o; + buffer_t pbuf; + + pbuf.data = (uint8_t *) strdup(TEST_DATA2); + if (pbuf.data == NULL) { + printf("Failed to strdup data2.\n"); + goto fail; + } + pbuf.len = strlen((char *) pbuf.data) + 1; + + reg_respond_accept(info, &pbuf); + + if (info->qs.cypher_s == 0) { + freebuf(pbuf); + } else if (strcmp((char *) pbuf.data, TEST_DATA) != 0) { + printf("Data was not passed correctly.\n"); + goto fail; + } + + return (void *) 0; + fail: + return (void *) -1; +} + +static int test_reg_accept_flow_success(void) +{ + pthread_t thr; + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + buffer_t pbuf = {(uint8_t *) TEST_DATA, strlen(TEST_DATA)}; + buffer_t rbuf = {NULL, 0}; + + struct flow_info info = { + .n_pid = TEST_PID, + .qs = qos_raw + }; + + struct flow_info n_1_info = { + .n_1_pid = TEST_N_1_PID, + .qs = qos_data_crypt, + .state = FLOW_ALLOCATED /* RESPONSE SUCCESS */ + }; + + TEST_START(); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + ts_add(&abstime, &timeo, &abstime); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to add flow.\n"); + goto fail; + } + + if (reg_prepare_flow_accept(&info, &pbuf) < 0) { + printf("Failed to prepare flow for accept.\n"); + goto fail; + } + + n_1_info.id = info.id; + n_1_info.mpl = 1; + + pthread_create(&thr, NULL, test_flow_respond_accept, &n_1_info); + + if (reg_wait_flow_accepted(&info, &rbuf, &abstime) < 0 ) { + printf("Flow allocation failed.\n"); + goto fail; + } + + pthread_join(thr, NULL); + + if (info.state != FLOW_ALLOCATED) { + printf("Flow succeeded but not in allocated state.\n"); + goto fail; + } + + if (rbuf.data == NULL) { + printf("rbuf data not returned.\n"); + goto fail; + } + + if (strcmp((char *) rbuf.data, TEST_DATA2) != 0) { + printf("Data2 was not passed correctly.\n"); + goto fail; + } + + freebuf(rbuf); + + reg_dealloc_flow(&info); + + if (info.state != FLOW_DEALLOC_PENDING) { + printf("Flow dealloc requested but not in pending state.\n"); + goto fail; + } + + reg_dealloc_flow_resp(&info); + + if (info.state != FLOW_DEALLOCATED) { + printf("Flow deallocated but not in deallocated state.\n"); + goto fail; + } + + reg_destroy_flow(n_1_info.id); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_accept_flow_success_no_crypt(void) +{ + pthread_t thr; + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + buffer_t pbuf = {(uint8_t *) TEST_DATA, strlen(TEST_DATA)}; + buffer_t rbuf = {NULL, 0}; + + struct flow_info info = { + .n_pid = TEST_PID, + .qs = qos_raw + }; + + struct flow_info n_1_info = { + .n_1_pid = TEST_N_1_PID, + .qs = qos_data, + .state = FLOW_ALLOCATED /* RESPONSE SUCCESS */ + }; + + TEST_START(); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + ts_add(&abstime, &timeo, &abstime); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to add flow.\n"); + goto fail; + } + + if (reg_prepare_flow_accept(&info, &pbuf) < 0) { + printf("Failed to prepare flow for accept.\n"); + goto fail; + } + + n_1_info.id = info.id; + n_1_info.mpl = 1; + + pthread_create(&thr, NULL, test_flow_respond_accept, &n_1_info); + + if (reg_wait_flow_accepted(&info, &rbuf, &abstime) < 0 ) { + printf("Flow allocation failed.\n"); + goto fail; + } + + pthread_join(thr, NULL); + + if (info.state != FLOW_ALLOCATED) { + printf("Flow succeeded but not in allocated state.\n"); + goto fail; + } + + if (rbuf.data == NULL) { + printf("rbuf data was not returned.\n"); + goto fail; + } + + if (strcmp((char *) rbuf.data, TEST_DATA) != 0) { + printf("Data was updated.\n"); + goto fail; + } + + n_1_info.state = FLOW_DEALLOCATED; + + reg_dealloc_flow(&info); + + if (info.state != FLOW_DEALLOC_PENDING) { + printf("Flow dealloc requested but not in pending state.\n"); + goto fail; + } + + reg_dealloc_flow_resp(&info); + + if (info.state != FLOW_DEALLOCATED) { + printf("Flow deallocated but not in deallocated state.\n"); + goto fail; + } + + reg_destroy_flow(n_1_info.id); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + + +static int test_reg_allocate_flow_fail(void) +{ + buffer_t buf = {NULL, 0}; + pthread_t thr; + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + + struct flow_info info = { + .n_pid = TEST_PID, + .qs = qos_raw + }; + + struct flow_info n_1_info = { + .n_1_pid = TEST_N_1_PID, + .qs = qos_data, + .state = FLOW_DEALLOCATED /* RESPONSE FAIL */ + }; + + TEST_START(); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + ts_add(&abstime, &timeo, &abstime); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to add flow.\n"); + goto fail; + } + + info.n_1_pid = TEST_N_1_PID; + + if (reg_prepare_flow_alloc(&info) < 0) { + printf("Failed to prepare flow for alloc.\n"); + goto fail; + } + + n_1_info.id = info.id; + + pthread_create(&thr, NULL, test_flow_respond_alloc, &n_1_info); + + if (reg_wait_flow_allocated(&info, &buf, &abstime) == 0 ) { + printf("Flow allocation succeeded.\n"); + goto fail; + } + + pthread_join(thr, NULL); + + if (info.state != FLOW_DEALLOCATED) { + printf("Flow failed but not in deallocated state.\n"); + goto fail; + } + + reg_destroy_flow(n_1_info.id); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_flow(void) { + int ret = 0; + + ret |= test_reg_create_flow(); + + ret |= test_reg_allocate_flow_timeout(); + + ret |= test_reg_accept_flow_success(); + + ret |= test_reg_accept_flow_success_no_crypt(); + + ret |= test_reg_allocate_flow_fail(); + + return ret; +} + +static int test_reg_create_ipcp(void) +{ + struct ipcp_info info = { + .name = TEST_IPCP, + .pid = TEST_PID, + .state = IPCP_BOOT /* set by spawn_ipcp */ + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_ipcp(&info) < 0) { + printf("Failed to create ipcp.\n"); + goto fail; + } + + if (reg.n_ipcps != 1) { + printf("n_ipcps was not updated.\n"); + goto fail; + } + + if (!reg_has_ipcp(info.pid)) { + printf("Failed to find ipcp.\n"); + goto fail; + } + + if (reg_destroy_ipcp(info.pid) < 0) { + printf("Failed to destroy ipcp.\n"); + goto fail; + } + + if (reg.n_ipcps != 0) { + printf("n_ipcps was not updated.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_set_layer(void) +{ + struct reg_ipcp * ipcp; + struct ipcp_info info = { + .name = TEST_IPCP, + .pid = TEST_PID, + .state = IPCP_BOOT /* set by spawn_ipcp */ + }; + struct layer_info layer = { + .name = TEST_LAYER, + }; + + struct ipcp_info get_info = { + .pid = TEST_PID + }; + struct layer_info get_layer; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_ipcp(&info) < 0) { + printf("Failed to create ipcp.\n"); + goto fail; + } + + ipcp = __reg_get_ipcp(info.pid); + ipcp->info.state = IPCP_OPERATIONAL; + info.state = IPCP_ENROLLED; + + reg_set_layer_for_ipcp(&info, &layer); + + reg_get_ipcp(&get_info, &get_layer); + + if (memcmp(&get_info, &info, sizeof(ipcp)) != 0) { + printf("Failed to set ipcp info.\n"); + goto fail; + } + + if (memcmp(&get_layer, &layer, sizeof(layer)) != 0) { + printf("Failed to set layer info.\n"); + goto fail; + } + + if (reg_destroy_ipcp(info.pid) < 0) { + printf("Failed to destroy ipcp.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_ipcp(void) +{ + int ret = 0; + + ret |= test_reg_create_ipcp(); + + ret |= test_set_layer(); + + return ret; +} + +static int test_reg_create_name(void) +{ + struct name_info info = { + .name = TEST_NAME, + .pol_lb = LB_RR + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_name(&info) < 0) { + printf("Failed to create name.\n"); + goto fail; + } + + if (reg.n_names != 1) { + printf("n_names was not updated.\n"); + goto fail; + } + + if (!reg_has_name(info.name)) { + printf("Failed to find name.\n"); + goto fail; + } + + if (reg_destroy_name(info.name) < 0) { + printf("Failed to destroy name.\n"); + goto fail; + } + + if (reg.n_names != 0) { + printf("n_names was not updated.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_name(void) +{ + int ret = 0; + + ret |= test_reg_create_name(); + + return ret; +} + +static int test_reg_create_proc(void) +{ + struct proc_info info = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_proc(&info) < 0) { + printf("Failed to create process.\n"); + goto fail; + } + + if (reg.n_procs != 1) { + printf("n_procs was not updated.\n"); + goto fail; + } + + if (!reg_has_proc(info.pid)) { + printf("Failed to find process.\n"); + goto fail; + } + + if (reg_destroy_proc(info.pid) < 0) { + printf("Failed to destroy process.\n"); + goto fail; + } + + if (reg.n_procs != 0) { + printf("n_procs was not updated.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_proc(void) +{ + int ret = 0; + + ret |= test_reg_create_proc(); + + return ret; +} + +static int test_reg_spawned(void) +{ + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_spawned(TEST_PID) < 0) { + printf("Failed to create process.\n"); + goto fail; + } + + if (reg.n_spawned != 1) { + printf("n_spawned was not updated.\n"); + goto fail; + } + + if (!reg_has_spawned(TEST_PID)) { + printf("Failed to find spawned.\n"); + goto fail; + } + + if (reg_destroy_spawned(TEST_PID) < 0) { + printf("Failed to destroy spawned.\n"); + goto fail; + } + + if (reg.n_spawned != 0) { + printf("n_spawned was not updated.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_create_prog(void) +{ + struct prog_info info = { + .name = TEST_PROG + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_prog(&info) < 0) { + printf("Failed to create program.\n"); + goto fail; + } + + if (reg.n_progs != 1) { + printf("n_progs was not updated.\n"); + goto fail; + } + + if (!reg_has_prog(info.name)) { + printf("Failed to find program.\n"); + goto fail; + } + + if (reg_destroy_prog(info.name) < 0) { + printf("Failed to destroy program.\n"); + goto fail; + } + + if (reg.n_progs != 0) { + printf("n_progs was not updated.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_prog(void) +{ + int ret = 0; + + ret |= test_reg_create_prog(); + + return ret; +} + +static int test_bind_proc(void) +{ + struct proc_info pinfo = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + struct name_info ninfo = { + .name = TEST_NAME, + .pol_lb = LB_RR + }; + + TEST_START(); + + if (reg_init()) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_name(&ninfo) < 0) { + printf("Failed to create name.\n"); + goto fail; + } + + if (reg_create_proc(&pinfo) < 0) { + printf("Failed to create proc.\n"); + goto fail; + } + + if (reg_bind_proc(TEST_NAME, TEST_PID) < 0) { + printf("Failed to bind proc.\n"); + goto fail; + } + + if (reg_unbind_proc(TEST_NAME, TEST_PID) < 0) { + printf("Failed to unbind proc.\n"); + goto fail; + } + + reg_destroy_proc(TEST_PID); + + if (reg_name_has_proc( __reg_get_name(TEST_NAME), TEST_PID)) { + printf("Proc still in name after destroy.\n"); + goto fail; + } + + reg_destroy_name(TEST_NAME); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; +fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_bind_prog(void) +{ + struct prog_info pinfo = { + .name = TEST_PROG + }; + + struct name_info ninfo = { + .name = TEST_NAME, + .pol_lb = LB_RR + }; + + char * exec[] = { TEST_PROG, "--argswitch", "argvalue", NULL}; + + TEST_START(); + + if (reg_init()) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_name(&ninfo) < 0) { + printf("Failed to create name.\n"); + goto fail; + } + + if (reg_create_prog(&pinfo) < 0) { + printf("Failed to create prog.\n"); + goto fail; + } + + if (reg_bind_prog(TEST_NAME, exec, BIND_AUTO) < 0) { + printf("Failed to bind prog.\n"); + goto fail; + } + + if (!reg_name_has_prog( __reg_get_name(TEST_NAME), TEST_PROG)) { + printf("Prog not found in name.\n"); + goto fail; + } + + if (!reg_prog_has_name( __reg_get_prog(TEST_PROG), TEST_NAME)) { + printf("Name not found in prog.\n"); + goto fail; + } + + if (reg_unbind_prog(TEST_NAME, TEST_PROG) < 0) { + printf("Failed to unbind prog.\n"); + goto fail; + } + + if (reg_name_has_prog( __reg_get_name(TEST_NAME), TEST_PROG)) { + printf("Prog still in name after unbind.\n"); + goto fail; + } + + if (reg_prog_has_name( __reg_get_prog(TEST_PROG), TEST_NAME)) { + printf("Name still in prog after unbind.\n"); + goto fail; + } + + if (reg_bind_prog(TEST_NAME, exec, 0) < 0) { + printf("Failed to bind prog.\n"); + goto fail; + } + + if (reg_name_has_prog( __reg_get_name(TEST_NAME), TEST_PROG)) { + printf("Non-auto prog found in name.\n"); + goto fail; + } + + if (reg_unbind_prog(TEST_NAME, TEST_PROG) < 0) { + printf("Failed to unbind prog.\n"); + goto fail; + } + + reg_destroy_prog(TEST_PROG); + + reg_destroy_name(TEST_NAME); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; +fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_inherit_prog(void) +{ + struct name_info nameinfo = { + .name = TEST_NAME, + .pol_lb = LB_RR + }; + + struct prog_info proginfo = { + .name = TEST_PROG + }; + + struct proc_info procinfo = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + char * exec[] = { TEST_PROG, NULL}; + + TEST_START(); + + if (reg_init()) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_name(&nameinfo) < 0) { + printf("Failed to create name.\n"); + goto fail; + } + + if (reg_create_prog(&proginfo) < 0) { + printf("Failed to create prog.\n"); + goto fail; + } + + if (reg_bind_prog(TEST_NAME, exec, 0) < 0) { + printf("Failed to bind prog.\n"); + goto fail; + } + + if (reg_create_proc(&procinfo) < 0) { + printf("Failed to create proc.\n"); + goto fail; + } + + if (!reg_name_has_proc(__reg_get_name(TEST_NAME), TEST_PID)) { + printf("Failed to update name from prog.\n"); + goto fail; + } + + if (!reg_proc_has_name(__reg_get_proc(TEST_PID), TEST_NAME)) { + printf("Failed to update proc from prog.\n"); + goto fail; + } + + reg_destroy_proc(TEST_PID); + + reg_destroy_prog(TEST_PROG); + + reg_destroy_name(TEST_NAME); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; +fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_wait_accepting_timeout(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_MS(1); + int flow_id; + uint8_t hash[64]; + struct name_info ninfo = { + .name = TEST_NAME, + .pol_lb = LB_RR + }; + + TEST_START(); + + if (reg_init()) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_name(&ninfo) < 0) { + printf("Failed to create name.\n"); + goto fail; + } + + str_hash(HASH_SHA3_256, hash, ninfo.name); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + flow_id = reg_wait_flow_accepting(HASH_SHA3_256, hash, &abstime); + if (flow_id != -ETIMEDOUT) { + printf("Wait accept did not time out: %d.\n", flow_id); + goto fail; + } + + reg_destroy_name(TEST_NAME); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_wait_accepting_fail_name(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + int flow_id; + uint8_t hash[64]; + + TEST_START(); + + if (reg_init()) { + printf("Failed to init registry.\n"); + goto fail; + } + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + str_hash(HASH_SHA3_256, hash, "C0FF33"); + + flow_id = reg_wait_flow_accepting(HASH_SHA3_256, hash, &abstime); + if (flow_id != -ENAME) { + printf("Wait accept did not fail on name: %d.\n", flow_id); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static void * test_call_flow_accept(void * o) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_MS(1); + buffer_t pbuf = {NULL, 0}; + + struct proc_info pinfo = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + struct flow_info info = { + .n_pid = pinfo.pid, + .qs = qos_raw, + }; + + if (reg_create_proc(&pinfo) < 0) { + printf("Failed to create proc.\n"); + goto fail; + } + + if (reg_bind_proc((char *) o, TEST_PID) < 0) { + printf("Failed to bind proc.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to create flow.\n"); + goto fail; + } + + info.state = FLOW_ACCEPT_PENDING; + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + reg_prepare_flow_accept(&info, &pbuf); + + if (reg_wait_flow_accepted(&info, &pbuf, &abstime) != -ETIMEDOUT) { + printf("Wait allocated did not timeout.\n"); + goto fail; + } + + reg_destroy_flow(info.id); + reg_destroy_proc(pinfo.pid); + + return (void *) 0; + fail: + return (void *) -1; +} + +static int test_wait_accepting_success(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + int flow_id; + pthread_t thr; + uint8_t hash[64]; + struct name_info ninfo = { + .name = TEST_NAME, + .pol_lb = LB_RR + }; + + TEST_START(); + + if (reg_init()) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_name(&ninfo) < 0) { + printf("Failed to create name.\n"); + goto fail; + } + + pthread_create(&thr, NULL, test_call_flow_accept, ninfo.name); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + str_hash(HASH_SHA3_256, hash, ninfo.name); + + flow_id = reg_wait_flow_accepting(HASH_SHA3_256, hash, &abstime); + if (flow_id < 0) { + printf("Wait accept did not return a flow id: %d.", flow_id); + goto fail; + } + + pthread_join(thr, NULL); + + reg_destroy_name(TEST_NAME); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_wait_accepting(void) +{ + int ret = 0; + + ret |= test_wait_accepting_timeout(); + + ret |= test_wait_accepting_fail_name(); + + ret |= test_wait_accepting_success(); + + return ret; +} + +static int test_wait_ipcp_boot_timeout(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_MS(1); + struct ipcp_info info = { + .name = TEST_IPCP, + .pid = TEST_PID, + .state = IPCP_BOOT /* set by spawn_ipcp */ + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_ipcp(&info) < 0) { + printf("Failed to create ipcp.\n"); + goto fail; + } + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + if (reg_wait_ipcp_boot(&info, &abstime) != -ETIMEDOUT) { + printf("Wait boot did not timeout.\n"); + goto fail; + } + + if (reg_destroy_ipcp(info.pid) < 0) { + printf("Failed to destroy ipcp.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static void * test_ipcp_respond(void * o) +{ + (void) o; + + reg_respond_ipcp((struct ipcp_info *) o); + + return (void *) 0; +} + +static int test_wait_ipcp_boot_fail(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + pthread_t thr; + struct ipcp_info info = { + .name = TEST_IPCP, + .pid = TEST_PID, + .state = IPCP_BOOT /* set by spawn_ipcp */ + }; + struct ipcp_info resp_info = { + .name = TEST_IPCP, + .pid = TEST_PID, + .state = IPCP_NULL + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_ipcp(&info) < 0) { + printf("Failed to create ipcp.\n"); + goto fail; + } + + pthread_create(&thr, NULL, test_ipcp_respond, &resp_info); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + info.state = IPCP_BOOT; + + if (reg_wait_ipcp_boot(&info, &abstime) == 0) { + printf("IPCP boot reported success.\n"); + goto fail; + } + + pthread_join(thr, NULL); + + if (reg_destroy_ipcp(info.pid) < 0) { + printf("Failed to destroy ipcp.\n"); + goto fail; + } + + if (reg.n_ipcps != 0) { + printf("n_ipcps was not updated.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_wait_ipcp_boot_success(void) +{ + pthread_t thr; + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + struct ipcp_info info = { + .name = TEST_IPCP, + .pid = TEST_PID, + .state = IPCP_BOOT /* set by spawn_ipcp */ + }; + struct ipcp_info resp_info = { + .name = TEST_IPCP, + .pid = TEST_PID, + .state = IPCP_OPERATIONAL + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_ipcp(&info) < 0) { + printf("Failed to create ipcp.\n"); + goto fail; + } + + pthread_create(&thr, NULL, test_ipcp_respond, &resp_info); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + info.state = IPCP_BOOT; + + if (reg_wait_ipcp_boot(&info, &abstime) < 0) { + printf("IPCP boot failed.\n"); + goto fail; + } + + pthread_join(thr, NULL); + + if (info.state != IPCP_OPERATIONAL) { + printf("IPCP boot succeeded in non-operational state.\n"); + goto fail; + } + + if (reg_destroy_ipcp(info.pid) < 0) { + printf("Failed to destroy ipcp.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_wait_ipcp_boot(void) +{ + int ret = 0; + + ret |= test_wait_ipcp_boot_timeout(); + + ret |= test_wait_ipcp_boot_fail(); + + ret |= test_wait_ipcp_boot_success(); + + return ret; +} + +static int test_wait_proc_timeout(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_MS(1); + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + if (reg_wait_proc(TEST_PID, &abstime) != -ETIMEDOUT) { + printf("Wait proc did not timeout.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static void * test_proc(void * o) +{ + (void) o; + + reg_create_proc((struct proc_info *) o); + + return (void *) 0; +} + +static int test_wait_proc_success(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + pthread_t thr; + struct proc_info info = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + pthread_create(&thr, NULL, test_proc, &info); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + ts_add(&abstime, &timeo, &abstime); + + if (reg_wait_proc(info.pid, &abstime) < 0) { + printf("Waiting for proc failed.\n"); + goto fail; + } + + pthread_join(thr, NULL); + + reg_destroy_proc(info.pid); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_wait_proc(void) +{ + int ret = 0; + + ret |= test_wait_proc_timeout(); + + ret |= test_wait_proc_success(); + + return ret; +} + + +int reg_test(int argc, + char ** argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + ret |= test_reg_init(); + + ret |= test_reg_flow(); + + ret |= test_reg_ipcp(); + + ret |= test_reg_name(); + + ret |= test_reg_proc(); + + ret |= test_reg_prog(); + + ret |= test_reg_spawned(); + + ret |= test_bind_proc(); + + ret |= test_bind_prog(); + + ret |= test_inherit_prog(); + + ret |= test_wait_accepting(); + + ret |= test_wait_ipcp_boot(); + + ret |= test_wait_proc(); + + return ret; +}
\ No newline at end of file diff --git a/src/irmd/tests/CMakeLists.txt b/src/irmd/tests/CMakeLists.txt index e1fd2f21..e005d194 100644 --- a/src/irmd/tests/CMakeLists.txt +++ b/src/irmd/tests/CMakeLists.txt @@ -15,5 +15,5 @@ remove(tests_to_run test_suite.c) foreach(test ${tests_to_run}) get_filename_component(test_name ${test} NAME_WE) - add_test(${test_name} ${C_TEST_PATH}/${src_folder}_test ${test_name}) + add_test(irmd/${test_name} ${C_TEST_PATH}/${src_folder}_test ${test_name}) endforeach(test) diff --git a/src/irmd/utils.c b/src/irmd/utils.c deleted file mode 100644 index 2d7c8a88..00000000 --- a/src/irmd/utils.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2024 - * - * The IPC Resource Manager - Utilities - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#define _POSIX_C_SOURCE 200809L - -#include <stdlib.h> -#include <string.h> - -size_t argvlen(char ** argv) -{ - size_t argc = 0; - - if (argv == NULL) - return 0; - - while (*argv++ != NULL) - argc++; - - return argc; -} - -void argvfree(char ** argv) -{ - char ** argv_dup; - - if (argv == NULL) - return; - - argv_dup = argv; - while (*argv_dup != NULL) - free(*(argv_dup++)); - - free(argv); -} - -char ** argvdup(char ** argv) -{ - int argc = 0; - char ** argv_dup = argv; - int i; - - if (argv == NULL) - return NULL; - - while (*(argv_dup++) != NULL) - argc++; - - argv_dup = malloc((argc + 1) * sizeof(*argv_dup)); - if (argv_dup == NULL) - return NULL; - - for (i = 0; i < argc; ++i) { - argv_dup[i] = strdup(argv[i]); - if (argv_dup[i] == NULL) { - argvfree(argv_dup); - return NULL; - } - } - - argv_dup[argc] = NULL; - return argv_dup; -} diff --git a/src/irmd/utils.h b/src/irmd/utils.h deleted file mode 100644 index 698028ee..00000000 --- a/src/irmd/utils.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Ouroboros - Copyright (C) 2016 - 2024 - * - * Utils of the IPC Resource Manager - * - * Dimitri Staessens <dimitri@ouroboros.rocks> - * Sander Vrijders <sander@ouroboros.rocks> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., http://www.fsf.org/about/contact/. - */ - -#ifndef OUROBOROS_IRMD_UTILS_H -#define OUROBOROS_IRMD_UTILS_H - -#include <ouroboros/list.h> - -#include <sys/types.h> - -struct str_el { - struct list_head next; - char * str; -}; - -struct pid_el { - struct list_head next; - pid_t pid; -}; - -/* functions for copying and destroying arguments list */ -size_t argvlen(char ** argv); - -char ** argvdup(char ** argv); - -void argvfree(char ** argv); - -#endif /* OUROBOROS_IRM_UTILS_H */ |