diff options
author | Dimitri Staessens <dimitri@ouroboros.rocks> | 2024-02-17 10:19:46 +0100 |
---|---|---|
committer | Sander Vrijders <sander@ouroboros.rocks> | 2024-02-19 11:49:07 +0100 |
commit | 06ee3370998f965b469d1c2859e3e34159c71e20 (patch) | |
tree | 93881747a4f1e99f6932231b0cb2358941cb9741 /src/irmd/reg/name.c | |
parent | 7bb8aed15c7f29de4d9719acf8db7fdf73731af5 (diff) | |
download | ouroboros-06ee3370998f965b469d1c2859e3e34159c71e20.tar.gz ouroboros-06ee3370998f965b469d1c2859e3e34159c71e20.zip |
irmd: Revise IRMd internals
This is a full revision of the IRMd internal implementation.
The registry is now a proper subcomponent managing its own internal
lock (a single mutex). Some tests are added for the registry and its
data structures. Some macros for tests are added in <ouroboros/test.h>.
Flow allocation is now more symmetric between the client side (alloc)
and server size (accept). Each will create a flow in pending state
(ALLOC_PENDING/ACCEPT_PENDING) that is potentially fulfilled by an
IPCP using respond_alloc and respond_accept primitives. Deallocation
is split in flow_dealloc (application side) and ipcp_flow_dealloc
(IPCP side) to get the flow in DEALLOC_PENDING and DEALLOCATED state.
Cleanup of failed flow allocation is now properly handled instead of
relying on the sanitizer thread. The new sanitizer only needs to
monitor crashed processes.
On shutdown, the IRMd will now detect hanging processes and SIGKILL
them and clean up their fuse mountpoints if needed.
A lot of other things have been cleaned up and shuffled around a bit.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
Diffstat (limited to 'src/irmd/reg/name.c')
-rw-r--r-- | src/irmd/reg/name.c | 516 |
1 files changed, 222 insertions, 294 deletions
diff --git a/src/irmd/reg/name.c b/src/irmd/reg/name.c index db9842d1..1ac939a5 100644 --- a/src/irmd/reg/name.c +++ b/src/irmd/reg/name.c @@ -1,3 +1,4 @@ + /* * Ouroboros - Copyright (C) 2016 - 2024 * @@ -20,428 +21,355 @@ * Foundation, Inc., http://www.fsf.org/about/contact/. */ -#if defined(__linux__) || defined(__CYGWIN__) -#define _DEFAULT_SOURCE -#else #define _POSIX_C_SOURCE 200809L -#endif - -#include "config.h" -#define OUROBOROS_PREFIX "reg_name" +#define OUROBOROS_PREFIX "reg/name" -#include <ouroboros/errno.h> #include <ouroboros/logs.h> -#include <ouroboros/time_utils.h> -#include <ouroboros/pthread.h> +#include <ouroboros/utils.h> #include "name.h" -#include "utils.h" +#include <assert.h> +#include <errno.h> #include <stdlib.h> -#include <stdbool.h> #include <string.h> -#include <signal.h> -#include <unistd.h> -#include <limits.h> -#include <assert.h> - -struct reg_name * reg_name_create(const char * name, - enum pol_balance lb) -{ - pthread_condattr_t cattr; - struct reg_name * n; - - assert(name != NULL); - n = malloc(sizeof(*n)); - if (n == NULL) - goto fail_malloc; - - if (pthread_condattr_init(&cattr)) - goto fail_cattr; - -#ifndef __APPLE__ - pthread_condattr_setclock(&cattr, PTHREAD_COND_CLOCK); -#endif - if (pthread_cond_init(&n->cond, &cattr)) - goto fail_cond; - - if (pthread_mutex_init(&n->mtx, NULL)) - goto fail_mutex; +struct prog_entry { + struct list_head next; + char ** exec; +}; - n->name = strdup(name); - if (n->name == NULL) - goto fail_name; +struct proc_entry { + struct list_head next; + pid_t pid; +}; - pthread_condattr_destroy(&cattr); - - list_head_init(&n->next); - list_head_init(&n->reg_progs); - list_head_init(&n->reg_pids); - - n->pol_lb = lb; - n->state = NAME_IDLE; - - return n; +static void __free_prog_entry(struct prog_entry * entry) +{ + assert(entry != NULL); + assert(entry->exec != NULL); - fail_name: - pthread_mutex_destroy(&n->mtx); - fail_mutex: - pthread_cond_destroy(&n->cond); - fail_cond: - pthread_condattr_destroy(&cattr); - fail_cattr: - free(n); - fail_malloc: - return NULL; + argvfree(entry->exec); + free(entry); } -static void cancel_reg_name_destroy(void * o) +struct reg_name * reg_name_create(const struct name_info * info) { struct reg_name * name; - struct list_head * p; - struct list_head * h; - name = (struct reg_name *) o; + assert(info != NULL); - pthread_mutex_unlock(&name->mtx); - - pthread_cond_destroy(&name->cond); - pthread_mutex_destroy(&name->mtx); + name = malloc(sizeof(*name)); + if (name == NULL) { + log_err("Failed to malloc name."); + goto fail_malloc; + } - if (name->name != NULL) - free(name->name); + list_head_init(&name->next); + list_head_init(&name->progs); + list_head_init(&name->procs); + list_head_init(&name->active); - list_for_each_safe(p, h, &name->reg_pids) { - struct pid_el * pe = list_entry(p, struct pid_el, next); - list_del(&pe->next); - free(pe); - } + name->info = *info; + name->n_progs = 0; + name->n_procs = 0; + name->n_active = 0; - list_for_each_safe(p, h, &name->reg_progs) { - struct str_el * se = list_entry(p, struct str_el, next); - list_del(&se->next); - free(se->str); - free(se); - } + return name; - free(name); + fail_malloc: + return NULL; } void reg_name_destroy(struct reg_name * name) { - if (name == NULL) - return; - - pthread_mutex_lock(&name->mtx); - - if (name->state == NAME_DESTROY) { - pthread_mutex_unlock(&name->mtx); - return; - } - - if (name->state != NAME_FLOW_ACCEPT) - name->state = NAME_NULL; - else - name->state = NAME_DESTROY; + assert(name != NULL); - pthread_cond_broadcast(&name->cond); + assert(list_is_empty(&name->next)); - pthread_cleanup_push(cancel_reg_name_destroy, name); + assert(name->n_progs == 0); + assert(name->n_procs == 0); + assert(name->n_active == 0); - while (name->state != NAME_NULL) - pthread_cond_wait(&name->cond, &name->mtx); + assert(list_is_empty(&name->progs)); + assert(list_is_empty(&name->procs)); + assert(list_is_empty(&name->active)); - pthread_cleanup_pop(true); + free(name); } -static bool reg_name_has_prog(struct reg_name * name, - const char * prog) +static struct proc_entry * __reg_name_get_active(const struct reg_name * name, + pid_t pid) { struct list_head * p; - list_for_each(p, &name->reg_progs) { - struct str_el * name = list_entry(p, struct str_el, next); - if (!strcmp(name->str, prog)) - return true; - } - - return false; -} - -int reg_name_add_prog(struct reg_name * name, - struct reg_prog * a) -{ - struct str_el * n; - - if (reg_name_has_prog(name, a->prog)) { - log_warn("Program %s already accepting flows for %s.", - a->prog, name->name); - return 0; - } - - if (!(a->flags & BIND_AUTO)) { - log_dbg("Program %s cannot be auto-instantiated.", a->prog); - return 0; - } - - n = malloc(sizeof(*n)); - if (n == NULL) - return -ENOMEM; + assert(name != NULL); + assert(pid > 0); - n->str = strdup(a->prog); - if (n->str == NULL) { - free(n); - return -ENOMEM; + list_for_each(p, &name->active) { + struct proc_entry * entry; + entry = list_entry(p, struct proc_entry, next); + if (entry->pid == pid) + return entry; } - list_add(&n->next, &name->reg_progs); - - pthread_mutex_lock(&name->mtx); - - if (name->state == NAME_IDLE) - name->state = NAME_AUTO_ACCEPT; - - pthread_mutex_unlock(&name->mtx); - - return 0; + return NULL; } -void reg_name_del_prog(struct reg_name * name, - const char * prog) +static void __reg_name_del_all_active(struct reg_name * name, + pid_t pid) { struct list_head * p; struct list_head * h; - list_for_each_safe(p, h, &name->reg_progs) { - struct str_el * se = list_entry(p, struct str_el, next); - if (strcmp(prog, se->str) == 0) { - list_del(&se->next); - free(se->str); - free(se); + list_for_each_safe(p, h, &name->active) { + struct proc_entry * entry; + entry = list_entry(p, struct proc_entry, next); + if (entry->pid == pid) { + list_del(&entry->next); + free(entry); + name->n_active--; } } - - pthread_mutex_lock(&name->mtx); - - if (name->state == NAME_AUTO_ACCEPT && list_is_empty(&name->reg_progs)) { - name->state = NAME_IDLE; - pthread_cond_broadcast(&name->cond); - } - - pthread_mutex_unlock(&name->mtx); } -char * reg_name_get_prog(struct reg_name * name) +static struct proc_entry * __reg_name_get_proc(const struct reg_name * name, + pid_t pid) { - if (!list_is_empty(&name->reg_pids) || list_is_empty(&name->reg_progs)) - return NULL; + struct list_head * p; + + assert(name != NULL); + assert(pid > 0); - return list_first_entry(&name->reg_progs, struct str_el, next)->str; + list_for_each(p, &name->procs) { + struct proc_entry * entry; + entry = list_entry(p, struct proc_entry, next); + if (entry->pid == pid) + return entry; + } + + return NULL; } -static bool reg_name_has_pid(struct reg_name * name, - pid_t pid) +static struct prog_entry * __reg_name_get_prog(const struct reg_name * name, + const char * prog) { struct list_head * p; - list_for_each(p, &name->reg_progs) { - struct pid_el * name = list_entry(p, struct pid_el, next); - if (name->pid == pid) - return true; + assert(name != NULL); + assert(prog != NULL); + + list_for_each(p, &name->progs) { + struct prog_entry * entry; + entry = list_entry(p, struct prog_entry, next); + if (strcmp(entry->exec[0], prog) == 0) + return entry; } - return false; + return NULL; } -int reg_name_add_pid(struct reg_name * name, - pid_t pid) +int reg_name_add_active(struct reg_name * name, + pid_t pid) { - struct pid_el * i; + struct proc_entry * entry; - assert(name); + assert(name != NULL); + assert(pid > 0); - if (reg_name_has_pid(name, pid)) { - log_dbg("Process already registered with this name."); - return -EPERM; - } + assert(__reg_name_get_proc(name, pid) != NULL); - pthread_mutex_lock(&name->mtx); + log_dbg("Process %d accepting flows for %s.", pid, name->info.name); - if (name->state == NAME_NULL) { - pthread_mutex_unlock(&name->mtx); - log_dbg("Tried to add instance in NULL state."); - return -EPERM; - } + if (__reg_name_get_active(name, pid) != NULL) + log_dbg("Process calling accept from multiple threads."); - i = malloc(sizeof(*i)); - if (i == NULL) { - pthread_mutex_unlock(&name->mtx); - return -ENOMEM; + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc active."); + goto fail_malloc; } - i->pid = pid; + entry->pid = pid; - /* load balancing policy assigns queue order for this process. */ - switch(name->pol_lb) { + switch (name->info.pol_lb) { case LB_RR: /* Round robin policy. */ - list_add_tail(&i->next, &name->reg_pids); + list_add_tail(&entry->next, &name->active); break; case LB_SPILL: /* Keep accepting flows on the current process */ - list_add(&i->next, &name->reg_pids); + list_add(&entry->next, &name->active); break; default: - free(i); - assert(false); - }; - - if (name->state == NAME_IDLE || - name->state == NAME_AUTO_ACCEPT || - name->state == NAME_AUTO_EXEC) { - name->state = NAME_FLOW_ACCEPT; - pthread_cond_broadcast(&name->cond); + goto fail_unreachable; } - pthread_mutex_unlock(&name->mtx); + name->n_active++; return 0; -} -void reg_name_set_policy(struct reg_name * name, - enum pol_balance lb) -{ - name->pol_lb = lb; + fail_unreachable: + free(entry); + assert(false); + fail_malloc: + return -1; } -static void reg_name_check_state(struct reg_name * name) +void reg_name_del_active(struct reg_name * name, + pid_t pid) { - assert(name); + struct proc_entry * entry; - if (name->state == NAME_DESTROY) { - name->state = NAME_NULL; - pthread_cond_broadcast(&name->cond); + entry = __reg_name_get_active(name, pid); + if (entry == NULL) return; - } - if (list_is_empty(&name->reg_pids)) { - if (!list_is_empty(&name->reg_progs)) - name->state = NAME_AUTO_ACCEPT; - else - name->state = NAME_IDLE; - } else { - name->state = NAME_FLOW_ACCEPT; - } + list_del(&entry->next); + + name->n_active--; - pthread_cond_broadcast(&name->cond); + free(entry); } -void reg_name_del_pid_el(struct reg_name * name, - struct pid_el * p) +pid_t reg_name_get_active(struct reg_name * name) { - assert(name); - assert(p); + assert(name != NULL); - list_del(&p->next); - free(p); + if (list_is_empty(&name->active)) + return -1; - reg_name_check_state(name); + return list_first_entry(&name->active, struct proc_entry, next)->pid; } -void reg_name_del_pid(struct reg_name * name, +int reg_name_add_proc(struct reg_name * name, pid_t pid) { - struct list_head * p; - struct list_head * h; + struct proc_entry * entry; - assert(name); + assert(name != NULL); + assert(pid > 0); - if (name == NULL) - return; + assert(__reg_name_get_proc(name, pid) == NULL); - list_for_each_safe(p, h, &name->reg_pids) { - struct pid_el * a = list_entry(p, struct pid_el, next); - if (a->pid == pid) { - list_del(&a->next); - free(a); - } + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc proc."); + goto fail_malloc; } - reg_name_check_state(name); -} + entry->pid = pid; -pid_t reg_name_get_pid(struct reg_name * name) -{ - if (name == NULL) - return -1; + list_add(&entry->next, &name->procs); - if (list_is_empty(&name->reg_pids)) - return -1; + name->n_procs++; - return list_first_entry(&name->reg_pids, struct pid_el, next)->pid; + return 0; + + fail_malloc: + return -1; } -enum name_state reg_name_get_state(struct reg_name * name) +void reg_name_del_proc(struct reg_name * name, + pid_t pid) { - enum name_state state; + struct proc_entry * entry; - assert(name); + assert(name != NULL); + assert(pid > 0); - pthread_mutex_lock(&name->mtx); + entry = __reg_name_get_proc(name, pid); + if (entry == NULL) + return; + + __reg_name_del_all_active(name, pid); - state = name->state; + list_del(&entry->next); - pthread_mutex_unlock(&name->mtx); + free(entry); - return state; + name->n_procs--; + + assert(__reg_name_get_proc(name, pid) == NULL); } -int reg_name_set_state(struct reg_name * name, - enum name_state state) +bool reg_name_has_proc(const struct reg_name * name, + pid_t pid) +{ + return __reg_name_get_proc(name, pid) != NULL; +} char ** exec; + + +int reg_name_add_prog(struct reg_name * name, + char ** exec) { - assert(state != NAME_DESTROY); + struct prog_entry * entry; + + assert(name != NULL); + assert(exec != NULL); + assert(exec[0] != NULL); + + assert(__reg_name_get_prog(name, exec[0]) == NULL); + + entry = malloc(sizeof(*entry)); + if (entry == NULL) { + log_err("Failed to malloc prog."); + goto fail_malloc; + } + + entry->exec = argvdup(exec); + if (entry->exec == NULL) { + log_err("Failed to argvdup prog."); + goto fail_exec; + } - pthread_mutex_lock(&name->mtx); + list_add(&entry->next, &name->progs); - name->state = state; - pthread_cond_broadcast(&name->cond); + log_dbg("Add prog %s to name %s.", exec[0], name->info.name); - pthread_mutex_unlock(&name->mtx); + name->n_progs++; return 0; + + fail_exec: + free(entry); + fail_malloc: + return -1; } -int reg_name_leave_state(struct reg_name * name, - enum name_state state, - struct timespec * timeout) +void reg_name_del_prog(struct reg_name * name, + const char * prog) { - struct timespec ts; - struct timespec * abstime = NULL; - int ret = 0; + struct prog_entry * entry; - assert(name); - assert(state != NAME_DESTROY); + assert(name != NULL); + assert(prog != NULL); - if (timeout != NULL) { - clock_gettime(PTHREAD_COND_CLOCK, &ts); - ts_add(&ts, timeout, &ts); - abstime = &ts; - } + entry = __reg_name_get_prog(name, prog); + if (entry == NULL) + return; - pthread_mutex_lock(&name->mtx); + list_del(&entry->next); - pthread_cleanup_push(__cleanup_mutex_unlock, &name->mtx); + __free_prog_entry(entry); - while (name->state == state && ret != -ETIMEDOUT) - ret = -__timedwait(&name->cond,&name->mtx, abstime); + name->n_progs--; - if (name->state == NAME_DESTROY) { - ret = -1; - name->state = NAME_NULL; - pthread_cond_broadcast(&name->cond); - } + assert(__reg_name_get_prog(name, prog) == NULL); +} - pthread_cleanup_pop(true); +bool reg_name_has_prog(const struct reg_name * name, + const char * prog) +{ + assert(name != NULL); + assert(prog != NULL); + + return __reg_name_get_prog(name, prog) != NULL; +} + +char ** reg_name_get_exec(const struct reg_name * name) +{ + if (list_is_empty(&name->progs)) + return NULL; - return ret; + return list_first_entry(&name->progs, struct prog_entry, next)->exec; } |