<commit_before>#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <pthread.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <signal.h>
#include <assert.h>
#include <endian.h>
#include <sys/stat.h>
#include <pthread.h>

#include "rlite/kernel-msg.h"
#include "rlite/conf-msg.h"
#include "rlite/utils.h"
#include "rlite/list.h"
#include "rlite/evloop.h"
#include "rlite/evloop.h"

#include "../helpers.h"
#include "uipcp-container.h"


const char *ctrl_dev_name = "/dev/rlite";
const char *io_dev_name = "/dev/rlite-io";

struct registered_ipcp {
    unsigned int ipcp_id;
    struct rina_name ipcp_name;
    char *dif_name;

    struct list_head node;
};


/* Global variable containing the main struct of the uipcps. This variable
 * should be accessed directly only by signal handlers (because I don't know
 * how to do it differently). The rest of the program should access it
 * through pointers.
 */
static struct uipcps guipcps;

static int
rlite_conf_response(int sfd, struct rlite_msg_base *req,
                   struct rlite_msg_base_resp *resp)
{
    resp->msg_type = RLITE_CFG_BASE_RESP;
    resp->event_id = req->event_id;

    return rlite_msg_write_fd(sfd, RLITE_MB(resp));
}

static void
track_ipcp_registration(struct uipcps *uipcps, unsigned ipcp_id,
                        const struct rl_cmsg_ipcp_register *req)
{
    struct registered_ipcp *ripcp;

    /* Append a successful registration to the persistent
     * registration list. */
    list_for_each_entry(ripcp, &uipcps->ipcps_registrations, node) {
        if (strcmp(ripcp->dif_name, req->dif_name) == 0 &&
                rina_name_cmp(&ripcp->ipcp_name, &req->ipcp_name) == 0) {
            return;
        }
    }

    ripcp = malloc(sizeof(*ripcp));
    if (!ripcp) {
        PE("ripcp allocation failed\n");
        return;
    }

    memset(ripcp, 0, sizeof(*ripcp));
    ripcp->dif_name = strdup(req->dif_name);
    ripcp->ipcp_id = ipcp_id;
    rina_name_copy(&ripcp->ipcp_name, &req->ipcp_name);
    list_add_tail(&ripcp->node, &uipcps->ipcps_registrations);
}

static void
track_ipcp_unregistration(struct uipcps *uipcps,
                          unsigned int ipcp_id)
{
    struct registered_ipcp *ripcp;

    /* Try to remove a registration element from the persistent
     * registration list. If 'dif_name' and 'ipcp_name' are specified,
     * match the corresponding tuple fields. Otherwise match the
     * by IPCP id. */
    list_for_each_entry(ripcp, &uipcps->ipcps_registrations, node) {
        if (ripcp->ipcp_id == ipcp_id) {
            list_del(&ripcp->node);
            free(ripcp->dif_name);
            rina_name_free(&ripcp->ipcp_name);
            free(ripcp);
            break;
        }
    }
}

static int
ipcp_register(struct uipcps *uipcps,
              const struct rl_cmsg_ipcp_register *req)
{
    struct uipcp *uipcp;
    int result = RLITE_ERR;

    /* Grab the corresponding userspace IPCP. */
    uipcp = uipcp_get_by_name(uipcps, &req->ipcp_name);
    if (!uipcp) {
        return -1;
    }

    if (uipcp->ops.register_to_lower) {
        result = uipcp->ops.register_to_lower(uipcp, req);

        if (result == RLITE_SUCC) {
            /* Track the (un)registration in the persistent registration
             * list. */
            if (req->reg) {
                track_ipcp_registration(uipcps, uipcp->ipcp_id, req);
            } else {
                track_ipcp_unregistration(uipcps, uipcp->ipcp_id);
            }
        }
    }

    uipcp_put(uipcps, uipcp->ipcp_id);

    return result;
}

static int
rlite_conf_ipcp_register(struct uipcps *uipcps, int sfd,
                        const struct rlite_msg_base *b_req)
{
    struct rl_cmsg_ipcp_register *req = (struct rl_cmsg_ipcp_register *)b_req;
    struct rlite_msg_base_resp resp;

    resp.result = ipcp_register(uipcps, req);

    return rlite_conf_response(sfd, RLITE_MB(req), &resp);
}

static int
ipcp_enroll(struct uipcps *uipcps, const struct rl_cmsg_ipcp_enroll *req)
{
    int ret = RLITE_ERR; /* Report failure by default. */
    struct uipcp *uipcp;

    /* Find the userspace part of the enrolling IPCP. */
    uipcp = uipcp_get_by_name(uipcps, &req->ipcp_name);
    if (uipcp && uipcp->ops.enroll) {
        ret = uipcp->ops.enroll(uipcp, req, 1);
    }

    uipcp_put(uipcps, uipcp->ipcp_id);

    return ret;
}

static int
rlite_conf_ipcp_enroll(struct uipcps *uipcps, int sfd,
                       const struct rlite_msg_base *b_req)
{
    struct rl_cmsg_ipcp_enroll *req = (struct rl_cmsg_ipcp_enroll *)b_req;
    struct rlite_msg_base_resp resp;

    resp.result = ipcp_enroll(uipcps, req);

    return rlite_conf_response(sfd, RLITE_MB(req), &resp);
}

static int
rlite_conf_ipcp_dft_set(struct uipcps *uipcps, int sfd,
                       const struct rlite_msg_base *b_req)
{
    struct rl_cmsg_ipcp_dft_set *req = (struct rl_cmsg_ipcp_dft_set *)b_req;
    struct rlite_msg_base_resp resp;
    struct uipcp *uipcp;

    resp.result = RLITE_ERR; /* Report failure by default. */

    uipcp = uipcp_get_by_name(uipcps, &req->ipcp_name);
    if (!uipcp) {
        goto out;
    }

    if (uipcp->ops.dft_set) {
        resp.result = uipcp->ops.dft_set(uipcp, req);
    }

    uipcp_put(uipcps, uipcp->ipcp_id);

out:
    return rlite_conf_response(sfd, RLITE_MB(req), &resp);
}

static int
rlite_conf_ipcp_rib_show(struct uipcps *uipcps, int sfd,
                        const struct rlite_msg_base *b_req)
{
    struct rl_cmsg_ipcp_rib_show_req *req =
                    (struct rl_cmsg_ipcp_rib_show_req *)b_req;
    struct rl_cmsg_ipcp_rib_show_resp resp;
    struct uipcp *uipcp;
    int ret;

    resp.result = RLITE_ERR; /* Report failure by default. */
    resp.dump = NULL;

    uipcp = uipcp_get_by_name(uipcps, &req->ipcp_name);
    if (!uipcp) {
        goto out;
    }

    if (uipcp->ops.rib_show) {
        resp.dump = uipcp->ops.rib_show(uipcp);
        if (resp.dump) {
            resp.result = RLITE_SUCC;
        }
    }

    uipcp_put(uipcps, uipcp->ipcp_id);

out:
    resp.msg_type = RLITE_CFG_IPCP_RIB_SHOW_RESP;
    resp.event_id = req->event_id;

    ret = rlite_msg_write_fd(sfd, RLITE_MB(&resp));

    if (resp.dump) {
        free(resp.dump);
    }

    return ret;
}

typedef int (*rlite_req_handler_t)(struct uipcps *uipcps, int sfd,
                                   const struct rlite_msg_base * b_req);

/* The table containing all application request handlers. */
static rlite_req_handler_t rlite_config_handlers[] = {
    [RLITE_CFG_IPCP_REGISTER] = rlite_conf_ipcp_register,
    [RLITE_CFG_IPCP_ENROLL] = rlite_conf_ipcp_enroll,
    [RLITE_CFG_IPCP_DFT_SET] = rlite_conf_ipcp_dft_set,
    [RLITE_CFG_IPCP_RIB_SHOW_REQ] = rlite_conf_ipcp_rib_show,
    [RLITE_CFG_MSG_MAX] = NULL,
};

struct worker_info {
    struct uipcps *uipcps;
    int cfd;
};

static void *
worker_fn(void *opaque)
{
    struct worker_info *wi = opaque;
    struct rlite_msg_base *req;
    char serbuf[4096];
    char msgbuf[4096];
    int ret;
    int n;

    PD("Worker %p started\n", wi);

    /* Read the request message in serialized form. */
    n = read(wi->cfd, serbuf, sizeof(serbuf));
    if (n < 0) {
        PE("read() error [%d]\n", n);
    }

    /* Deserialize into a formatted message. */
    ret = deserialize_rlite_msg(rlite_conf_numtables, RLITE_CFG_MSG_MAX,
            serbuf, n, msgbuf, sizeof(msgbuf));
    if (ret) {
        PE("deserialization error [%d]\n", ret);
    }

    /* Lookup the message type. */
    req = RLITE_MB(msgbuf);
    if (rlite_config_handlers[req->msg_type] == NULL) {
        struct rlite_msg_base_resp resp;

        PE("Invalid message received [type=%d]\n",
                req->msg_type);
        resp.msg_type = RLITE_CFG_BASE_RESP;
        resp.event_id = req->event_id;
        resp.result = RLITE_ERR;
        rlite_msg_write_fd(wi->cfd, RLITE_MB(&resp));
    } else {
        /* Valid message type: handle the request. */
        ret = rlite_config_handlers[req->msg_type](wi->uipcps, wi->cfd, req);
        if (ret) {
            PE("Error while handling message type [%d]\n",
                    req->msg_type);
        }
    }

    rlite_msg_free(rlite_conf_numtables, RLITE_CFG_MSG_MAX, req);

    /* Close the connection. */
    close(wi->cfd);

    free(wi);

    PD("Worker %p stopped\n", wi);

    return NULL;
}

/* Unix server thread to manage configuration requests. */
static int
unix_server(struct uipcps *uipcps)
{
    for (;;) {
        struct sockaddr_un client_address;
        socklen_t client_address_len = sizeof(client_address);
        struct worker_info *wi;
        pthread_t wth;
        int ret;

        wi = malloc(sizeof(*wi));
        wi->uipcps = uipcps;

        /* Accept a new client and create a thread to serve it. */
        wi->cfd = accept(uipcps->lfd, (struct sockaddr *)&client_address,
                         &client_address_len);

        ret = pthread_create(&wth, NULL, worker_fn, wi);
        if (ret) {
            PE("pthread_create() failed [%d]\n", errno);
            close(wi->cfd);
            free(wi);
        }
    }

    return 0;
}

static int
uipcps_ipcp_update(struct rlite_evloop *loop,
                   const struct rlite_msg_base *b_resp,
                   const struct rlite_msg_base *b_req)
{
    struct uipcps *uipcps = container_of(loop, struct uipcps, loop);
    struct rl_kmsg_ipcp_update *upd = (struct rl_kmsg_ipcp_update *)b_resp;
    struct uipcp *uipcp;
    int ret = -1;

    switch (upd->update_type) {
        case RLITE_UPDATE_ADD:
            if (upd->dif_type && type_has_uipcp(upd->dif_type)) {
                /* We only care about IPCP with userspace implementation. */
                ret = uipcp_add(uipcps, upd->ipcp_id, upd->dif_type);
            }
            break;

        case RLITE_UPDATE_DEL:
            pthread_mutex_lock(&uipcps->lock);
            uipcp = uipcp_lookup(uipcps, upd->ipcp_id);
            pthread_mutex_unlock(&uipcps->lock);
            if (uipcp) {
                /* Track all the unregistrations of the destroyed IPCP in
                 * the persistent registrations list. */
                track_ipcp_unregistration(uipcps, upd->ipcp_id);
                ret = uipcp_put(uipcps, upd->ipcp_id);

            } else {
                /* This is an IPCP with no userspace implementation. */
                ret = 0;
            }
            break;

        default:
            ret = 0;
            break;
    }

    if (ret) {
        PE("IPCP update synchronization failed\n");
    }

    uipcps_print(uipcps);

    return 0;
}

static void
process_persistence_file(struct uipcps *uipcps)
{
    /* Read the persistent IPCP registration file into
     * the ipcps_registrations list. */
    FILE *fpreg = fopen(RLITE_PERSISTENCE_FILE, "r");
    char line[4096];

    if (!fpreg) {
        PD("Persistence file %s not found\n", RLITE_PERSISTENCE_FILE);
        return;
    }

    PD("Persistence file %s opened\n", RLITE_PERSISTENCE_FILE);

    while (fgets(line, sizeof(line), fpreg)) {
        char *s0 = NULL;
        char *s1 = NULL;
        char *s2 = NULL;
        char *s3 = NULL;
        char *s4 = NULL;

        s0 = strchr(line, '\n');
        if (s0) {
            *s0 = '\0';
        }

        s0 = strtok(line, " ");
        s1 = strtok(0, " ");
        s2 = strtok(0, " ");
        s3 = strtok(0, " ");
        s4 = strtok(0, " ");

        if (strncmp(s0, "REG", 3) == 0) {
            /* Redo an ipcp registration. */
            struct rl_cmsg_ipcp_register msg;
            int ret = 0;

            if (!s1 || !s2) {
                PE("Invalid REG line");
                continue;
            }

            msg.reg = 1;
            ret |= rina_name_from_string(s2, &msg.ipcp_name);
            msg.dif_name = strdup(s1);
            if (ret || !msg.dif_name) {
                PE("Out of memory\n");
                rlite_msg_free(rlite_conf_numtables, RLITE_CFG_MSG_MAX,
                               RLITE_MB(&msg));
                continue;
            }

            ret = ipcp_register(uipcps, &msg);
            PI("Automatic re-registration for %s --> %s\n",
                    s2, (ret == RLITE_SUCC) ? "DONE" : "FAILED");
            rlite_msg_free(rlite_conf_numtables, RLITE_CFG_MSG_MAX,
                           RLITE_MB(&msg));

        } else if (strncmp(s0, "ENR", 3) == 0) {
            /* Redo an enrollment. */
            struct rl_cmsg_ipcp_enroll msg;
            int ret = 0;

            if (!s1 || !s2 || !s3 || !s4) {
                PE("Invalid ENR line");
                continue;
            }

            ret |= rina_name_from_string(s2, &msg.ipcp_name);
            ret |= rina_name_from_string(s3, &msg.neigh_name);
            msg.dif_name = strdup(s1);
            msg.supp_dif_name = strdup(s4);
            if (ret || !msg.dif_name || !msg.supp_dif_name) {
                PE("Out of memory\n");
                rlite_msg_free(rlite_conf_numtables, RLITE_CFG_MSG_MAX,
                               RLITE_MB(&msg));
                continue;
            }

            ret = ipcp_enroll(uipcps, &msg);
            PI("Automatic re-enrollment for %s in DIF %s --> %s\n", s2, s1,
               (ret == RLITE_SUCC) ? "DONE" : "FAILED");
            rlite_msg_free(rlite_conf_numtables, RLITE_CFG_MSG_MAX,
                           RLITE_MB(&msg));
        }
    }

    fclose(fpreg);
}

static int
uipcps_update(struct uipcps *uipcps)
{
    struct rl_ipcp *rl_ipcp;
    int ret = 0;

    ret = rl_evloop_init(&uipcps->loop, ctrl_dev_name, NULL, 0);
    if (ret) {
        return ret;
    }

    rl_evloop_set_handler(&uipcps->loop, RLITE_KER_IPCP_UPDATE,
                          uipcps_ipcp_update);

    rl_ctrl_ipcps_print(&uipcps->loop.ctrl);

    /* Create an userspace IPCP for each existing IPCP. */
    pthread_mutex_lock(&uipcps->loop.lock);
    list_for_each_entry(rl_ipcp, &uipcps->loop.ctrl.ipcps, node) {
        if (type_has_uipcp(rl_ipcp->dif_type)) {
            ret = uipcp_add(uipcps, rl_ipcp->ipcp_id,
                            rl_ipcp->dif_type);
            if (ret) {
                pthread_mutex_unlock(&uipcps->loop.lock);
                return ret;
            }
        }
    }
    pthread_mutex_unlock(&uipcps->loop.lock);

    uipcps_print(uipcps);

    process_persistence_file(uipcps);

    return 0;
}

static void
dump_persistence_file(struct uipcps *uipcps)
{
    FILE *fpreg = fopen(RLITE_PERSISTENCE_FILE, "w");
    struct registered_ipcp *ripcp;
    struct uipcp *uipcp;

    if (!fpreg) {
        PE("Cannot open persistence file (%s)\n",
                RLITE_PERSISTENCE_FILE);
        return;
    }

    /* Dump the ipcps_registrations list, so that
     * subsequent uipcps invocations can redo the registrations. */
    list_for_each_entry(ripcp, &uipcps->ipcps_registrations, node) {
        char *ipcp_s;

        ipcp_s = rina_name_to_string(&ripcp->ipcp_name);
        if (ipcp_s) {
            fprintf(fpreg, "REG %s %s\n", ripcp->dif_name, ipcp_s);
        } else {
            PE("Error in rina_name_to_string()\n");
        }
        if (ipcp_s) free(ipcp_s);
    }

    /* Dump to a file the list of enrolled ipcps for which we were initiator,
     * so that subsequent uipcps invocations can redo the registrations. */
    list_for_each_entry(uipcp, &uipcps->uipcps, node) {
        char *ipcp_s, *neigh_s;
        struct enrolled_neigh *en, *tmp;
        struct list_head neighs;

        if (!uipcp->ops.get_enrollment_targets) {
            continue;
        }

        if (uipcp->ops.get_enrollment_targets(uipcp, &neighs)) {
            PE("get_enrolled_neighs() failed for uipcp [%u]\n",
               uipcp->ipcp_id);
            continue;
        }

        list_for_each_entry_safe(en, tmp, &neighs, node) {
            ipcp_s = rina_name_to_string(&en->ipcp_name);
            neigh_s = rina_name_to_string(&en->neigh_name);

            if (ipcp_s && neigh_s) {
                fprintf(fpreg, "ENR %s %s %s %s\n", en->dif_name, ipcp_s,
                        neigh_s, en->supp_dif);

            } else {
                PE("Error in rina_name_to_string()\n");
            }

            if (ipcp_s) free(ipcp_s);
            if (neigh_s) free(neigh_s);
        }
    }

    fclose(fpreg);
}

static void
sigint_handler(int signum)
{
    struct uipcps *uipcps = &guipcps;

    pthread_mutex_lock(&uipcps->lock);
    dump_persistence_file(uipcps);
    pthread_mutex_unlock(&uipcps->lock);

    /* TODO Here we should free all the dynamically allocated memory
     * referenced by uipcps. */

    unlink(RLITE_UIPCPS_UNIX_NAME);
    exit(EXIT_SUCCESS);
}

static int
char_device_exists(const char *path)
{
    struct stat s;

    return stat(path, &s) == 0 && S_ISCHR(s.st_mode);
}

int main(int argc, char **argv)
{
    struct uipcps *uipcps = &guipcps;
    struct sockaddr_un server_address;
    struct sigaction sa;
    int ret;

    /* We require root permissions. */
    if (geteuid() != 0) {
        PE("uipcps daemon needs root permissions\n");
        return -1;
    }

    if (!char_device_exists(ctrl_dev_name)) {
        PE("Device %s not found", ctrl_dev_name);
        return -1;
    }

    if (!char_device_exists(io_dev_name)) {
        PE("Device %s not found", io_dev_name);
        return -1;
    }

    /* Open a Unix domain socket to listen to. */
    uipcps->lfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (uipcps->lfd < 0) {
        perror("socket(AF_UNIX)");<commit_msg>user: uipcps: add missing newline in error logs<commit_after>#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <pthread.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <signal.h>
#include <assert.h>
#include <endian.h>
#include <sys/stat.h>
#include <pthread.h>

#include "rlite/kernel-msg.h"
#include "rlite/conf-msg.h"
#include "rlite/utils.h"
#include "rlite/list.h"
#include "rlite/evloop.h"
#include "rlite/evloop.h"

#include "../helpers.h"
#include "uipcp-container.h"


const char *ctrl_dev_name = "/dev/rlite";
const char *io_dev_name = "/dev/rlite-io";

struct registered_ipcp {
    unsigned int ipcp_id;
    struct rina_name ipcp_name;
    char *dif_name;

    struct list_head node;
};


/* Global variable containing the main struct of the uipcps. This variable
 * should be accessed directly only by signal handlers (because I don't know
 * how to do it differently). The rest of the program should access it
 * through pointers.
 */
static struct uipcps guipcps;

static int
rlite_conf_response(int sfd, struct rlite_msg_base *req,
                   struct rlite_msg_base_resp *resp)
{
    resp->msg_type = RLITE_CFG_BASE_RESP;
    resp->event_id = req->event_id;

    return rlite_msg_write_fd(sfd, RLITE_MB(resp));
}

static void
track_ipcp_registration(struct uipcps *uipcps, unsigned ipcp_id,
                        const struct rl_cmsg_ipcp_register *req)
{
    struct registered_ipcp *ripcp;

    /* Append a successful registration to the persistent
     * registration list. */
    list_for_each_entry(ripcp, &uipcps->ipcps_registrations, node) {
        if (strcmp(ripcp->dif_name, req->dif_name) == 0 &&
                rina_name_cmp(&ripcp->ipcp_name, &req->ipcp_name) == 0) {
            return;
        }
    }

    ripcp = malloc(sizeof(*ripcp));
    if (!ripcp) {
        PE("ripcp allocation failed\n");
        return;
    }

    memset(ripcp, 0, sizeof(*ripcp));
    ripcp->dif_name = strdup(req->dif_name);
    ripcp->ipcp_id = ipcp_id;
    rina_name_copy(&ripcp->ipcp_name, &req->ipcp_name);
    list_add_tail(&ripcp->node, &uipcps->ipcps_registrations);
}

static void
track_ipcp_unregistration(struct uipcps *uipcps,
                          unsigned int ipcp_id)
{
    struct registered_ipcp *ripcp;

    /* Try to remove a registration element from the persistent
     * registration list. If 'dif_name' and 'ipcp_name' are specified,
     * match the corresponding tuple fields. Otherwise match the
     * by IPCP id. */
    list_for_each_entry(ripcp, &uipcps->ipcps_registrations, node) {
        if (ripcp->ipcp_id == ipcp_id) {
            list_del(&ripcp->node);
            free(ripcp->dif_name);
            rina_name_free(&ripcp->ipcp_name);
            free(ripcp);
            break;
        }
    }
}

static int
ipcp_register(struct uipcps *uipcps,
              const struct rl_cmsg_ipcp_register *req)
{
    struct uipcp *uipcp;
    int result = RLITE_ERR;

    /* Grab the corresponding userspace IPCP. */
    uipcp = uipcp_get_by_name(uipcps, &req->ipcp_name);
    if (!uipcp) {
        return -1;
    }

    if (uipcp->ops.register_to_lower) {
        result = uipcp->ops.register_to_lower(uipcp, req);

        if (result == RLITE_SUCC) {
            /* Track the (un)registration in the persistent registration
             * list. */
            if (req->reg) {
                track_ipcp_registration(uipcps, uipcp->ipcp_id, req);
            } else {
                track_ipcp_unregistration(uipcps, uipcp->ipcp_id);
            }
        }
    }

    uipcp_put(uipcps, uipcp->ipcp_id);

    return result;
}

static int
rlite_conf_ipcp_register(struct uipcps *uipcps, int sfd,
                        const struct rlite_msg_base *b_req)
{
    struct rl_cmsg_ipcp_register *req = (struct rl_cmsg_ipcp_register *)b_req;
    struct rlite_msg_base_resp resp;

    resp.result = ipcp_register(uipcps, req);

    return rlite_conf_response(sfd, RLITE_MB(req), &resp);
}

static int
ipcp_enroll(struct uipcps *uipcps, const struct rl_cmsg_ipcp_enroll *req)
{
    int ret = RLITE_ERR; /* Report failure by default. */
    struct uipcp *uipcp;

    /* Find the userspace part of the enrolling IPCP. */
    uipcp = uipcp_get_by_name(uipcps, &req->ipcp_name);
    if (uipcp && uipcp->ops.enroll) {
        ret = uipcp->ops.enroll(uipcp, req, 1);
    }

    uipcp_put(uipcps, uipcp->ipcp_id);

    return ret;
}

static int
rlite_conf_ipcp_enroll(struct uipcps *uipcps, int sfd,
                       const struct rlite_msg_base *b_req)
{
    struct rl_cmsg_ipcp_enroll *req = (struct rl_cmsg_ipcp_enroll *)b_req;
    struct rlite_msg_base_resp resp;

    resp.result = ipcp_enroll(uipcps, req);

    return rlite_conf_response(sfd, RLITE_MB(req), &resp);
}

static int
rlite_conf_ipcp_dft_set(struct uipcps *uipcps, int sfd,
                       const struct rlite_msg_base *b_req)
{
    struct rl_cmsg_ipcp_dft_set *req = (struct rl_cmsg_ipcp_dft_set *)b_req;
    struct rlite_msg_base_resp resp;
    struct uipcp *uipcp;

    resp.result = RLITE_ERR; /* Report failure by default. */

    uipcp = uipcp_get_by_name(uipcps, &req->ipcp_name);
    if (!uipcp) {
        goto out;
    }

    if (uipcp->ops.dft_set) {
        resp.result = uipcp->ops.dft_set(uipcp, req);
    }

    uipcp_put(uipcps, uipcp->ipcp_id);

out:
    return rlite_conf_response(sfd, RLITE_MB(req), &resp);
}

static int
rlite_conf_ipcp_rib_show(struct uipcps *uipcps, int sfd,
                        const struct rlite_msg_base *b_req)
{
    struct rl_cmsg_ipcp_rib_show_req *req =
                    (struct rl_cmsg_ipcp_rib_show_req *)b_req;
    struct rl_cmsg_ipcp_rib_show_resp resp;
    struct uipcp *uipcp;
    int ret;

    resp.result = RLITE_ERR; /* Report failure by default. */
    resp.dump = NULL;

    uipcp = uipcp_get_by_name(uipcps, &req->ipcp_name);
    if (!uipcp) {
        goto out;
    }

    if (uipcp->ops.rib_show) {
        resp.dump = uipcp->ops.rib_show(uipcp);
        if (resp.dump) {
            resp.result = RLITE_SUCC;
        }
    }

    uipcp_put(uipcps, uipcp->ipcp_id);

out:
    resp.msg_type = RLITE_CFG_IPCP_RIB_SHOW_RESP;
    resp.event_id = req->event_id;

    ret = rlite_msg_write_fd(sfd, RLITE_MB(&resp));

    if (resp.dump) {
        free(resp.dump);
    }

    return ret;
}

typedef int (*rlite_req_handler_t)(struct uipcps *uipcps, int sfd,
                                   const struct rlite_msg_base * b_req);

/* The table containing all application request handlers. */
static rlite_req_handler_t rlite_config_handlers[] = {
    [RLITE_CFG_IPCP_REGISTER] = rlite_conf_ipcp_register,
    [RLITE_CFG_IPCP_ENROLL] = rlite_conf_ipcp_enroll,
    [RLITE_CFG_IPCP_DFT_SET] = rlite_conf_ipcp_dft_set,
    [RLITE_CFG_IPCP_RIB_SHOW_REQ] = rlite_conf_ipcp_rib_show,
    [RLITE_CFG_MSG_MAX] = NULL,
};

struct worker_info {
    struct uipcps *uipcps;
    int cfd;
};

static void *
worker_fn(void *opaque)
{
    struct worker_info *wi = opaque;
    struct rlite_msg_base *req;
    char serbuf[4096];
    char msgbuf[4096];
    int ret;
    int n;

    PD("Worker %p started\n", wi);

    /* Read the request message in serialized form. */
    n = read(wi->cfd, serbuf, sizeof(serbuf));
    if (n < 0) {
        PE("read() error [%d]\n", n);
    }

    /* Deserialize into a formatted message. */
    ret = deserialize_rlite_msg(rlite_conf_numtables, RLITE_CFG_MSG_MAX,
            serbuf, n, msgbuf, sizeof(msgbuf));
    if (ret) {
        PE("deserialization error [%d]\n", ret);
    }

    /* Lookup the message type. */
    req = RLITE_MB(msgbuf);
    if (rlite_config_handlers[req->msg_type] == NULL) {
        struct rlite_msg_base_resp resp;

        PE("Invalid message received [type=%d]\n",
                req->msg_type);
        resp.msg_type = RLITE_CFG_BASE_RESP;
        resp.event_id = req->event_id;
        resp.result = RLITE_ERR;
        rlite_msg_write_fd(wi->cfd, RLITE_MB(&resp));
    } else {
        /* Valid message type: handle the request. */
        ret = rlite_config_handlers[req->msg_type](wi->uipcps, wi->cfd, req);
        if (ret) {
            PE("Error while handling message type [%d]\n",
                    req->msg_type);
        }
    }

    rlite_msg_free(rlite_conf_numtables, RLITE_CFG_MSG_MAX, req);

    /* Close the connection. */
    close(wi->cfd);

    free(wi);

    PD("Worker %p stopped\n", wi);

    return NULL;
}

/* Unix server thread to manage configuration requests. */
static int
unix_server(struct uipcps *uipcps)
{
    for (;;) {
        struct sockaddr_un client_address;
        socklen_t client_address_len = sizeof(client_address);
        struct worker_info *wi;
        pthread_t wth;
        int ret;

        wi = malloc(sizeof(*wi));
        wi->uipcps = uipcps;

        /* Accept a new client and create a thread to serve it. */
        wi->cfd = accept(uipcps->lfd, (struct sockaddr *)&client_address,
                         &client_address_len);

        ret = pthread_create(&wth, NULL, worker_fn, wi);
        if (ret) {
            PE("pthread_create() failed [%d]\n", errno);
            close(wi->cfd);
            free(wi);
        }
    }

    return 0;
}

static int
uipcps_ipcp_update(struct rlite_evloop *loop,
                   const struct rlite_msg_base *b_resp,
                   const struct rlite_msg_base *b_req)
{
    struct uipcps *uipcps = container_of(loop, struct uipcps, loop);
    struct rl_kmsg_ipcp_update *upd = (struct rl_kmsg_ipcp_update *)b_resp;
    struct uipcp *uipcp;
    int ret = -1;

    switch (upd->update_type) {
        case RLITE_UPDATE_ADD:
            if (upd->dif_type && type_has_uipcp(upd->dif_type)) {
                /* We only care about IPCP with userspace implementation. */
                ret = uipcp_add(uipcps, upd->ipcp_id, upd->dif_type);
            }
            break;

        case RLITE_UPDATE_DEL:
            pthread_mutex_lock(&uipcps->lock);
            uipcp = uipcp_lookup(uipcps, upd->ipcp_id);
            pthread_mutex_unlock(&uipcps->lock);
            if (uipcp) {
                /* Track all the unregistrations of the destroyed IPCP in
                 * the persistent registrations list. */
                track_ipcp_unregistration(uipcps, upd->ipcp_id);
                ret = uipcp_put(uipcps, upd->ipcp_id);

            } else {
                /* This is an IPCP with no userspace implementation. */
                ret = 0;
            }
            break;

        default:
            ret = 0;
            break;
    }

    if (ret) {
        PE("IPCP update synchronization failed\n");
    }

    uipcps_print(uipcps);

    return 0;
}

static void
process_persistence_file(struct uipcps *uipcps)
{
    /* Read the persistent IPCP registration file into
     * the ipcps_registrations list. */
    FILE *fpreg = fopen(RLITE_PERSISTENCE_FILE, "r");
    char line[4096];

    if (!fpreg) {
        PD("Persistence file %s not found\n", RLITE_PERSISTENCE_FILE);
        return;
    }

    PD("Persistence file %s opened\n", RLITE_PERSISTENCE_FILE);

    while (fgets(line, sizeof(line), fpreg)) {
        char *s0 = NULL;
        char *s1 = NULL;
        char *s2 = NULL;
        char *s3 = NULL;
        char *s4 = NULL;

        s0 = strchr(line, '\n');
        if (s0) {
            *s0 = '\0';
        }

        s0 = strtok(line, " ");
        s1 = strtok(0, " ");
        s2 = strtok(0, " ");
        s3 = strtok(0, " ");
        s4 = strtok(0, " ");

        if (strncmp(s0, "REG", 3) == 0) {
            /* Redo an ipcp registration. */
            struct rl_cmsg_ipcp_register msg;
            int ret = 0;

            if (!s1 || !s2) {
                PE("Invalid REG line");
                continue;
            }

            msg.reg = 1;
            ret |= rina_name_from_string(s2, &msg.ipcp_name);
            msg.dif_name = strdup(s1);
            if (ret || !msg.dif_name) {
                PE("Out of memory\n");
                rlite_msg_free(rlite_conf_numtables, RLITE_CFG_MSG_MAX,
                               RLITE_MB(&msg));
                continue;
            }

            ret = ipcp_register(uipcps, &msg);
            PI("Automatic re-registration for %s --> %s\n",
                    s2, (ret == RLITE_SUCC) ? "DONE" : "FAILED");
            rlite_msg_free(rlite_conf_numtables, RLITE_CFG_MSG_MAX,
                           RLITE_MB(&msg));

        } else if (strncmp(s0, "ENR", 3) == 0) {
            /* Redo an enrollment. */
            struct rl_cmsg_ipcp_enroll msg;
            int ret = 0;

            if (!s1 || !s2 || !s3 || !s4) {
                PE("Invalid ENR line");
                continue;
            }

            ret |= rina_name_from_string(s2, &msg.ipcp_name);
            ret |= rina_name_from_string(s3, &msg.neigh_name);
            msg.dif_name = strdup(s1);
            msg.supp_dif_name = strdup(s4);
            if (ret || !msg.dif_name || !msg.supp_dif_name) {
                PE("Out of memory\n");
                rlite_msg_free(rlite_conf_numtables, RLITE_CFG_MSG_MAX,
                               RLITE_MB(&msg));
                continue;
            }

            ret = ipcp_enroll(uipcps, &msg);
            PI("Automatic re-enrollment for %s in DIF %s --> %s\n", s2, s1,
               (ret == RLITE_SUCC) ? "DONE" : "FAILED");
            rlite_msg_free(rlite_conf_numtables, RLITE_CFG_MSG_MAX,
                           RLITE_MB(&msg));
        }
    }

    fclose(fpreg);
}

static int
uipcps_update(struct uipcps *uipcps)
{
    struct rl_ipcp *rl_ipcp;
    int ret = 0;

    ret = rl_evloop_init(&uipcps->loop, ctrl_dev_name, NULL, 0);
    if (ret) {
        return ret;
    }

    rl_evloop_set_handler(&uipcps->loop, RLITE_KER_IPCP_UPDATE,
                          uipcps_ipcp_update);

    rl_ctrl_ipcps_print(&uipcps->loop.ctrl);

    /* Create an userspace IPCP for each existing IPCP. */
    pthread_mutex_lock(&uipcps->loop.lock);
    list_for_each_entry(rl_ipcp, &uipcps->loop.ctrl.ipcps, node) {
        if (type_has_uipcp(rl_ipcp->dif_type)) {
            ret = uipcp_add(uipcps, rl_ipcp->ipcp_id,
                            rl_ipcp->dif_type);
            if (ret) {
                pthread_mutex_unlock(&uipcps->loop.lock);
                return ret;
            }
        }
    }
    pthread_mutex_unlock(&uipcps->loop.lock);

    uipcps_print(uipcps);

    process_persistence_file(uipcps);

    return 0;
}

static void
dump_persistence_file(struct uipcps *uipcps)
{
    FILE *fpreg = fopen(RLITE_PERSISTENCE_FILE, "w");
    struct registered_ipcp *ripcp;
    struct uipcp *uipcp;

    if (!fpreg) {
        PE("Cannot open persistence file (%s)\n",
                RLITE_PERSISTENCE_FILE);
        return;
    }

    /* Dump the ipcps_registrations list, so that
     * subsequent uipcps invocations can redo the registrations. */
    list_for_each_entry(ripcp, &uipcps->ipcps_registrations, node) {
        char *ipcp_s;

        ipcp_s = rina_name_to_string(&ripcp->ipcp_name);
        if (ipcp_s) {
            fprintf(fpreg, "REG %s %s\n", ripcp->dif_name, ipcp_s);
        } else {
            PE("Error in rina_name_to_string()\n");
        }
        if (ipcp_s) free(ipcp_s);
    }

    /* Dump to a file the list of enrolled ipcps for which we were initiator,
     * so that subsequent uipcps invocations can redo the registrations. */
    list_for_each_entry(uipcp, &uipcps->uipcps, node) {
        char *ipcp_s, *neigh_s;
        struct enrolled_neigh *en, *tmp;
        struct list_head neighs;

        if (!uipcp->ops.get_enrollment_targets) {
            continue;
        }

        if (uipcp->ops.get_enrollment_targets(uipcp, &neighs)) {
            PE("get_enrolled_neighs() failed for uipcp [%u]\n",
               uipcp->ipcp_id);
            continue;
        }

        list_for_each_entry_safe(en, tmp, &neighs, node) {
            ipcp_s = rina_name_to_string(&en->ipcp_name);
            neigh_s = rina_name_to_string(&en->neigh_name);

            if (ipcp_s && neigh_s) {
                fprintf(fpreg, "ENR %s %s %s %s\n", en->dif_name, ipcp_s,
                        neigh_s, en->supp_dif);

            } else {
                PE("Error in rina_name_to_string()\n");
            }

            if (ipcp_s) free(ipcp_s);
            if (neigh_s) free(neigh_s);
        }
    }

    fclose(fpreg);
}

static void
sigint_handler(int signum)
{
    struct uipcps *uipcps = &guipcps;

    pthread_mutex_lock(&uipcps->lock);
    dump_persistence_file(uipcps);
    pthread_mutex_unlock(&uipcps->lock);

    /* TODO Here we should free all the dynamically allocated memory
     * referenced by uipcps. */

    unlink(RLITE_UIPCPS_UNIX_NAME);
    exit(EXIT_SUCCESS);
}

static int
char_device_exists(const char *path)
{
    struct stat s;

    return stat(path, &s) == 0 && S_ISCHR(s.st_mode);
}

int main(int argc, char **argv)
{
    struct uipcps *uipcps = &guipcps;
    struct sockaddr_un server_address;
    struct sigaction sa;
    int ret;

    /* We require root permissions. */
    if (geteuid() != 0) {
        PE("uipcps daemon needs root permissions\n");
        return -1;
    }

    if (!char_device_exists(ctrl_dev_name)) {
        PE("Device %s not found\n", ctrl_dev_name);
        return -1;
    }

    if (!char_device_exists(io_dev_name)) {
        PE("Device %s not found\n", io_dev_name);
        return -1;
    }

    /* Open a Unix domain socket to listen to. */
    uipcps->lfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (uipcps->lfd < 0) {
        perror("socket(AF_UNIX)");
{'fraction_non_alphanumeric': 0.11575654152445962, 'fraction_numerical': 0.004266211604095563, 'mean_word_length': 2.2269640234948604, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 217, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5969472', 'n_tokens_mistral': 13310, 'n_tokens_neox': 12797, 'n_words': 2610}