diff options
Diffstat (limited to 'src/irmd/reg')
-rw-r--r-- | src/irmd/reg/CMakeLists.txt | 7 | ||||
-rw-r--r-- | src/irmd/reg/flow.c | 208 | ||||
-rw-r--r-- | src/irmd/reg/flow.h | 63 | ||||
-rw-r--r-- | src/irmd/reg/ipcp.c | 92 | ||||
-rw-r--r-- | src/irmd/reg/ipcp.h | 47 | ||||
-rw-r--r-- | src/irmd/reg/name.c | 375 | ||||
-rw-r--r-- | src/irmd/reg/name.h | 78 | ||||
-rw-r--r-- | src/irmd/reg/proc.c | 183 | ||||
-rw-r--r-- | src/irmd/reg/proc.h | 56 | ||||
-rw-r--r-- | src/irmd/reg/prog.c | 174 | ||||
-rw-r--r-- | src/irmd/reg/prog.h | 53 | ||||
-rw-r--r-- | src/irmd/reg/reg.c | 2120 | ||||
-rw-r--r-- | src/irmd/reg/reg.h | 148 | ||||
-rw-r--r-- | src/irmd/reg/tests/CMakeLists.txt | 29 | ||||
-rw-r--r-- | src/irmd/reg/tests/flow_test.c | 294 | ||||
-rw-r--r-- | src/irmd/reg/tests/ipcp_test.c | 89 | ||||
-rw-r--r-- | src/irmd/reg/tests/name_test.c | 283 | ||||
-rw-r--r-- | src/irmd/reg/tests/proc_test.c | 107 | ||||
-rw-r--r-- | src/irmd/reg/tests/prog_test.c | 105 | ||||
-rw-r--r-- | src/irmd/reg/tests/reg_test.c | 1583 |
20 files changed, 6094 insertions, 0 deletions
diff --git a/src/irmd/reg/CMakeLists.txt b/src/irmd/reg/CMakeLists.txt new file mode 100644 index 00000000..ff9d2e99 --- /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) diff --git a/src/irmd/reg/flow.c b/src/irmd/reg/flow.c new file mode 100644 index 00000000..4d091b23 --- /dev/null +++ b/src/irmd/reg/flow.c @@ -0,0 +1,208 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Flows + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200809L + +#define OUROBOROS_PREFIX "reg/flow" + +#include <ouroboros/logs.h> + +#include "flow.h" + +#include <assert.h> +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> + +struct reg_flow * reg_flow_create(const struct flow_info * info) +{ + 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(flow, 0, sizeof(*flow)); + + clock_gettime(PTHREAD_COND_CLOCK, &flow->t0); + list_head_init(&flow->next); + + flow->info = *info; + + return flow; + + fail_malloc: + return NULL; +} + +static void destroy_rbuffs(struct reg_flow * flow) +{ + if (flow->n_rb != NULL) + shm_rbuff_destroy(flow->n_rb); + flow->n_rb = NULL; + + 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 * flow) +{ + assert(flow != NULL); + + switch(flow->info.state) { + case FLOW_ACCEPT_PENDING: + clrbuf(flow->data); + /* FALLTHRU */ + default: + destroy_rbuffs(flow); + break; + } + + assert(flow->n_rb == NULL); + assert(flow->n_1_rb == NULL); + assert(flow->data.data == NULL); + assert(flow->data.len == 0); + + assert(list_is_empty(&flow->next)); + + free(flow); +} + +static int create_rbuffs(struct reg_flow * flow, + struct flow_info * info) +{ + assert(flow != NULL); + assert(info != NULL); + + flow->n_rb = shm_rbuff_create(info->n_pid, info->id); + if (flow->n_rb == NULL) + goto fail_n_rb; + + assert(flow->info.n_1_pid == 0); + assert(flow->n_1_rb == NULL); + + 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; + + return 0; + + fail_n_1_rb: + shm_rbuff_destroy(flow->n_rb); + fail_n_rb: + return -ENOMEM; +} + +int reg_flow_update(struct reg_flow * flow, + struct flow_info * info) +{ + 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; + } + + flow->info.state = info->state; + + *info = flow->info; + + return 0; + fail: + return -ENOMEM; +} + +void reg_flow_set_data(struct reg_flow * flow, + const buffer_t * buf) +{ + assert(flow != NULL); + assert(buf != NULL); + assert(flow->data.data == NULL); + assert(flow->data.len == 0); + + flow->data = *buf; +} + +void reg_flow_get_data(struct reg_flow * flow, + buffer_t * buf) +{ + assert(flow != NULL); + assert(buf != NULL); + + *buf = flow->data; + + clrbuf(flow->data); +} + +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 new file mode 100644 index 00000000..75ada971 --- /dev/null +++ b/src/irmd/reg/flow.h @@ -0,0 +1,63 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Flows + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_IRMD_REG_FLOW_H +#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 <time.h> + +struct reg_flow { + struct list_head next; + + struct flow_info info; + + buffer_t data; + struct timespec t0; + + struct shm_rbuff * n_rb; + struct shm_rbuff * n_1_rb; +}; + +struct reg_flow * reg_flow_create(const struct flow_info * info); + +void reg_flow_destroy(struct reg_flow * flow); + +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_get_data(struct reg_flow * flow, + buffer_t * buf); + +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 new file mode 100644 index 00000000..6580cb5b --- /dev/null +++ b/src/irmd/reg/ipcp.c @@ -0,0 +1,92 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - IPCPs + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200809L + +#define OUROBOROS_PREFIX "reg/ipcp" + +#include <ouroboros/logs.h> +#include <ouroboros/time.h> + +#include "ipcp.h" + +#include <assert.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; + + assert(info != NULL); + assert(info->state == IPCP_BOOT); + + ipcp = malloc(sizeof(*ipcp)); + if (ipcp == NULL) { + log_err("Failed to malloc ipcp."); + goto fail_malloc; + } + + memset(ipcp, 0, sizeof(*ipcp)); + memset(&ipcp->layer, 0, sizeof(ipcp->layer)); + + list_head_init(&ipcp->next); + + ipcp->info = *info; + ipcp->info.state = IPCP_BOOT; + + strcpy(ipcp->layer.name, "Not enrolled."); + + return ipcp; + + fail_malloc: + return NULL; +} + +void reg_ipcp_destroy(struct reg_ipcp * ipcp) +{ + assert(ipcp != NULL); + + assert(list_is_empty(&ipcp->next)); + + free(ipcp); +} + +void reg_ipcp_update(struct reg_ipcp * ipcp, + const struct ipcp_info * info) +{ + assert(ipcp != NULL); + assert(info->state != IPCP_INIT); + + ipcp->info = *info; +} + +void reg_ipcp_set_layer(struct reg_ipcp * ipcp, + const struct layer_info * info) +{ + assert(ipcp != NULL); + assert(ipcp->info.state == IPCP_OPERATIONAL); + + ipcp->layer = *info; +} diff --git a/src/irmd/reg/ipcp.h b/src/irmd/reg/ipcp.h new file mode 100644 index 00000000..375973a7 --- /dev/null +++ b/src/irmd/reg/ipcp.h @@ -0,0 +1,47 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - IPCPs + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_IRMD_REG_IPCP_H +#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; + + struct layer_info layer; +}; + +struct reg_ipcp * reg_ipcp_create(const struct ipcp_info * info); + +void reg_ipcp_destroy(struct reg_ipcp * ipcp); + +void reg_ipcp_update(struct reg_ipcp * ipcp, + const struct ipcp_info * info); + +void reg_ipcp_set_layer(struct reg_ipcp * ipcp, + const struct layer_info * info); + +#endif /* OUROBOROS_IRMD_REG_IPCP_H */ diff --git a/src/irmd/reg/name.c b/src/irmd/reg/name.c new file mode 100644 index 00000000..1ac939a5 --- /dev/null +++ b/src/irmd/reg/name.c @@ -0,0 +1,375 @@ + +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Names + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200809L + +#define OUROBOROS_PREFIX "reg/name" + +#include <ouroboros/logs.h> +#include <ouroboros/utils.h> + +#include "name.h" + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +struct prog_entry { + struct list_head next; + char ** exec; +}; + +struct proc_entry { + struct list_head next; + pid_t pid; +}; + +static void __free_prog_entry(struct prog_entry * entry) +{ + assert(entry != NULL); + assert(entry->exec != NULL); + + argvfree(entry->exec); + free(entry); +} + +struct reg_name * reg_name_create(const struct name_info * info) +{ + struct reg_name * name; + + assert(info != NULL); + + name = malloc(sizeof(*name)); + if (name == NULL) { + log_err("Failed to malloc name."); + goto fail_malloc; + } + + list_head_init(&name->next); + list_head_init(&name->progs); + list_head_init(&name->procs); + list_head_init(&name->active); + + name->info = *info; + name->n_progs = 0; + name->n_procs = 0; + name->n_active = 0; + + return name; + + fail_malloc: + return NULL; +} + +void reg_name_destroy(struct reg_name * name) +{ + assert(name != NULL); + + assert(list_is_empty(&name->next)); + + assert(name->n_progs == 0); + assert(name->n_procs == 0); + assert(name->n_active == 0); + + assert(list_is_empty(&name->progs)); + assert(list_is_empty(&name->procs)); + assert(list_is_empty(&name->active)); + + free(name); +} + +static struct proc_entry * __reg_name_get_active(const struct reg_name * name, + pid_t pid) +{ + struct list_head * p; + + assert(name != NULL); + assert(pid > 0); + + list_for_each(p, &name->active) { + struct proc_entry * entry; + entry = list_entry(p, struct proc_entry, next); + if (entry->pid == pid) + return entry; + } + + return NULL; +} + +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->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--; + } + } +} + +static struct proc_entry * __reg_name_get_proc(const struct reg_name * name, + pid_t pid) +{ + struct list_head * p; + + assert(name != NULL); + assert(pid > 0); + + 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 struct prog_entry * __reg_name_get_prog(const struct reg_name * name, + const char * prog) +{ + struct list_head * p; + + 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 NULL; +} + +int reg_name_add_active(struct reg_name * name, + pid_t pid) +{ + struct proc_entry * entry; + + assert(name != NULL); + assert(pid > 0); + + assert(__reg_name_get_proc(name, pid) != NULL); + + log_dbg("Process %d accepting flows for %s.", pid, name->info.name); + + if (__reg_name_get_active(name, pid) != NULL) + log_dbg("Process calling accept from multiple threads."); + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc active."); + goto fail_malloc; + } + + entry->pid = pid; + + switch (name->info.pol_lb) { + case LB_RR: /* Round robin policy. */ + list_add_tail(&entry->next, &name->active); + break; + case LB_SPILL: /* Keep accepting flows on the current process */ + list_add(&entry->next, &name->active); + break; + default: + goto fail_unreachable; + } + + name->n_active++; + + return 0; + + fail_unreachable: + free(entry); + assert(false); + fail_malloc: + return -1; +} + +void reg_name_del_active(struct reg_name * name, + pid_t pid) +{ + struct proc_entry * entry; + + entry = __reg_name_get_active(name, pid); + if (entry == NULL) + return; + + list_del(&entry->next); + + name->n_active--; + + free(entry); +} + +pid_t reg_name_get_active(struct reg_name * name) +{ + assert(name != NULL); + + if (list_is_empty(&name->active)) + return -1; + + return list_first_entry(&name->active, struct proc_entry, next)->pid; +} + +int reg_name_add_proc(struct reg_name * name, + pid_t pid) +{ + struct proc_entry * entry; + + assert(name != NULL); + assert(pid > 0); + + assert(__reg_name_get_proc(name, pid) == NULL); + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc proc."); + goto fail_malloc; + } + + entry->pid = pid; + + list_add(&entry->next, &name->procs); + + name->n_procs++; + + return 0; + + fail_malloc: + return -1; +} + +void reg_name_del_proc(struct reg_name * name, + pid_t pid) +{ + struct proc_entry * entry; + + assert(name != NULL); + assert(pid > 0); + + entry = __reg_name_get_proc(name, pid); + if (entry == NULL) + return; + + __reg_name_del_all_active(name, pid); + + list_del(&entry->next); + + free(entry); + + name->n_procs--; + + assert(__reg_name_get_proc(name, pid) == NULL); +} + +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) +{ + 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; + } + + list_add(&entry->next, &name->progs); + + log_dbg("Add prog %s to name %s.", exec[0], name->info.name); + + name->n_progs++; + + return 0; + + fail_exec: + free(entry); + fail_malloc: + return -1; +} + +void reg_name_del_prog(struct reg_name * name, + const char * prog) +{ + struct prog_entry * entry; + + assert(name != NULL); + assert(prog != NULL); + + entry = __reg_name_get_prog(name, prog); + if (entry == NULL) + return; + + list_del(&entry->next); + + __free_prog_entry(entry); + + name->n_progs--; + + assert(__reg_name_get_prog(name, prog) == NULL); +} + +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 list_first_entry(&name->progs, struct prog_entry, next)->exec; +} diff --git a/src/irmd/reg/name.h b/src/irmd/reg/name.h new file mode 100644 index 00000000..97ca7f04 --- /dev/null +++ b/src/irmd/reg/name.h @@ -0,0 +1,78 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Names + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_IRMD_REG_NAME_H +#define OUROBOROS_IRMD_REG_NAME_H + +#include <ouroboros/list.h> +#include <ouroboros/name.h> + +#define BIND_AUTO 0x01 + +struct reg_name { + struct list_head next; + + struct name_info info; + + struct list_head progs; /* autostart programs for this name */ + size_t n_progs; /* number of programs */ + + struct list_head procs; /* processes bound to this name */ + size_t n_procs; /* number of processes */ + + struct list_head active; /* processes actively calling accept */ + size_t n_active; /* number of processes accepting */ +}; + +struct reg_name * reg_name_create(const struct name_info * info); + +void reg_name_destroy(struct reg_name * name); + +int reg_name_add_proc(struct reg_name * name, + pid_t proc); + +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); + +bool reg_name_has_prog(const struct reg_name * name, + const char * prog); + +char ** reg_name_get_exec(const struct reg_name * name); + +int reg_name_add_active(struct reg_name * name, + pid_t proc); + +pid_t reg_name_get_active(struct reg_name * name); + +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 new file mode 100644 index 00000000..9bbdf0eb --- /dev/null +++ b/src/irmd/reg/proc.c @@ -0,0 +1,183 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Processes + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * 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 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 procram; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200809L + +#define OUROBOROS_PREFIX "reg/proc" + +#include <ouroboros/logs.h> + +#include "proc.h" + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +struct name_entry { + struct list_head next; + char * name; +}; + +static void __free_name_entry(struct name_entry * entry) +{ + assert(entry != NULL); + assert(entry->name != NULL); + + free(entry->name); + free(entry); +} + +static void __reg_proc_clear_names(struct reg_proc * proc) +{ + struct list_head * p; + struct list_head * h; + + assert(proc != NULL); + + 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--; + } +} + +struct reg_proc * reg_proc_create(const struct proc_info * info) +{ + struct reg_proc * proc; + + assert(info != NULL); + + proc = malloc(sizeof(*proc)); + if (proc == NULL) { + log_err("Failed to malloc proc."); + goto fail_malloc; + } + + 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->info = *info; + proc->n_names = 0; + + return proc; + + fail_set: + free(proc); + fail_malloc: + return NULL; +} + +void reg_proc_destroy(struct reg_proc * proc) +{ + assert(proc != NULL); + + shm_flow_set_destroy(proc->set); + + __reg_proc_clear_names(proc); + + assert(list_is_empty(&proc->next)); + + assert(proc->n_names == 0); + + assert(list_is_empty(&proc->names)); + + free(proc); +} + +static struct name_entry * __reg_proc_get_name(const struct reg_proc * proc, + const char * name) +{ + struct list_head * p; + + 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; + } + + return NULL; +} + +int reg_proc_add_name(struct reg_proc * proc, + const char * name) +{ + struct name_entry * entry; + + assert(__reg_proc_get_name(proc, name) == NULL); + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc name."); + goto fail_malloc; + } + + entry->name = strdup(name); + if (entry == NULL) { + log_err("Failed to strdup name."); + goto fail_name; + } + + list_add(&entry->next, &proc->names); + + proc->n_names++; + + return 0; + + fail_name: + free(entry); + fail_malloc: + return -1; +} + +void reg_proc_del_name(struct reg_proc * proc, + const char * name) +{ + struct name_entry * entry; + + entry = __reg_proc_get_name(proc, name); + if(entry == NULL) + return; + + list_del(&entry->next); + + __free_name_entry(entry); + + proc->n_names--; + + assert(__reg_proc_get_name(proc, name) == NULL); +} + +bool reg_proc_has_name(const struct reg_proc * proc, + const char * name) +{ + return __reg_proc_get_name(proc, name) != NULL; +} diff --git a/src/irmd/reg/proc.h b/src/irmd/reg/proc.h new file mode 100644 index 00000000..99f74fef --- /dev/null +++ b/src/irmd/reg/proc.h @@ -0,0 +1,56 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Processes + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_IRMD_REG_PROC_H +#define OUROBOROS_IRMD_REG_PROC_H + +#include <ouroboros/list.h> +#include <ouroboros/proc.h> +#include <ouroboros/shm_flow_set.h> + +struct reg_proc { + struct list_head next; + + struct proc_info info; + + 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(const struct proc_info * info); + +void reg_proc_destroy(struct reg_proc * proc); + +void reg_proc_clear(struct reg_proc * proc); + +int reg_proc_add_name(struct reg_proc * proc, + const char * name); + +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 new file mode 100644 index 00000000..9b9e7510 --- /dev/null +++ b/src/irmd/reg/prog.c @@ -0,0 +1,174 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Programs + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200809L + +#define OUROBOROS_PREFIX "reg/prog" + +#include <ouroboros/logs.h> +#include <ouroboros/utils.h> + +#include "prog.h" + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +struct name_entry { + struct list_head next; + char * name; +}; + +static void __free_name_entry(struct name_entry * entry) +{ + assert(entry != NULL); + assert(entry->name != NULL); + + free(entry->name); + free(entry); +} + +static void __reg_prog_clear_names(struct reg_prog * prog) +{ + struct list_head * p; + struct list_head * h; + + assert(prog != 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 struct prog_info * info) +{ + struct reg_prog * p; + + assert(info != NULL); + + p = malloc(sizeof(*p)); + if (p == NULL) { + log_err("Failed to malloc prog."); + goto fail_malloc; + } + + list_head_init(&p->next); + list_head_init(&p->names); + + p->info = *info; + p->n_names = 0; + + return p; + + fail_malloc: + return NULL; +} + +void reg_prog_destroy(struct reg_prog * prog) +{ + assert(prog != NULL); + + __reg_prog_clear_names(prog); + + assert(list_is_empty(&prog->next)); + + assert(prog->n_names == 0); + + assert(list_is_empty(&prog->names)); + + 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 name_entry * entry; + + assert(__reg_prog_get_name(prog, name) == NULL); + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc name."); + goto fail_malloc; + } + + entry->name = strdup(name); + if (entry == NULL) { + log_err("Failed to strdup name."); + goto fail_name; + } + + list_add(&entry->next, &prog->names); + + prog->n_names++; + + return 0; + + fail_name: + free(entry); + fail_malloc: + return -1; +} + +void reg_prog_del_name(struct reg_prog * prog, + const char * name) +{ + struct name_entry * entry; + + 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; +} diff --git a/src/irmd/reg/prog.h b/src/irmd/reg/prog.h new file mode 100644 index 00000000..a98fc6a1 --- /dev/null +++ b/src/irmd/reg/prog.h @@ -0,0 +1,53 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Programs + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_IRMD_REG_PROG_H +#define OUROBOROS_IRMD_REG_PROG_H + +#include <ouroboros/list.h> +#include <ouroboros/proc.h> + +#include <stdint.h> + +struct reg_prog { + struct list_head next; + + 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); + +int reg_prog_add_name(struct reg_prog * prog, + const char * name); + +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..d95a4722 --- /dev/null +++ b/src/irmd/reg/reg.c @@ -0,0 +1,2120 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * +The IPC Resource Manager - Registry + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200809L + +#define OUROBOROS_PREFIX "reg" + +#include <ouroboros/bitmap.h> +#include <ouroboros/errno.h> +#include <ouroboros/list.h> +#include <ouroboros/logs.h> +#include <ouroboros/pthread.h> + +#include "reg.h" +#include "flow.h" +#include "ipcp.h" +#include "name.h" +#include "proc.h" +#include "prog.h" + +#include <assert.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> + +#define ID_OFFT 1 /* reserve some flow_ids */ + +struct { + struct bmp * flow_ids; /* flow_ids for flows */ + struct list_head flows; /* flow information */ + size_t n_flows; /* number of flows */ + + struct list_head ipcps; /* list of ipcps in system */ + size_t n_ipcps; /* number of ipcps */ + + struct list_head names; /* registered names known */ + size_t n_names; /* number of names */ + + struct list_head procs; /* processes */ + size_t n_procs; /* number of processes */ + + struct list_head progs; /* programs known */ + size_t n_progs; /* number of programs */ + + struct list_head spawned; /* child processes */ + size_t n_spawned; /* number of child processes */ + + pthread_mutex_t mtx; /* registry lock */ + pthread_cond_t cond; /* condvar for reg changes */ +} reg; + +struct pid_entry { + struct list_head next; + pid_t pid; +}; + +static struct reg_flow * __reg_get_flow(int flow_id) +{ + struct list_head * p; + + assert(flow_id >= ID_OFFT); + + list_for_each(p, ®.flows) { + struct reg_flow * entry; + entry = list_entry(p, struct reg_flow, next); + if (entry->info.id == flow_id) + return entry; + } + + return NULL; +} + +static struct reg_flow * __reg_get_accept_flow(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.flows) { + struct reg_flow * entry; + entry = list_entry(p, struct reg_flow, next); + if (entry->info.state != FLOW_ACCEPT_PENDING) + continue; + if (entry->info.n_pid == pid) + return entry; + } + + return NULL; +} + +static struct list_head * __reg_after_flow(int flow_id) +{ + struct list_head * p; + + assert(flow_id >= ID_OFFT); + + list_for_each(p, ®.flows) { + struct reg_flow * entry; + entry = list_entry(p, struct reg_flow, next); + if (entry->info.id > flow_id) + break; + } + + return p; +} + +static struct reg_ipcp * __reg_get_ipcp(pid_t pid) +{ + struct list_head * p; + + assert(pid > 0); + + list_for_each(p, ®.ipcps) { + struct reg_ipcp * entry; + entry = list_entry(p, struct reg_ipcp, next); + if (entry->info.pid == pid) + return entry; + } + + return NULL; +} + +static struct reg_ipcp * __reg_get_ipcp_by_layer(const char * layer) +{ + struct list_head * p; + + list_for_each(p, ®.ipcps) { + struct reg_ipcp * entry; + entry = list_entry(p, struct reg_ipcp, next); + if (strcmp(entry->layer.name, layer) == 0) + return entry; + } + + return NULL; +} + +static struct list_head * __reg_after_ipcp(pid_t pid) +{ + struct list_head * p; + + assert(pid > 0); + + list_for_each(p, ®.ipcps) { + struct reg_ipcp * entry; + entry = list_entry(p, struct reg_ipcp, next); + if (entry->info.pid > pid) + break; + } + + return p; +} + +static struct reg_name * __reg_get_name(const char * name) +{ + struct list_head * p; + + assert(name != NULL); + + list_for_each(p, ®.names) { + struct reg_name * entry; + entry = list_entry(p, struct reg_name, next); + if (strcmp(entry->info.name, name) == 0) + return entry; + } + + return NULL; +} + +static struct reg_name * __reg_get_name_by_hash(enum hash_algo algo, + const uint8_t * hash) +{ + struct list_head * p; + uint8_t * thash; + size_t len; + + len = hash_len(algo); + + thash = malloc(len); + if (thash == NULL) + return NULL; + + list_for_each(p, ®.names) { + struct reg_name * n = list_entry(p, struct reg_name, next); + str_hash(algo, thash, n->info.name); + if (memcmp(thash, hash, len) == 0) { + free(thash); + return n; + } + } + + free(thash); + + return NULL; +} + +static int __reg_get_pending_flow_id_for_hash(enum hash_algo algo, + const uint8_t * hash) +{ + struct reg_name * entry; + struct reg_flow * flow; + pid_t pid; + + entry =__reg_get_name_by_hash(algo, hash); + if (entry == NULL) + return -ENAME; + + pid = reg_name_get_active(entry); + if (pid < 0) + return -EAGAIN; + + flow = __reg_get_accept_flow(pid); + assert(flow != NULL); + + return flow->info.id; +} + +static struct list_head * __reg_after_name(const char * name) +{ + struct list_head * p; + + assert(name != NULL); + + list_for_each(p, ®.names) { + struct reg_name * entry; + entry = list_entry(p, struct reg_name, next); + if (strcmp(entry->info.name, name) > 0) + break; + } + + return p; +} + +static struct reg_proc * __reg_get_proc(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.procs) { + struct reg_proc * entry; + entry = list_entry(p, struct reg_proc, next); + if (entry->info.pid == pid) + return entry; + } + + return NULL; +} + +static struct list_head * __reg_after_proc(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.procs) { + struct reg_proc * entry; + entry = list_entry(p, struct reg_proc, next); + if (entry->info.pid > pid) + break; + } + + return p; +} + +static void __reg_kill_all_proc(int signal) +{ + struct list_head * p; + + list_for_each(p, ®.procs) { + struct reg_proc * entry; + entry = list_entry(p, struct reg_proc, next); + kill(entry->info.pid, signal); + } +} + +static pid_t __reg_get_dead_proc(void) +{ + struct list_head * p; + + list_for_each(p, ®.procs) { + struct reg_proc * entry; + entry = list_entry(p, struct reg_proc, next); + if (kill(entry->info.pid, 0) < 0) + return entry->info.pid; + } + + return -1; +} + +static void __reg_cancel_flows_for_proc(pid_t pid) +{ + struct list_head * p; + bool changed = false; + + list_for_each(p, ®.flows) { + struct reg_flow * entry; + entry = list_entry(p, struct reg_flow, next); + if (entry->info.n_pid != pid) + continue; + + switch (entry->info.state) { + case FLOW_ALLOC_PENDING: + /* FALLTHRU */ + case FLOW_ACCEPT_PENDING: + entry->info.state = FLOW_DEALLOCATED; + changed = true; + break; + default: + continue; + } + } + + if (changed) + pthread_cond_broadcast(®.cond); +} + +static struct pid_entry * __reg_get_spawned(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.spawned) { + struct pid_entry * entry; + entry = list_entry(p, struct pid_entry, next); + if (entry->pid == pid) + return entry; + } + + return NULL; +} + +static struct list_head * __reg_after_spawned(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.spawned) { + struct pid_entry * entry; + entry = list_entry(p, struct pid_entry, next); + if (entry->pid > pid) + break; + } + + return p; +} + +static void __reg_kill_all_spawned(int signal) +{ + struct list_head * p; + + list_for_each(p, ®.spawned) { + struct pid_entry * entry; + entry = list_entry(p, struct pid_entry, next); + kill(entry->pid, signal); + } +} + +static pid_t __reg_first_spawned(void) +{ + if (list_is_empty(®.spawned)) + return -1; + + return list_first_entry(®.spawned, struct pid_entry, next)->pid; +} + +static struct reg_prog * __reg_get_prog(const char * name) +{ + struct list_head * p; + + list_for_each(p, ®.progs) { + struct reg_prog * entry; + entry = list_entry(p, struct reg_prog, next); + if (strcmp(entry->info.name, name) == 0) + return entry; + } + + return NULL; +} + +static char ** __reg_get_exec(enum hash_algo algo, + const uint8_t * hash) +{ + struct list_head * p; + uint8_t * buf; + + buf = malloc(hash_len(algo)); + if (buf == NULL) { + log_err("Failed to malloc hash buffer."); + return NULL; + } + + list_for_each(p, ®.names) { + struct reg_name * entry; + entry = list_entry(p, struct reg_name, next); + str_hash(algo, buf, entry->info.name); + if (memcmp(buf, hash, hash_len(algo)) == 0) { + free(buf); + return reg_name_get_exec(entry); + } + } + + free(buf); + + return NULL; +} + +static struct list_head * __reg_after_prog(const char * name) +{ + struct list_head * p; + + list_for_each(p, ®.progs) { + struct reg_prog * entry; + entry = list_entry(p, struct reg_prog, next); + if (strcmp(entry->info.name, name) > 0) + break; + } + + return p; +} + +static void __reg_del_name_from_procs(const char * name) +{ + struct list_head * p; + + list_for_each(p, ®.procs) { + struct reg_proc * proc; + proc = list_entry(p, struct reg_proc, next); + reg_proc_del_name(proc, name); + } +} + +static void __reg_del_name_from_progs(const char * name) +{ + struct list_head * p; + + list_for_each(p, ®.progs) { + struct reg_prog * prog; + prog = list_entry(p, struct reg_prog, next); + reg_prog_del_name(prog, name); + } +} + +static void __reg_proc_update_names(struct reg_proc * proc) +{ + struct list_head * p; + struct reg_prog * prog; + + assert(list_is_empty(&proc->names)); + + prog = __reg_get_prog(proc->info.prog); + if (prog == NULL) + return; + + list_for_each(p, ®.names) { + struct reg_name * name; + name = list_entry(p, struct reg_name, next); + assert(!reg_name_has_proc(name, proc->info.pid)); + if (reg_prog_has_name(prog, name->info.name)) { + reg_proc_add_name(proc, name->info.name); + reg_name_add_proc(name, proc->info.pid); + } + } +} + +static void __reg_del_proc_from_names(pid_t pid) +{ + struct list_head * p; + + list_for_each(p, ®.names) { + struct reg_name * name; + name = list_entry(p, struct reg_name, next); + reg_name_del_proc(name, pid); + } +} + +static void __reg_del_prog_from_names(const char * prog) +{ + struct list_head * p; + + list_for_each(p, ®.names) { + struct reg_name * name; + name = list_entry(p, struct reg_name, next); + reg_name_del_prog(name, prog); + } +} + +static int __reg_add_active_proc(pid_t pid) +{ + struct list_head * p; + size_t n_names = 0; + size_t failed = 0; + + assert(pid > 0); + + list_for_each(p, ®.names) { + struct reg_name * name; + name = list_entry(p, struct reg_name, next); + if (reg_name_has_proc(name, pid)) { + if (reg_name_add_active(name, pid) < 0) + failed++; + n_names++; + } + } + + if (n_names > 0 && failed == n_names) + return -1; + + return 0; /* some were marked */ +} + +static void __reg_del_active_proc(pid_t pid) +{ + struct list_head * p; + + assert(pid > 0); + + list_for_each(p, ®.names) { + struct reg_name * name; + name = list_entry(p, struct reg_name, next); + reg_name_del_active(name, pid); + } +} + +int reg_init(void) +{ + pthread_condattr_t cattr; + + if (pthread_mutex_init(®.mtx, NULL) != 0) { + log_err("Failed to initialize mutex."); + goto fail_mtx; + } + + if (pthread_condattr_init(&cattr) != 0) { + log_err("Failed to initialize condattr."); + goto fail_cattr; + } + +#ifndef __APPLE__ + pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); +#endif + if (pthread_cond_init(®.cond, &cattr) != 0) { + log_err("Failed to initialize condvar."); + goto fail_cond; + } + + reg.flow_ids = bmp_create(SYS_MAX_FLOWS -ID_OFFT, ID_OFFT); + if (reg.flow_ids == NULL) { + log_err("Failed to create flow_ids bitmap."); + goto fail_flow_ids; + } + + pthread_condattr_destroy(&cattr); + + list_head_init(®.flows); + list_head_init(®.ipcps); + list_head_init(®.names); + list_head_init(®.procs); + list_head_init(®.progs); + list_head_init(®.spawned); + + return 0; + + fail_flow_ids: + pthread_cond_destroy(®.cond); + fail_cond: + pthread_condattr_destroy(&cattr); + fail_cattr: + pthread_mutex_destroy(®.mtx); + fail_mtx: + return -1; +} + +void reg_clear(void) +{ + struct list_head * p; + struct list_head * h; + + pthread_mutex_lock(®.mtx); + + list_for_each_safe(p, h, ®.spawned) { + struct pid_entry * entry; + entry = list_entry(p, struct pid_entry, next); + list_del(&entry->next); + free(entry); + reg.n_spawned--; + } + + list_for_each_safe(p, h, ®.progs) { + struct reg_prog * entry; + entry = list_entry(p, struct reg_prog, next); + list_del(&entry->next); + __reg_del_prog_from_names(entry->info.path); + reg_prog_destroy(entry); + reg.n_progs--; + } + + list_for_each_safe(p, h, ®.procs) { + struct reg_proc * entry; + entry = list_entry(p, struct reg_proc, next); + list_del(&entry->next); + __reg_del_proc_from_names(entry->info.pid); + reg_proc_destroy(entry); + reg.n_procs--; + } + + list_for_each_safe(p, h, ®.names) { + struct reg_name * entry; + entry = list_entry(p, struct reg_name, next); + list_del(&entry->next); + reg_name_destroy(entry); + reg.n_names--; + } + + list_for_each_safe(p, h, ®.ipcps) { + struct reg_ipcp * entry; + entry = list_entry(p, struct reg_ipcp, next); + list_del(&entry->next); + reg_ipcp_destroy(entry); + reg.n_ipcps--; + } + + list_for_each_safe(p, h, ®.flows) { + struct reg_flow * entry; + entry = list_entry(p, struct reg_flow, next); + list_del(&entry->next); + reg_flow_destroy(entry); + reg.n_flows--; + } + + pthread_mutex_unlock(®.mtx); +} + +void reg_fini(void) +{ + assert(list_is_empty(®.spawned)); + assert(list_is_empty(®.progs)); + assert(list_is_empty(®.procs)); + assert(list_is_empty(®.names)); + assert(list_is_empty(®.ipcps)); + assert(list_is_empty(®.flows)); + + assert(reg.n_spawned == 0); + assert(reg.n_progs == 0); + assert(reg.n_procs == 0); + assert(reg.n_names == 0); + assert(reg.n_ipcps == 0); + assert(reg.n_flows == 0); + + bmp_destroy(reg.flow_ids); + + if (pthread_cond_destroy(®.cond) != 0) + log_warn("Failed to destroy condvar."); + + if (pthread_mutex_destroy(®.mtx) != 0) + log_warn("Failed to destroy mutex."); +} + +int reg_create_flow(struct flow_info * info) +{ + struct reg_flow * f; + + assert(info != NULL); + assert(info->id == 0); + assert(info->n_pid != 0); + assert(info->state == FLOW_INIT); + + pthread_mutex_lock(®.mtx); + + info->id = bmp_allocate(reg.flow_ids); + if (!bmp_is_id_valid(reg.flow_ids, info->id)) { + log_err("Failed to allocate flow id."); + goto fail_id; + } + + f = reg_flow_create(info); + if (f == NULL) { + log_err("Failed to create flow %d.", info->id); + goto fail_flow; + } + + list_add(&f->next, __reg_after_flow(info->id)); + + reg.n_flows++; + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_flow: + bmp_release(reg.flow_ids, info->id); + info->id = 0; + fail_id: + pthread_mutex_unlock(®.mtx); + return -1; +} + +int reg_destroy_flow(int flow_id) +{ + struct reg_flow * f; + + pthread_mutex_lock(®.mtx); + + f = __reg_get_flow(flow_id); + if (f == NULL) { + log_err("Flow %d does not exist.", flow_id); + goto no_flow; + } + + list_del(&f->next); + + reg.n_flows--; + + bmp_release(reg.flow_ids, flow_id); + + pthread_mutex_unlock(®.mtx); + + pthread_cond_broadcast(®.cond); + + reg_flow_destroy(f); + + return 0; + + no_flow: + pthread_mutex_unlock(®.mtx); + return -1; + +} + +bool reg_has_flow(int flow_id) +{ + bool ret; + + pthread_mutex_lock(®.mtx); + + ret = __reg_get_flow(flow_id) != NULL; + + pthread_mutex_unlock(®.mtx); + + return ret; +} + +int reg_create_ipcp(const struct ipcp_info * info) +{ + struct reg_ipcp * ipcp; + struct pid_entry * entry; + + assert(info != NULL); + assert(info->pid != 0); + assert(info->state == IPCP_BOOT); + + pthread_mutex_lock(®.mtx); + + if (__reg_get_ipcp(info->pid) != NULL) { + log_err("IPCP %d already exists.", info->pid); + goto fail_ipcp; + } + + ipcp = reg_ipcp_create(info); + if (ipcp == NULL) { + log_err("Failed to create ipcp %s.", info->name); + goto fail_ipcp; + } + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to create spawn entry.\n"); + goto fail_spawn; + } + + entry->pid = info->pid; + + list_add(&ipcp->next, __reg_after_ipcp(info->pid)); + list_add(&entry->next, __reg_after_spawned(info->pid)); + + reg.n_ipcps++; + reg.n_spawned++; + + pthread_mutex_unlock(®.mtx); + + return 0; + + fail_spawn: + reg_ipcp_destroy(ipcp); + fail_ipcp: + pthread_mutex_unlock(®.mtx); + return -1; + +} + +int reg_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) { + *ipcps = NULL; + 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; + struct pid_entry * spawn; + struct reg_ipcp * ipcp; + + pthread_mutex_lock(®.mtx); + + proc = __reg_get_proc(pid); + if (proc != NULL) { + list_del(&proc->next); + reg.n_procs--; + reg_proc_destroy(proc); + __reg_del_proc_from_names(pid); + __reg_cancel_flows_for_proc(pid); + } + + spawn = __reg_get_spawned(pid); + if (spawn != NULL) { + list_del(&spawn->next); + reg.n_spawned--; + free(spawn); + } + + ipcp = __reg_get_ipcp(pid); + if (ipcp != NULL) { + list_del(&ipcp->next); + reg.n_ipcps--; + reg_ipcp_destroy(ipcp); + } + + pthread_mutex_unlock(®.mtx); + + return 0; +} + +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; +} + +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 = -EPERM; + 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); + } + + flow = __reg_get_flow(flow->info.id); + if (flow == NULL) { + info->state = FLOW_DEALLOCATED; + ret = -1; + break; + } + + if (ret == -ETIMEDOUT) { + info->state = FLOW_DEALLOCATED; + reg_flow_update(flow, info); + break; + } + } + + if (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_warn("Flow %d already destroyed.", info->id); + goto fail_flow; + } + + if (flow->info.state == FLOW_DEALLOCATED) { + log_warn("Flow %d already deallocated.", 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); + } + + flow = __reg_get_flow(flow->info.id); + if (flow == NULL) { + info->state = FLOW_DEALLOCATED; + ret = -1; + break; + } + + if (ret == -ETIMEDOUT) { + info->state = FLOW_DEALLOCATED; + reg_flow_update(flow, info); + break; + } + } + + pthread_cleanup_pop(true); /* __cleanup_wait_accept */ + + if (flow != NULL) { + 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); + + 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. */ + } + + ipcp = __reg_get_ipcp(info->pid); + + if (ret == -ETIMEDOUT) + break; + } + + 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..17dfcc32 --- /dev/null +++ b/src/irmd/reg/reg.h @@ -0,0 +1,148 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_IRMD_REG_H +#define OUROBOROS_IRMD_REG_H + +#include <ouroboros/flow.h> +#include <ouroboros/ipcp.h> +#include <ouroboros/name.h> +#include <ouroboros/proc.h> +#include <ouroboros/protobuf.h> +#include <ouroboros/time.h> +#include <ouroboros/utils.h> + +int reg_init(void); + +void reg_clear(void); + +void reg_fini(void); + +int reg_create_flow(struct flow_info * info); + +int reg_destroy_flow(int flow_id); + +bool reg_has_flow(int flow_id); + +int reg_create_proc(const struct proc_info * info); + +/* Use this for all processes, including ipcps */ +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); + +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_ipcp(const struct ipcp_info * info); + +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_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..f9d23fd1 --- /dev/null +++ b/src/irmd/reg/tests/flow_test.c @@ -0,0 +1,294 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Flows - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include "../flow.c" + +#include <ouroboros/test.h> + +#include <string.h> + +#define TEST_DATA "testpiggybackdata" + +static int test_reg_flow_create(void) +{ + struct reg_flow * f; + + struct flow_info info = { + .id = 1, + .n_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + TEST_START(); + + f = reg_flow_create(&info); + if (f == NULL) { + printf("Failed to create flow.\n"); + goto fail; + } + + reg_flow_destroy(f); + + TEST_SUCCESS(); + + return 0; + fail: + TEST_FAIL(); + return -1; +} + +static int test_reg_flow_create_no_id(void) { + struct flow_info info = { + .id = 0, + .n_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + reg_flow_create(&info); /* assert fail */ + + return 0; +} + +static int test_reg_flow_create_no_pid(void) { + struct flow_info info = { + .id = 1, + .n_pid = 0, + .qs = qos_raw, + .state = FLOW_INIT + }; + + reg_flow_create(&info); /* assert fail */ + + return 0; +} + +static int test_reg_flow_create_has_n_1_pid(void) { + struct flow_info info = { + .id = 1, + .n_pid = 0, + .n_1_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + reg_flow_create(&info); /* assert fail */ + + return 0; +} + +static int test_reg_flow_create_wrong_state(void) { + struct flow_info info = { + .id = 1, + .n_pid = 0, + .n_1_pid = 1, + .qs = qos_raw, + .state = FLOW_ALLOC_PENDING + }; + + reg_flow_create(&info); /* assert fail */ + + return 0; +} + +static int test_reg_flow_create_has_mpl(void) { + struct flow_info info = { + .id = 1, + .n_pid = 1, + .n_1_pid = 0, + .mpl = 10, + .qs = qos_raw, + .state = FLOW_ALLOC_PENDING + }; + + reg_flow_create(&info); /* assert fail */ + + return 0; +} + +static int test_reg_flow_update(void) +{ + struct reg_flow * f; + + struct flow_info info = { + .id = 1, + .n_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + struct flow_info upd = { + .id = 1, + .n_pid = 1, + .qs = qos_data, + .state = FLOW_DEALLOCATED + }; + + TEST_START(); + + f = reg_flow_create(&info); + if (f == NULL) { + printf("Failed to create flow.\n"); + goto fail; + } + + reg_flow_update(f, &upd); + + if (memcmp(&f->info, &upd, sizeof(upd)) != 0) { + printf("Flow info not updated.\n"); + goto fail; + } + + reg_flow_destroy(f); + + TEST_SUCCESS(); + + return 0; + fail: + TEST_FAIL(); + return -1; +} + +static int test_reg_flow_update_wrong_id(void) +{ + struct reg_flow * f; + + struct flow_info info = { + .id = 1, + .n_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + struct flow_info upd = { + .id = 2, + .n_pid = 1, + .qs = qos_data, + .state = FLOW_DEALLOCATED + }; + + TEST_START(); + + f = reg_flow_create(&info); + if (f == NULL) { + printf("Failed to create flow.\n"); + goto fail; + } + + reg_flow_update(f, &upd); /* assert fail */ + + TEST_SUCCESS(); + + return 0; + fail: + TEST_FAIL(); + return -1; +} + +static int test_reg_flow_assert_fails(void) +{ + int ret = 0; + + ret |= test_assert_fail(test_reg_flow_create_no_id); + + ret |= test_assert_fail(test_reg_flow_create_no_pid); + + ret |= test_assert_fail(test_reg_flow_create_has_n_1_pid); + + ret |= test_assert_fail(test_reg_flow_create_wrong_state); + + ret |= test_assert_fail(test_reg_flow_create_has_mpl); + + ret |= test_assert_fail(test_reg_flow_update_wrong_id); + + return ret; +} + +static int test_flow_data(void) +{ + struct reg_flow * f; + + struct flow_info info = { + .id = 1, + .n_pid = 1, + .qs = qos_raw, + .state = FLOW_INIT + }; + + char * data; + buffer_t buf; + buffer_t rcv = {NULL, 0}; + + TEST_START(); + + data = strdup(TEST_DATA); + if (data == NULL) { + printf("Failed to strdup data.\n"); + goto fail; + } + + buf.data = (uint8_t *) data; + buf.len = strlen(data); + + f = reg_flow_create(&info); + if (f == NULL) { + printf("Failed to create flow.\n"); + goto fail; + } + + reg_flow_set_data(f, &buf); + + reg_flow_get_data(f, &rcv); + + freebuf(buf); + clrbuf(rcv); + + reg_flow_destroy(f); + + TEST_SUCCESS(); + + return 0; + fail: + free(data); + TEST_FAIL(); + return -1; +} + +int flow_test(int argc, + char ** argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + ret |= test_reg_flow_create(); + + ret |= test_reg_flow_update(); + + ret |= test_reg_flow_assert_fails(); + + ret |= test_flow_data(); + + return ret; +} diff --git a/src/irmd/reg/tests/ipcp_test.c b/src/irmd/reg/tests/ipcp_test.c new file mode 100644 index 00000000..fb8ba71b --- /dev/null +++ b/src/irmd/reg/tests/ipcp_test.c @@ -0,0 +1,89 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - IPCPs - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include <ouroboros/test.h> + +#include "../ipcp.c" + +#define TEST_PID 65535 + +static int test_reg_ipcp_create(void) +{ + struct reg_ipcp * ipcp; + struct ipcp_info info = { + .pid = TEST_PID, + .state = IPCP_BOOT + }; + struct layer_info layer = { + .name = "testlayer", + .dir_hash_algo = DIR_HASH_SHA3_224 + }; + + TEST_START(); + + ipcp = reg_ipcp_create(&info); + if (ipcp == NULL) { + printf("Failed to create ipcp.\n"); + goto fail; + } + + if (strcmp(ipcp->layer.name, "Not enrolled.") != 0) { + printf("Layer name was not set.\n"); + goto fail; + } + + ipcp->info.state = IPCP_OPERATIONAL; + + reg_ipcp_set_layer(ipcp, &layer); + + if (strcmp(ipcp->layer.name, layer.name) != 0) { + printf("Layer name was not set.\n"); + goto fail; + } + + if (ipcp->info.state != IPCP_OPERATIONAL) { + printf("IPCP state was not set.\n"); + goto fail; + } + + reg_ipcp_destroy(ipcp); + + TEST_SUCCESS(); + + return 0; + fail: + TEST_FAIL(); + return -1; +} + +int ipcp_test(int argc, + char ** argv) +{ + int res = 0; + + (void) argc; + (void) argv; + + res |= test_reg_ipcp_create(); + + return res; +} diff --git a/src/irmd/reg/tests/name_test.c b/src/irmd/reg/tests/name_test.c new file mode 100644 index 00000000..48f132e9 --- /dev/null +++ b/src/irmd/reg/tests/name_test.c @@ -0,0 +1,283 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Names - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include "../name.c" + +#define TEST_PID 65534 +#define TEST_PROG "/usr/bin/testprog" +#define TEST_NAME "testservicename" + +static int test_reg_name_create(void) +{ + struct reg_name * n; + struct name_info info = { + .name = TEST_NAME, + .pol_lb = LB_RR, + }; + + n = reg_name_create(&info); + if (n == NULL) { + printf("Failed to create name %s.\n", info.name); + goto fail; + } + + reg_name_destroy(n); + + return 0; + fail: + return -1; +} + +static int test_reg_name_add_proc(void) +{ + struct reg_name * n; + struct name_info info = { + .name = TEST_NAME, + .pol_lb = LB_RR, + }; + + n = reg_name_create(&info); + if (n == NULL) { + printf("Failed to create name %s.\n", info.name); + goto fail; + } + + if (reg_name_add_proc(n, TEST_PID) < 0) { + printf("Failed to add proc.\n"); + goto fail; + } + + if (n->n_procs != 1) { + printf("n_procs not updated.\n"); + goto fail; + } + + if (!reg_name_has_proc(n, TEST_PID)) { + printf("Proc not found.\n"); + goto fail; + } + + reg_name_del_proc(n, TEST_PID); + + if (n->n_procs != 0) { + printf("n_procs not updated.\n"); + goto fail; + } + + reg_name_destroy(n); + + return 0; + fail: + return -1; +} + +static int test_reg_name_add_prog(void) +{ + struct reg_name * n; + struct name_info info = { + .name = TEST_NAME, + .pol_lb = LB_RR, + }; + + char * exec[] = { TEST_PROG, "--argswitch", "argvalue", NULL}; + + n = reg_name_create(&info); + if (n == NULL) { + printf("Failed to create name %s.\n", info.name); + goto fail; + } + + if (reg_name_add_prog(n, exec) < 0) { + printf("Failed to add prog.\n"); + goto fail; + } + + if (n->n_progs != 1) { + printf("n_progs not updated.\n"); + goto fail; + } + + if (!reg_name_has_prog(n, TEST_PROG)) { + printf("Prog not found.\n"); + goto fail; + } + + reg_name_del_prog(n, TEST_PROG); + + if (n->n_progs != 0) { + printf("n_progs not updated.\n"); + goto fail; + } + + reg_name_destroy(n); + + return 0; + fail: + return -1; +} + +static int test_reg_name_add_active(enum pol_balance lb) +{ + struct reg_name * n; + pid_t pid; + struct name_info info = { + .name = TEST_NAME, + .pol_lb = lb, + }; + + n = reg_name_create(&info); + if (n == NULL) { + printf("Failed to create name %s.\n", info.name); + goto fail; + } + + if (reg_name_get_active(n) != -1) { + printf("Got active from empty actives.\n"); + goto fail; + } + + if (reg_name_add_proc(n, TEST_PID) < 0) { + printf("Failed to add proc 0.\n"); + goto fail; + } + + if (reg_name_add_proc(n, TEST_PID + 1) < 0) { + printf("Failed to add proc 1.\n"); + goto fail; + } + + if (reg_name_add_proc(n, TEST_PID + 2) < 0) { + printf("Failed to add proc 2.\n"); + goto fail; + } + + if (reg_name_add_active(n, TEST_PID) < 0) { + printf("Failed to add active.\n"); + goto fail; + } + + if (n->n_active != 1) { + printf("n_active not updated.\n"); + goto fail; + } + + if (reg_name_get_active(n) != TEST_PID) { + printf("Failed to get active.\n"); + goto fail; + } + + if (reg_name_get_active(n) != TEST_PID) { + printf("Failed to get active.\n"); + goto fail; + } + + if (reg_name_add_active(n, TEST_PID + 1) < 0) { + printf("Failed to add active 3.\n"); + goto fail; + } + + if (reg_name_add_active(n, TEST_PID + 1) < 0) { + printf("Failed to add active 3.\n"); + goto fail; + } + + + if (reg_name_add_active(n, TEST_PID + 2) < 0) { + printf("Failed to add active 4.\n"); + goto fail; + } + + if (n->n_procs != 3) { + printf("n_procs not updated.\n"); + goto fail; + } + + if (n->n_active != 4) { + printf("n_active not updated.\n"); + goto fail; + } + + pid = info.pol_lb == LB_RR ? TEST_PID : TEST_PID + 2; + + if (reg_name_get_active(n) != pid) { + printf("Got wrong active pid 1.\n"); + goto fail; + } + + reg_name_del_active(n, pid); + + if (reg_name_add_active(n, pid) < 0) { + printf("Failed to add active 4.\n"); + goto fail; + } + + pid = info.pol_lb == LB_RR ? TEST_PID + 1 : TEST_PID + 2; + + if (reg_name_get_active(n) != pid) { + printf("Got wrong active pid 2 %d.\n", pid); + goto fail; + } + + reg_name_del_proc(n, TEST_PID + 2); + + reg_name_del_proc(n, TEST_PID + 1); + + reg_name_del_proc(n, TEST_PID); + + if (n->n_procs != 0) { + printf("n_procs not updated.\n"); + goto fail; + } + + if (n->n_active != 0) { + printf("n_active not updated.\n"); + goto fail; + } + + reg_name_destroy(n); + + return 0; + fail: + return -1; +} + + +int name_test(int argc, + char ** argv) +{ + int res = 0; + + (void) argc; + (void) argv; + + res |= test_reg_name_create(); + + res |= test_reg_name_add_proc(); + + res |= test_reg_name_add_prog(); + + res |= test_reg_name_add_active(LB_RR); + + res |= test_reg_name_add_active(LB_SPILL); + + return res; +} diff --git a/src/irmd/reg/tests/proc_test.c b/src/irmd/reg/tests/proc_test.c new file mode 100644 index 00000000..5c9dd865 --- /dev/null +++ b/src/irmd/reg/tests/proc_test.c @@ -0,0 +1,107 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Processes - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include "../proc.c" + +#define TEST_PID 65534 +#define TEST_PROG "usr/bin/testprog" + +static int test_reg_proc_create(void) +{ + struct reg_proc * proc; + struct proc_info info = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + proc = reg_proc_create(&info); + if (proc == NULL) { + printf("Failed to create proc.\n"); + goto fail; + } + + reg_proc_destroy(proc); + + return 0; + fail: + return -1; +} + +static int test_reg_proc_add_name(void) +{ + struct reg_proc * proc; + struct proc_info info = { + .pid = TEST_PID, + .prog = TEST_PROG + }; + + char * name = "testname"; + + proc = reg_proc_create(&info); + if (proc == NULL) { + printf("Failed to create proc.\n"); + goto fail; + } + + if (reg_proc_add_name(proc, name) < 0) { + printf("Failed to add name."); + goto fail; + } + + if (proc->n_names != 1) { + printf("n_names not updated.\n"); + goto fail; + } + + if (!reg_proc_has_name(proc, name)) { + printf("Name not found.\n"); + goto fail; + } + + reg_proc_del_name(proc, name); + + if (proc->n_names != 0) { + printf("n_names not updated.\n"); + goto fail; + } + + reg_proc_destroy(proc); + + return 0; + fail: + return -1; +} + +int proc_test(int argc, + char ** argv) +{ + int res = 0; + + (void) argc; + (void) argv; + + res |= test_reg_proc_create(); + + res |= test_reg_proc_add_name(); + + return res; +} diff --git a/src/irmd/reg/tests/prog_test.c b/src/irmd/reg/tests/prog_test.c new file mode 100644 index 00000000..5e6931d8 --- /dev/null +++ b/src/irmd/reg/tests/prog_test.c @@ -0,0 +1,105 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Programs - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include "../prog.c" + +#define TEST_PROG "usr/bin/testprog" + + +static int test_reg_prog_create(void) +{ + struct reg_prog * prog; + struct prog_info info = { + .name = TEST_PROG + }; + + prog = reg_prog_create(&info); + if (prog == NULL) { + printf("Failed to create prog.\n"); + goto fail; + } + + reg_prog_destroy(prog); + + return 0; + fail: + return -1; +} + +static int test_reg_prog_add_name(void) +{ + struct reg_prog * prog; + struct prog_info info = { + .name = TEST_PROG + }; + + char * name = "testname"; + + prog = reg_prog_create(&info); + if (prog == NULL) { + printf("Failed to create prog.\n"); + goto fail; + } + + if (reg_prog_add_name(prog, name) < 0) { + printf("Failed to add name."); + goto fail; + } + + if (prog->n_names != 1) { + printf("n_names not updated.\n"); + goto fail; + } + + if (!reg_prog_has_name(prog, name)) { + printf("Name not found.\n"); + goto fail; + } + + reg_prog_del_name(prog, name); + + if (prog->n_names != 0) { + printf("n_names not updated.\n"); + goto fail; + } + + reg_prog_destroy(prog); + + return 0; + fail: + return -1; +} + +int prog_test(int argc, + char ** argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + ret |= test_reg_prog_create(); + + ret |= test_reg_prog_add_name(); + + return ret; +} diff --git a/src/irmd/reg/tests/reg_test.c b/src/irmd/reg/tests/reg_test.c new file mode 100644 index 00000000..c341c297 --- /dev/null +++ b/src/irmd/reg/tests/reg_test.c @@ -0,0 +1,1583 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * The IPC Resource Manager - Registry - Unit Tests + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + + +#include "../reg.c" + +#include <ouroboros/test.h> + +#define TEST_PID 3666 +#define TEST_N_1_PID 3999 +#define TEST_FAKE_ID 9128349 +#define TEST_MPL 5 +#define TEST_PROG "reg_test" /* own binary for binary check */ +#define TEST_IPCP "testipcp" +#define TEST_NAME "testname" +#define TEST_DATA "testpbufdata" +#define TEST_DATA2 "testpbufdata2" +#define TEST_LAYER "testlayer" +#define REG_TEST_FAIL() \ + do { TEST_FAIL(); memset(®, 0, sizeof(reg)); } while(0) + +static int test_reg_init(void) +{ + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_create_flow(void) +{ + struct flow_info info = { + .n_pid = TEST_PID, + .qs = qos_raw, + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to create flow.\n"); + goto fail; + } + + if (info.id == 0) { + printf("Failed to update id.'n"); + goto fail; + } + + if (reg.n_flows != 1) { + printf("n_flows was not updated.\n"); + goto fail; + } + + if (!reg_has_flow(info.id)) { + printf("Failed to find flow.\n"); + goto fail; + } + + if (reg_destroy_flow(info.id) < 0) { + printf("Failed to destroy flow.\n"); + goto fail; + } + + if (reg.n_flows != 0) { + printf("n_flows was not updated.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_allocate_flow_timeout(void) +{ + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_MS(1); + buffer_t pbuf; + buffer_t rbuf = {NULL, 0}; + + struct flow_info info = { + .n_pid = TEST_PID, + .qs = qos_raw + }; + + TEST_START(); + + pbuf.data = (uint8_t *) strdup(TEST_DATA);; + if (pbuf.data == NULL) { + printf("Failed to strdup data.\n"); + goto fail; + } + + pbuf.len = strlen((char *) pbuf.data) + 1; + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + ts_add(&abstime, &timeo, &abstime); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to add flow.\n"); + goto fail; + } + + if (reg_prepare_flow_accept(&info, &pbuf) < 0) { + printf("Failed to prepare flow for accept.\n"); + goto fail; + } + + if (reg_wait_flow_accepted(&info, &rbuf, &abstime) != -ETIMEDOUT) { + printf("Wait allocated did not timeout.\n"); + goto fail; + } + + if (info.state != FLOW_DEALLOCATED) { + printf("Flow did not timeout in deallocated state.\n"); + goto fail; + } + + if (pbuf.data == NULL) { + printf("Flow data was updated on timeout."); + goto fail; + } + + freebuf(pbuf); + reg_destroy_flow(info.id); + + if (reg.n_flows != 0) { + printf("Flow did not destroy.\n"); + goto fail; + } + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static void * test_flow_respond_alloc(void * o) +{ + struct flow_info * info = (struct flow_info *) o; + buffer_t pbuf = {NULL, 0}; + + if (info->state == FLOW_ALLOCATED) { + pbuf.data = (uint8_t *) strdup(TEST_DATA2); + if (pbuf.data == NULL) { + printf("Failed to strdup data2.\n"); + goto fail; + } + pbuf.len = strlen((char *) pbuf.data) + 1; + } + + reg_respond_alloc(info, &pbuf); + + return (void *) 0; + fail: + return (void *) -1; +} + +static void * test_flow_respond_accept(void * o) +{ + struct flow_info * info = (struct flow_info *) o; + buffer_t pbuf; + + pbuf.data = (uint8_t *) strdup(TEST_DATA2); + if (pbuf.data == NULL) { + printf("Failed to strdup data2.\n"); + goto fail; + } + pbuf.len = strlen((char *) pbuf.data) + 1; + + reg_respond_accept(info, &pbuf); + + if (info->qs.cypher_s == 0) { + freebuf(pbuf); + } else if (strcmp((char *) pbuf.data, TEST_DATA) != 0) { + printf("Data was not passed correctly.\n"); + goto fail; + } + + return (void *) 0; + fail: + return (void *) -1; +} + +static int test_reg_accept_flow_success(void) +{ + pthread_t thr; + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + buffer_t pbuf = {(uint8_t *) TEST_DATA, strlen(TEST_DATA)}; + buffer_t rbuf = {NULL, 0}; + + struct flow_info info = { + .n_pid = TEST_PID, + .qs = qos_raw + }; + + struct flow_info n_1_info = { + .n_1_pid = TEST_N_1_PID, + .qs = qos_data_crypt, + .state = FLOW_ALLOCATED /* RESPONSE SUCCESS */ + }; + + TEST_START(); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + ts_add(&abstime, &timeo, &abstime); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to add flow.\n"); + goto fail; + } + + if (reg_prepare_flow_accept(&info, &pbuf) < 0) { + printf("Failed to prepare flow for accept.\n"); + goto fail; + } + + n_1_info.id = info.id; + n_1_info.mpl = 1; + + pthread_create(&thr, NULL, test_flow_respond_accept, &n_1_info); + + if (reg_wait_flow_accepted(&info, &rbuf, &abstime) < 0 ) { + printf("Flow allocation failed.\n"); + goto fail; + } + + pthread_join(thr, NULL); + + if (info.state != FLOW_ALLOCATED) { + printf("Flow succeeded but not in allocated state.\n"); + goto fail; + } + + if (rbuf.data == NULL) { + printf("rbuf data not returned.\n"); + goto fail; + } + + if (strcmp((char *) rbuf.data, TEST_DATA2) != 0) { + printf("Data2 was not passed correctly.\n"); + goto fail; + } + + freebuf(rbuf); + + reg_dealloc_flow(&info); + + if (info.state != FLOW_DEALLOC_PENDING) { + printf("Flow dealloc requested but not in pending state.\n"); + goto fail; + } + + reg_dealloc_flow_resp(&info); + + if (info.state != FLOW_DEALLOCATED) { + printf("Flow deallocated but not in deallocated state.\n"); + goto fail; + } + + reg_destroy_flow(n_1_info.id); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_accept_flow_success_no_crypt(void) +{ + pthread_t thr; + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + buffer_t pbuf = {(uint8_t *) TEST_DATA, strlen(TEST_DATA)}; + buffer_t rbuf = {NULL, 0}; + + struct flow_info info = { + .n_pid = TEST_PID, + .qs = qos_raw + }; + + struct flow_info n_1_info = { + .n_1_pid = TEST_N_1_PID, + .qs = qos_data, + .state = FLOW_ALLOCATED /* RESPONSE SUCCESS */ + }; + + TEST_START(); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + ts_add(&abstime, &timeo, &abstime); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to add flow.\n"); + goto fail; + } + + if (reg_prepare_flow_accept(&info, &pbuf) < 0) { + printf("Failed to prepare flow for accept.\n"); + goto fail; + } + + n_1_info.id = info.id; + n_1_info.mpl = 1; + + pthread_create(&thr, NULL, test_flow_respond_accept, &n_1_info); + + if (reg_wait_flow_accepted(&info, &rbuf, &abstime) < 0 ) { + printf("Flow allocation failed.\n"); + goto fail; + } + + pthread_join(thr, NULL); + + if (info.state != FLOW_ALLOCATED) { + printf("Flow succeeded but not in allocated state.\n"); + goto fail; + } + + if (rbuf.data == NULL) { + printf("rbuf data was not returned.\n"); + goto fail; + } + + if (strcmp((char *) rbuf.data, TEST_DATA) != 0) { + printf("Data was updated.\n"); + goto fail; + } + + n_1_info.state = FLOW_DEALLOCATED; + + reg_dealloc_flow(&info); + + if (info.state != FLOW_DEALLOC_PENDING) { + printf("Flow dealloc requested but not in pending state.\n"); + goto fail; + } + + reg_dealloc_flow_resp(&info); + + if (info.state != FLOW_DEALLOCATED) { + printf("Flow deallocated but not in deallocated state.\n"); + goto fail; + } + + reg_destroy_flow(n_1_info.id); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + + +static int test_reg_allocate_flow_fail(void) +{ + buffer_t buf = {NULL, 0}; + pthread_t thr; + struct timespec abstime; + struct timespec timeo = TIMESPEC_INIT_S(1); + + struct flow_info info = { + .n_pid = TEST_PID, + .qs = qos_raw + }; + + struct flow_info n_1_info = { + .n_1_pid = TEST_N_1_PID, + .qs = qos_data, + .state = FLOW_DEALLOCATED /* RESPONSE FAIL */ + }; + + TEST_START(); + + clock_gettime(PTHREAD_COND_CLOCK, &abstime); + + ts_add(&abstime, &timeo, &abstime); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_flow(&info) < 0) { + printf("Failed to add flow.\n"); + goto fail; + } + + info.n_1_pid = TEST_N_1_PID; + + if (reg_prepare_flow_alloc(&info) < 0) { + printf("Failed to prepare flow for alloc.\n"); + goto fail; + } + + n_1_info.id = info.id; + + pthread_create(&thr, NULL, test_flow_respond_alloc, &n_1_info); + + if (reg_wait_flow_allocated(&info, &buf, &abstime) == 0 ) { + printf("Flow allocation succeeded.\n"); + goto fail; + } + + pthread_join(thr, NULL); + + if (info.state != FLOW_DEALLOCATED) { + printf("Flow failed but not in deallocated state.\n"); + goto fail; + } + + reg_destroy_flow(n_1_info.id); + + reg_fini(); + + TEST_SUCCESS(); + + return 0; + fail: + REG_TEST_FAIL(); + return -1; +} + +static int test_reg_flow(void) { + int ret = 0; + + ret |= test_reg_create_flow(); + + ret |= test_reg_allocate_flow_timeout(); + + ret |= test_reg_accept_flow_success(); + + ret |= test_reg_accept_flow_success_no_crypt(); + + ret |= test_reg_allocate_flow_fail(); + + return ret; +} + +static int test_reg_create_ipcp(void) +{ + struct ipcp_info info = { + .name = TEST_IPCP, + .pid = TEST_PID, + .state = IPCP_BOOT /* set by spawn_ipcp */ + }; + + TEST_START(); + + if (reg_init() < 0) { + printf("Failed to init registry.\n"); + goto fail; + } + + if (reg_create_ipcp(&info) < 0) { + printf("Failed to create ipcp.\n"); + goto fail; + } + + if (reg.n_ipcps != 1) { + printf("n_ipcps was not updated.\n"); + goto fail; + } + + if (!reg_has_ipcp(info.pid)) { + printf("Failed to find ipcp.\n"); + goto fail; + } + + if (reg_destroy_proc(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_proc(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_proc(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_proc(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_proc(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_proc(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; +} |