Ejemplo n.º 1
0
void _lml_handle_signal_if_needed(void)
{
        int signo;

        if ( ! got_signal )
                return;

        signo = got_signal;
        got_signal = 0;

#if !((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
        if ( signo == SIGQUIT || signo == SIGUSR1 ) {
                handle_sigquit();
                return;
        }
#endif

        server_close();

        if ( config.lml_client )
                prelude_client_destroy(config.lml_client, PRELUDE_CLIENT_EXIT_STATUS_FAILURE);

        prelude_deinit();

#if !((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
        if ( signo == SIGHUP ) {
                prelude_log(PRELUDE_LOG_WARN, "signal %d received, restarting (%s).\n", signo, get_restart_string());
                handle_sighup();
        }
#endif

        prelude_log(PRELUDE_LOG_WARN, "signal %d received, terminating prelude-lml.\n", signo);
        exit(2);
}
Ejemplo n.º 2
0
static int _charset_convert(lml_charset_t *lc, const char *in, size_t inlen, char **out, size_t *outlen)
{
    char *target;
    size_t maxlen;
    UErrorCode status = U_ZERO_ERROR;

    maxlen = lc->max_char_size * inlen;
    if ( maxlen < inlen || maxlen + 1 < maxlen )
        return -1;

    *out = target = malloc(maxlen + 1);
    if ( ! *out ) {
        prelude_log(PRELUDE_LOG_ERR, "ICU: failed allocating %lu bytes.\n", maxlen);
        return -1;
    }

    ucnv_convertEx(lc->to, lc->from, &target, *out + maxlen + 1, &in, in + inlen, NULL, NULL, NULL, NULL, 0, TRUE, &status);
    if ( U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING ) {
        prelude_log(PRELUDE_LOG_ERR, "ICU: failed converting input to UTF-8: %s.\n", u_errorName(status));
        free(*out);
        return -1;
    }

    *outlen = target - *out;
    return 0;
}
Ejemplo n.º 3
0
/**
 * prelude_daemonize:
 * @lockfile: Filename to a lockfile.
 *
 * Puts caller in background.
 * If @lockfile is not NULL, a lock for this program is created.
 *
 * The lockfile is automatically unlinked on exit.
 *
 * Returns: 0 on success, -1 if an error occured.
 */
int prelude_daemonize(const char *lockfile)
{
        pid_t pid;
        int fd = 0, ret, i;

        if ( lockfile ) {
                ret = get_absolute_filename(lockfile);
                if ( ret < 0 )
                        return ret;
        }

#if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__
        prelude_log(PRELUDE_LOG_ERR, "Daemonize call unsupported in this environment.\n");
        pid = getpid();
#else
        pid = fork();
        if ( pid < 0 )
                return prelude_error_from_errno(errno);

        else if ( pid )
                _exit(0);
#endif

        if ( lockfile ) {
                fd = lockfile_get_exclusive(slockfile);
                if ( fd < 0 )
                        return fd;

                ret = lockfile_write_pid(fd, getpid());
                if ( ret < 0 )
                        return ret;
        }

#if !((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
        setsid();

        ret = chdir("/");
        if ( ret < 0 )
                prelude_log(PRELUDE_LOG_ERR, "could not change working directory to '/': %s.\n", strerror(errno));

        umask(0);

        fd = open("/dev/null", O_RDWR);
        if ( fd < 0 )
                return prelude_error_from_errno(errno);

        for ( i = 0; i <= 2; i++ ) {
                do {
                        ret = dup2(fd, i);
                } while ( ret < 0 && errno == EINTR );

                if ( ret < 0 )
                        return prelude_error_from_errno(errno);
        }

        close(fd);
#endif

        return 0;
}
Ejemplo n.º 4
0
static int handle_connection(server_generic_t *server)
{
        int ret, client;
        server_generic_client_t *cdata;

        cdata = calloc(1, server->clientlen);
        if ( ! cdata ) {
                prelude_log(PRELUDE_LOG_ERR, "memory exhausted.\n");
                return -1;
        }

        client = accept_connection(server, cdata);
        if ( client < 0 ) {
                free(cdata);
                return -1;
        }

        ret = setup_client_socket(server, cdata, client);
        if ( ret < 0 ) {
                free(cdata);
                close(client);
                return -1;
        }

        ret = server_generic_process_requests(server, cdata);
        if ( ret < 0 ) {
                prelude_log(PRELUDE_LOG_ERR, "queueing client FD for server logic processing failed.\n");
                prelude_io_close(cdata->fd);
                prelude_io_destroy(cdata->fd);
                free(cdata);
                return -1;
        }

        return 0;
}
Ejemplo n.º 5
0
/*
 * Only called if the memory dump of the file was modified,
 * will write the new content to filename 'filename'.
 */
static int sync_and_free_file_content(config_t *cfg)
{
        FILE *fd;
        unsigned int i;
        size_t ret, len;

        fd = fopen(cfg->filename, "w");
        if ( ! fd )
                return prelude_error_verbose(prelude_error_code_from_errno(errno), "could not open '%s' for writing: %s",
                                             cfg->filename, strerror(errno));

        for ( i = 0; i < cfg->elements; i++ ) {
                len = strlen(cfg->content[i]);

                ret = fwrite(cfg->content[i], 1, len, fd);
                if ( ret != len && ferror(fd) )
                        prelude_log(PRELUDE_LOG_ERR, "error writing content to '%s': %s", cfg->filename, strerror(errno));

                if ( i + 1 != cfg->elements ) {
                        ret = fwrite("\n", 1, 1, fd);
                        if ( ret != 1 && ferror(fd) )
                                prelude_log(PRELUDE_LOG_ERR, "error writing content to '%s': %s", cfg->filename, strerror(errno));
                }

                free(cfg->content[i]);
        }

        fclose(fd);
        free(cfg->content);

        return 0;
}
Ejemplo n.º 6
0
static int _charset_convert(lml_charset_t *lc, const char *in, size_t inlen, char **out, size_t *outlen)
{
    char *outp;
    size_t ret, olen, bkpolen;

    bkpolen = olen = inlen * lc->max_char_size;

    if ( olen + 1 < olen )
        return -1;

    *out = outp = malloc(olen + 1);
    if ( ! outp )
        return -1;

    ret = iconv(lc->conv, (char **) &in, &inlen, &outp, &olen);
    if ( ret == (size_t) -1 ) {
        free(*out);
        prelude_log(PRELUDE_LOG_ERR, "ICONV: failed converting string to UTF-8: %s.\n", strerror(errno));
        return -1;
    }

    *outlen = (bkpolen - olen);
    *outp = '\0';

    return 0;
}
Ejemplo n.º 7
0
int _prelude_log_set_abort_level_from_string(const char *level)
{
        size_t i;
        char *eptr;
        long lvalue;
        struct {
                const char *prefix;
                prelude_log_t level;
        } tbl[] = {
                { "CRIT",  PRELUDE_LOG_CRIT  },
                { "ERR",   PRELUDE_LOG_ERR   },
                { "WARN",  PRELUDE_LOG_WARN  },
                { "INFO",  PRELUDE_LOG_INFO  },
                { "DEBUG", PRELUDE_LOG_DEBUG }
        };

        prelude_return_val_if_fail(level != NULL, prelude_error(PRELUDE_ERROR_ASSERTION));

        for ( i = 0; i < sizeof(tbl) / sizeof(*tbl); i++ ) {
                if ( strncasecmp(tbl[i].prefix, level, strlen(tbl[i].prefix)) == 0 ) {
                        _prelude_log_set_abort_level(tbl[i].level);
                        return 0;
                }
        }

        lvalue = strtol(level, &eptr, 10);
        if ( eptr != (level + strlen(level)) || lvalue == LONG_MIN || lvalue == LONG_MAX ) {
                prelude_log(PRELUDE_LOG_WARN, "Invalid abort level specified: '%s'.\n", level);
                return -1;
        }

        _prelude_log_set_abort_level(lvalue);

        return 0;
}
Ejemplo n.º 8
0
static void print_stats(const char *prefix, struct timeval *end)
{
        double tdiv;

        tdiv = (end->tv_sec + (double) end->tv_usec / 1000000) - (start.tv_sec + (double) start.tv_usec / 1000000);

        prelude_log(PRELUDE_LOG_WARN, "%s%lu line processed in %.2f seconds (%.2f EPS), %lu alert emited.\n",
                    prefix, config.line_processed, tdiv, config.line_processed / tdiv, config.alert_count);
}
Ejemplo n.º 9
0
static int _charset_open(lml_charset_t **lc, const char *from)
{
    *lc = calloc(1, sizeof(**lc));
    if ( ! *lc ) {
        prelude_log(PRELUDE_LOG_ERR, "ICONV: memory exhausted.\n");
        return -1;
    }

    (*lc)->conv = iconv_open("UTF-8", from);
    if ( (*lc)->conv == (iconv_t) -1 ) {
        prelude_log(PRELUDE_LOG_ERR, "ICONV: couldn't open %s -> UTF-8 converter : %s.\n", from, strerror(errno));
        free(*lc);
        return -1;
    }

    (*lc)->max_char_size = 4; /* Warning: this is UTF-8 specific */

    return 0;
}
Ejemplo n.º 10
0
static int accept_connection(server_generic_t *server, server_generic_client_t *cdata)
{
        socklen_t addrlen;
        int ret, sock, on = 1;

        addrlen = sizeof(cdata->sa);

        sock = accept(server->sock, (struct sockaddr *) &cdata->sa, &addrlen);
        if ( sock < 0 ) {
                prelude_log(PRELUDE_LOG_ERR, "accept error: %s.\n", strerror(errno));
                return -1;
        }

        ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(int));
        if ( ret < 0 )
                prelude_log(PRELUDE_LOG_ERR, "could not set SO_KEEPALIVE socket option: %s.\n", strerror(errno));

        return sock;
}
Ejemplo n.º 11
0
static int _charset_detect(const char *in, size_t len, const char **charset, int *confidence)
{
    UCharsetDetector *csd;
    const UCharsetMatch *ucm;
    UErrorCode status = U_ZERO_ERROR;

    csd = ucsdet_open(&status);
    if ( U_FAILURE(status) ) {
        prelude_log(PRELUDE_LOG_ERR, "ICU: error opening character set detector: %s.\n", u_errorName(status));
        return -1;
    }

    ucsdet_setText(csd, in, len, &status);
    if( U_FAILURE(status) ) {
        prelude_log(PRELUDE_LOG_ERR, "ICU: error setting text for character set detection: %s.\n", u_errorName(status));
        goto error;
    }

    ucm = ucsdet_detect(csd, &status);
    if( U_FAILURE(status) ) {
        prelude_log(PRELUDE_LOG_ERR, "ICU: character set detection failed: %s.\n", u_errorName(status));
        goto error;
    }

    *confidence = ucsdet_getConfidence(ucm, &status);
    if ( U_FAILURE(status) ) {
        prelude_log(PRELUDE_LOG_ERR, "ICU: error retrieving character set confidence: %s.\n", u_errorName(status));
        goto error;
    }

    *charset = ucsdet_getName(ucm, &status);
    if ( U_FAILURE(status) ) {
        prelude_log(PRELUDE_LOG_ERR, "ICU: error retrieving character set name: %s.\n", u_errorName(status));
        goto error;
    }

    return 0;

error:
    ucsdet_close(csd);
    return -1;
}
Ejemplo n.º 12
0
void server_generic_log_client(server_generic_client_t *cnx, prelude_log_t priority, const char *fmt, ...)
{
        va_list ap;
        int ret = 0;
        prelude_string_t *out;
        char addr[128] = { 0 };

        prelude_string_new(&out);

        if ( ((struct sockaddr *) &cnx->sa)->sa_family == AF_UNIX )
                snprintf(addr, sizeof(addr), "unix");

        else {
                void *in_addr;
                const char *str;
                unsigned int port;

#ifdef HAVE_IPV6
                port = ntohs(cnx->sa.sin6_port);
#else
                port = ntohs(cnx->sa.sin_port);
#endif
                in_addr = prelude_sockaddr_get_inaddr((struct sockaddr *) &cnx->sa);
                if ( ! in_addr )
                        goto out;

                str = inet_ntop(((struct sockaddr *)&cnx->sa)->sa_family, in_addr, addr, sizeof(addr));
                if ( str )
                        snprintf(addr + strlen(addr), sizeof(addr) - strlen(addr), ":%u", port);
        }

        if ( cnx->ident && cnx->permission ) {
                ret = prelude_string_sprintf(out, " 0x%" PRELUDE_PRIx64, cnx->ident);
                if ( ret < 0 )
                        goto out;

                ret = prelude_connection_permission_to_string(cnx->permission, out);
        }

        ret = prelude_string_sprintf(out, "]: ");
        if ( ret < 0  )
                goto out;

        va_start(ap, fmt);
        ret = prelude_string_vprintf(out, fmt, ap);
        va_end(ap);

        prelude_log(priority, "[%s%s", addr, prelude_string_get_string(out));

    out:
        prelude_string_destroy(out);
}
Ejemplo n.º 13
0
static UConverter *icu_initialize_converter(const char *charset)
{
    UConverter *conv;
    UErrorCode status = U_ZERO_ERROR;

    conv = ucnv_open(charset, &status);
    if ( U_FAILURE(status) ) {
        prelude_log(PRELUDE_LOG_ERR, "ICU: couldn't open %s converter : %s.\n", charset, u_errorName(status));
        return NULL;
    }

    return conv;
}
Ejemplo n.º 14
0
static void handle_sighup(void)
{
        int ret;

        /*
         * Here we go !
         */
        ret = execvp(global_argv[0], global_argv);
        if ( ret < 0 ) {
                prelude_log(PRELUDE_LOG_ERR, "error restarting '%s': %s\n", global_argv[0], prelude_strerror(ret));
                return;
        }
}
Ejemplo n.º 15
0
static pcre_rule_container_t *create_rule_container(pcre_rule_t *rule)
{
        pcre_rule_container_t *rc;

        rc = calloc(1, sizeof(*rc));
        if ( ! rc ) {
                prelude_log(PRELUDE_LOG_ERR, "memory exhausted.\n");
                return NULL;
        }

        rc->rule = rule;
        rule->refcount++;

        return rc;
}
Ejemplo n.º 16
0
static int sg_bind_common(server_generic_t *server, unsigned int port)
{
        char out[128];
        void *in_addr;

#if ! ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
        fcntl(server->sock, F_SETFD, fcntl(server->sock, F_GETFD) | FD_CLOEXEC);

        if ( server->sa->sa_family == AF_UNIX )
                prelude_log(PRELUDE_LOG_INFO, "server started (listening on %s).\n",
                            ((struct sockaddr_un *) server->sa)->sun_path);
        else {
#endif
                in_addr = prelude_sockaddr_get_inaddr(server->sa);
                assert(in_addr);

                inet_ntop(server->sa->sa_family, in_addr, out, sizeof(out));
                prelude_log(PRELUDE_LOG_INFO, "server started (listening on %s port %u).\n", out, port);
#if ! ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
        }
#endif

        return 0;
}
Ejemplo n.º 17
0
static int _charset_open(lml_charset_t **lc, const char *from)
{
    *lc = calloc(1, sizeof(**lc));
    if ( ! *lc ) {
        prelude_log(PRELUDE_LOG_ERR, "ICU: memory exhausted.\n");
        return -1;
    }

    (*lc)->from = icu_initialize_converter(from);
    if ( ! (*lc)->from )
        return lml_charset_close(*lc);

    (*lc)->to = icu_initialize_converter("UTF-8");
    if ( ! (*lc)->to )
        return lml_charset_close(*lc);

    (*lc)->max_char_size = ucnv_getMaxCharSize((*lc)->to);

    return 0;
}
Ejemplo n.º 18
0
server_generic_t *server_generic_new(size_t clientlen, server_generic_accept_func_t *acceptf,
                                     server_generic_read_func_t *readf,
                                     server_generic_write_func_t *writef,
                                     server_generic_close_func_t *closef)
{
        server_generic_t *server;

        server = malloc(sizeof(*server));
        if ( ! server ) {
                prelude_log(PRELUDE_LOG_ERR, "memory exhausted.\n");
                return NULL;
        }

        server->sa = NULL;
        server->read = readf;
        server->write = writef;
        server->accept = acceptf;
        server->close = closef;
        server->clientlen = clientlen;

        return server;
}
Ejemplo n.º 19
0
int main(int argc, char **argv)
{
        int ret;
        ev_timer evt;
        struct timeval end;
        struct sigaction action;

        /*
         * Initialize libev.
         */
        ev_default_loop(EVFLAG_AUTO);

        /*
         * make sure we ignore sighup until acceptable.
         */
#if !((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
        action.sa_flags = 0;
        action.sa_handler = SIG_IGN;
        sigemptyset(&action.sa_mask);
        sigaction(SIGHUP, &action, NULL);
#endif

        memset(&start, 0, sizeof(start));
        memset(&end, 0, sizeof(end));

        prelude_init(&argc, argv);
        global_argv = argv;

        PRELUDE_PLUGIN_SET_PRELOADED_SYMBOLS();

        ret = prelude_option_new_root(&lml_root_optlist);
        if ( ret < 0 )
                return ret;

        ret = log_plugins_init(LOG_PLUGIN_DIR, lml_root_optlist);
        if (ret < 0)
                return ret;

        prelude_log_debug(1, "Initialized %d logs plugins.\n", ret);

        ret = lml_options_init(lml_root_optlist, argc, argv);
        if ( ret < 0 )
                exit(1);

        /*
         * setup signal handling
         */
        action.sa_flags = 0;
        sigemptyset(&action.sa_mask);
        action.sa_handler = sig_handler;

#ifdef SA_INTERRUPT
        action.sa_flags |= SA_INTERRUPT;
#endif

        sigaction(SIGTERM, &action, NULL);
        sigaction(SIGINT, &action, NULL);
        sigaction(SIGABRT, &action, NULL);
#if !((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
        sigaction(SIGUSR1, &action, NULL);
        sigaction(SIGQUIT, &action, NULL);
        sigaction(SIGHUP, &action, NULL);
#endif

        ret = file_server_start_monitoring();
        if ( ret < 0 && ! config.udp_nserver ) {
                prelude_log(PRELUDE_LOG_WARN, "No file or UDP server available for monitoring: terminating.\n");
                return -1;
        }

        if ( config.daemon_mode ) {
                prelude_daemonize(config.pidfile);
                if ( config.pidfile )
                        free(config.pidfile);

                ev_default_fork();
        }

        ev_timer_init(&evt, libev_timer_cb, 1, 1);
        ev_timer_start(&evt);

        /*
         * Whether we are using batch-mode or file notification, we need
         * to process the currently un-processed entry.
         */
        gettimeofday(&start, NULL);

        do {
                ret = file_server_read_once();
                prelude_timer_wake_up();
        } while ( ret > 0 );

        /*
         * if either FAM or UDP server is enabled, we use polling to know
         * if there are data available for reading. if batch_mode is set,
         * then we revert to reading every data at once.
         */
        if ( ! config.batch_mode )
                wait_for_event();
        else {
                gettimeofday(&end, NULL);

                /*
                 * only call prelude_client_destroy in case we are running in batch
                 * mode, causing an heartbeat to be sent to notice of a normal exit.
                 */
                if ( ! config.dry_run )
                        prelude_client_destroy(config.lml_client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);

                print_stats("", &end);
        }

        prelude_deinit();
        return 0;
}
Ejemplo n.º 20
0
static int read_option_list(prelude_msg_t *msg, prelude_option_t *opt, uint64_t *source_id)
{
        int ret;
        void *buf;
        uint8_t tag;
        const char *tmp = NULL;
        uint32_t dlen, tmpint = 0;
        prelude_option_t *newopt;

        if ( ! opt )
                return -1;

        while ( (ret = prelude_msg_get(msg, &tag, &dlen, &buf)) == 0 ) {

                switch (tag) {

                case PRELUDE_MSG_OPTION_START:
                        ret = prelude_option_new(opt, &newopt);
                        if ( ret < 0 )
                                break;

                        read_option_list(msg, newopt, source_id);
                        break;

                case PRELUDE_MSG_OPTION_END:
                        return 0;

                case PRELUDE_MSG_OPTION_VALUE:
                        ret = prelude_extract_characters_safe(&tmp, buf, dlen);
                        if ( ret < 0 )
                                return ret;

                        prelude_option_set_value(opt, tmp);
                        break;

                case PRELUDE_MSG_OPTION_NAME:
                        ret = prelude_extract_characters_safe(&tmp, buf, dlen);
                        if ( ret < 0 )
                                return ret;

                        prelude_option_set_longopt(opt, tmp);
                        break;

                case PRELUDE_MSG_OPTION_DESC:
                        ret = prelude_extract_characters_safe(&tmp, buf, dlen);
                        if ( ret < 0 )
                                return ret;

                        prelude_option_set_description(opt, tmp);
                        break;

                case PRELUDE_MSG_OPTION_HELP:
                        ret = prelude_extract_characters_safe(&tmp, buf, dlen);
                        if ( ret < 0 )
                                return ret;

                        prelude_option_set_help(opt, tmp);
                        break;

                case PRELUDE_MSG_OPTION_INPUT_VALIDATION:
                        ret = prelude_extract_characters_safe(&tmp, buf, dlen);
                        if ( ret < 0 )
                                return ret;

                        prelude_option_set_input_validation_regex(opt, tmp);
                        break;

                case PRELUDE_MSG_OPTION_HAS_ARG:
                        ret = prelude_extract_uint32_safe(&tmpint, buf, dlen);
                        if ( ret < 0 )
                                return ret;

                        prelude_option_set_has_arg(opt, tmpint);
                        break;

                case PRELUDE_MSG_OPTION_TYPE:
                        ret = prelude_extract_uint32_safe(&tmpint, buf, dlen);
                        if ( ret < 0 )
                                return ret;

                        prelude_option_set_type(opt, tmpint);
                        break;

                case PRELUDE_MSG_OPTION_INPUT_TYPE:
                        ret = prelude_extract_uint32_safe(&tmpint, buf, dlen);
                        if ( ret < 0 )
                                return ret;

                        prelude_option_set_input_type(opt, tmpint);
                        break;

                default:
                        /*
                         * for compatibility purpose, don't return an error on unknow tag.
                         */
                        prelude_log(PRELUDE_LOG_WARN, "unknown option tag %d.\n", tag);
                }
        }

        return 0;
}
Ejemplo n.º 21
0
static void tls_log_func(int level, const char *data)
{
        prelude_log(PRELUDE_LOG_INFO, "%s", data);
}
Ejemplo n.º 22
0
int prelude_option_recv_reply(prelude_msg_t *msg, uint64_t *source_id,
                              uint32_t *request_id, void **value)
{
        void *buf;
        uint8_t tag;
        uint32_t dlen;
        int ret, type = -1;

        *value = NULL;

        while ( (ret = prelude_msg_get(msg, &tag, &dlen, &buf)) == 0 ) {

                switch (tag) {
                case PRELUDE_MSG_OPTION_HOP:
                        break;

                case PRELUDE_MSG_OPTION_REQUEST_ID:
                        type = PRELUDE_OPTION_REPLY_TYPE_SET;

                        ret = prelude_extract_uint32_safe(request_id, buf, dlen);
                        if ( ret < 0 )
                                return ret;

                        break;

                case PRELUDE_MSG_OPTION_VALUE:
                        type = PRELUDE_OPTION_REPLY_TYPE_GET;

                        ret = prelude_extract_characters_safe((const char **) value, buf, dlen);
                        if ( ret < 0 )
                                return ret;
                        break;

                case PRELUDE_MSG_OPTION_ERROR:
                        type = PRELUDE_OPTION_REPLY_TYPE_ERROR;
                        if ( ! dlen ) {
                                *value = "No error message";
                                break;
                        }

                        ret = prelude_extract_characters_safe((const char **) value, buf, dlen);
                        if ( ret < 0 )
                                return ret;
                        break;

                case PRELUDE_MSG_OPTION_TARGET_ID:
                        if ( dlen % sizeof(uint64_t) != 0 || dlen < (2 * sizeof(uint64_t)) )
                                return -1;

                        *source_id = prelude_extract_uint64((unsigned char *) buf + (dlen - sizeof(uint64_t)));
                        break;

                case PRELUDE_MSG_OPTION_LIST:
                        type = PRELUDE_OPTION_REPLY_TYPE_LIST;

                        ret = prelude_option_new(NULL, (prelude_option_t **) value);
                        if ( ret < 0 )
                                return ret;

                        ret = read_option_list(msg, *value, NULL);
                        if ( ret < 0 )
                                return ret;
                        break;

                default:
                        prelude_log(PRELUDE_LOG_WARN, "unknown option tag %d.\n", tag);
                }
        }

        return type;
}