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