diff options
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 <[email protected]> * Sander Vrijders <[email protected]> * - * 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 <[email protected]> + * Sander Vrijders <[email protected]> + * + * 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 <[email protected]> + * Sander Vrijders <[email protected]> + * + * 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 <[email protected]> + * Sander Vrijders <[email protected]> + * + * 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 <[email protected]> + * Sander Vrijders <[email protected]> + * + * 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 <[email protected]> + * Sander Vrijders <[email protected]> + * + * 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 <[email protected]> + * Sander Vrijders <[email protected]> + * + * 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 <[email protected]> + * Sander Vrijders <[email protected]> + * + * 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 <[email protected]> + * Sander Vrijders <[email protected]> + * + * 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 <[email protected]> - * Sander Vrijders <[email protected]> - * - * 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 <[email protected]> - * Sander Vrijders <[email protected]> - * - * 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 */ |