/*-
 * Copyright (c) 2013 UPLEX Nils Goroll Systemoptimierung
 * Copyright (c) 2013 Otto Gmbh & Co KG
 * All rights reserved
 *
 * Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
 *
 * Portions adapted from varnishncsa.c from the Varnish project
 * Copyright (c) 2006 Verdens Gang AS
 * Copyright (c) 2006-2011 Varnish Software AS
 * Author: Anders Berg <andersb@vgnett.no>
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
 * Author: Tollef Fog Heen <tfheen@varnish-software.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * Obtain log data from the shared memory log and output single-line
 * events to an output stream. By default just like varnishncsa, but:
 *
 * - output lines may correspond to both client and backend transactions
 * - also events on the pseudo fd 0, such as backend health checks, may be
 *   logged
 * - output formats are defined for client, backend and "zero" events
 * - some additional formatting tags are available
 * - the internal architecture is designed to ensure that the VSL-reading
 *   process keeps pace with varnishd writing to VSL under heavy loads
 */

#include "config.h"

#include <ctype.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <pthread.h>
#include <syslog.h>

#include "compat/daemon.h"

#include "vsb.h"
#include "vpf.h"
#include "vqueue.h"

#include "libvarnish.h"
#include "vsl.h"
#include "vre.h"
#include "varnishapi.h"
#include "miniobj.h"

#include "varnishevent.h"

#define DEFAULT_CONFIG "/etc/varnishevent.conf"

typedef enum {
    FD_EMPTY = 0,
    FD_OPEN
} fd_state_e;

typedef struct fd_t {
    unsigned magic;
#define FD_MAGIC 0xa06b2960
    logline_t *ll;
    fd_state_e state;
    double t;
    VTAILQ_ENTRY(fd_t) insert_list;
} fd_t;

static fd_t *fd_tbl;

VTAILQ_HEAD(insert_head_s, fd_t);
static struct insert_head_s insert_head = VTAILQ_HEAD_INITIALIZER(insert_head);

static unsigned seen = 0, open = 0, submitted = 0, not_logged = 0, occ_hi = 0,
    len_hi = 0, waits = 0, fd_overflows = 0, len_overflows = 0,
    hdr_overflows = 0, expired = 0, spec_mismatches = 0, wrong_tags = 0;

/* Hack, because we cannot have #ifdef in the macro definition SIGDISP */
#define _UNDEFINED(SIG) ((#SIG)[0] == 0)
#define UNDEFINED(SIG) _UNDEFINED(SIG)

#define SIGDISP(SIG, action)						\
    do { if (UNDEFINED(SIG)) break;					\
        if (sigaction((SIG), (&action), NULL) != 0)			\
            LOG_Log(LOG_ALERT,						\
                "Cannot install handler for " #SIG ": %s",		\
                strerror(errno));					\
    } while(0)

static struct sigaction dump_action, terminate_action, reopen_action,
    stacktrace_action, ignore_action;

static volatile sig_atomic_t reopen = 0, term = 0;

struct VSM_data *vd;

static int m_flag = 0;
static int cb_flag = 0;
static int z_flag = 0;

/* Local freelist */
static struct freehead_s reader_freelist = 
    VSTAILQ_HEAD_INITIALIZER(reader_freelist);
static unsigned rdr_free = 0;

static int waiting = 0;

static char cli_config_filename[BUFSIZ] = "";

int
RDR_Waiting(void)
{
    return waiting;
}

void
RDR_Stats(void)
{
    LOG_Log(LOG_INFO, "Reader (%s): fd_max=%u seen=%u open=%u load=%.2f "
        "submitted=%u not_logged=%u occ_hi=%u waits=%u expired=%u free=%u "
        "len_hi=%u fd_overflows=%u len_overflows=%u hdr_overflows=%u "
        "spec_mismatches=%u wrong_tags=%u",
        waiting ? "waiting" : "running", config.max_fd, seen, open,
        100.0 * open / config.max_fd, submitted, not_logged, occ_hi, waits,
        expired, rdr_free, len_hi, fd_overflows, len_overflows, hdr_overflows,
        spec_mismatches, wrong_tags);
}

static inline void
signal_spscq_ready(void)
{
    if (WRT_Waiting()) {
        AZ(pthread_mutex_lock(&spscq_ready_lock));
        AZ(pthread_cond_signal(&spscq_ready_cond));
        AZ(pthread_mutex_unlock(&spscq_ready_lock));
    }
}

static inline logline_t
*take(void)
{
    struct logline_t *data;

    while (VSTAILQ_EMPTY(&reader_freelist)) {
        rdr_free = DATA_Take_Freelist(&reader_freelist);
        if (VSTAILQ_EMPTY(&reader_freelist)) {
            AZ(rdr_free);
            signal_spscq_ready();
            LOG_Log0(LOG_DEBUG, "Reader: waiting for free list");
            AZ(pthread_mutex_lock(&data_ready_lock));
            waiting = 1;
            waits++;
            if (!WRT_Waiting())
                AZ(pthread_cond_wait(&data_ready_cond, &data_ready_lock));
            waiting = 0;
            AZ(pthread_mutex_unlock(&data_ready_lock));
            rdr_free = DATA_Take_Freelist(&reader_freelist);
            LOG_Log(LOG_DEBUG, "Reader: took %u from free list", rdr_free);
        }
    }
    data = VSTAILQ_FIRST(&reader_freelist);
    VSTAILQ_REMOVE_HEAD(&reader_freelist, freelist);
    rdr_free--;
    return (data);
}

static inline void
submit(fd_t *entry)
{
    logline_t *lp;

    CHECK_OBJ_NOTNULL(entry, FD_MAGIC);
    assert(entry->state == FD_OPEN);
    lp = entry->ll;
    CHECK_OBJ_NOTNULL(lp, LOGLINE_MAGIC);
    
    assert(lp->state == DATA_DONE);
    data_open--;
    if ((m_flag && !VSL_Matched(vd, lp->bitmap))
        || (lp->spec && !(lp->spec & cb_flag))) {
        not_logged++;
        DATA_Clear_Logline(lp);
        rdr_free++;
        VSTAILQ_INSERT_TAIL(&reader_freelist, lp, freelist);
        return;
    }
    SPSCQ_Enq((void *) lp);
    signal_spscq_ready();
    MON_StatsUpdate(STATS_DONE);
    submitted++;
}

static inline void
fd_free(fd_t *entry)
{
    CHECK_OBJ_NOTNULL(entry, FD_MAGIC);
    VTAILQ_REMOVE(&insert_head, entry, insert_list);
    entry->state = FD_EMPTY;
    entry->ll = NULL;
    open--;
}

static inline void
expire(double t)
{
    fd_t *entry;
    double insert_time = 0.0;

    while ((entry = VTAILQ_FIRST(&insert_head))) {
        CHECK_OBJ_NOTNULL(entry, FD_MAGIC);
        assert(entry->state == FD_OPEN);

        if (entry->t > t)
            return;

        assert(insert_time <= entry->t);
        insert_time = entry->t;

        logline_t *ll = entry->ll;
        CHECK_OBJ_NOTNULL(ll, LOGLINE_MAGIC);
        ll->incomplete = 1;
        ll->state = DATA_DONE;
        submit(entry);
        fd_free(entry);
        expired++;
    }
}

#define CB(spec) (C(spec) ? 'c' : B(spec) ? 'b' : '-')

static inline void
spec_mismatch(enum VSL_tag_e tag, unsigned saw, unsigned saved,
    const char *ptr, unsigned len)
{
    LOG_Log(LOG_ALERT, "client/backend spec mismatch, saw '%c', "
                       "was '%c', DATA DISCARDED: %s [%.*s]",
        CB(saw), CB(saved), VSL_tags[tag], len, ptr);
    spec_mismatches++;
}

static inline void
collect(struct logline_t *lp, enum VSL_tag_e tag, unsigned spec,
    const char *ptr, unsigned len)
{
    hdr_t *hdr = NULL;
    record_t *rec = NULL;
    unsigned *max;

    if (tag2idx[tag] == -1) {
        LOG_Log(LOG_ALERT, "Read unexpected tag %s, DATA DISCARDED: [%.*s]",
            VSL_tags[tag], len, ptr);
        wrong_tags++;
        return;
    }
    
    switch (tag) {
    case SLT_BackendReuse:
    case SLT_BackendClose:
        lp->state = DATA_DONE;
    case SLT_BackendOpen:
    case SLT_BackendXID:
    case SLT_TxRequest:
    case SLT_TxURL:
    case SLT_RxStatus:
    case SLT_BackendReq:
    case SLT_Fetch_Hdr:
    case SLT_Fetch_Body:
        if (!lp->spec)
            lp->spec = VSL_S_BACKEND;
        else if (lp->spec != VSL_S_BACKEND) {
            spec_mismatch(tag, VSL_S_BACKEND, lp->spec, ptr, len);
            return;
        }
        break;
        
    case SLT_ReqEnd:
    case SLT_SessionClose:
    case SLT_StatSess:
        lp->state = DATA_DONE;
    case SLT_SessionOpen:
    case SLT_ReqStart:
    case SLT_RxRequest:
    case SLT_RxURL:
    case SLT_TxStatus:
    case SLT_VCL_Log:
    case SLT_VCL_call:
    case SLT_Backend:
    case SLT_FetchError:
        if (!lp->spec)
            lp->spec = VSL_S_CLIENT;
        else if (lp->spec != VSL_S_CLIENT) {
            spec_mismatch(tag, VSL_S_CLIENT, lp->spec, ptr, len);
            return;
        }
        break;

    default:
        break;
    }

    if (spec & VSL_S_BACKEND || spec & VSL_S_CLIENT) {
        if (!lp->spec)
            lp->spec = spec;
        else if (lp->spec != spec) {
            spec_mismatch(tag, spec, lp->spec, ptr, len);
            return;
        }
    }

    if (lp->spec && !(lp->spec & cb_flag))
        return;

    if (tag == SLT_VCL_Log && memchr(ptr, ':', len) == NULL)
        return;

    if (len > len_hi)
        len_hi = len;
    
    if (len > config.max_reclen) {
        LOG_Log(LOG_ALERT,
            "Data length %u exceeds max %u, DATA TRUNCATED: %s [%.*s]",
            len, config.max_reclen, VSL_tags[tag], len, ptr);
        len_overflows++;
        len = config.max_reclen;
    }

    if (tag == SLT_RxHeader) {
        hdr = lp->rx_headers;
        max = &config.max_headers;
    }
    else if (tag == SLT_TxHeader) {
        hdr = lp->tx_headers;
        max = &config.max_headers;
    }
    else if (tag == SLT_VCL_Log) {
        hdr = lp->vcl_log;
        max = &config.max_vcl_log;
    }
    else if (tag == SLT_VCL_call) {
        hdr = lp->vcl_call;
        max = &config.max_vcl_call;
    }

    if (hdr != NULL) {
        if (hdr->nrec < *max)
            rec = &hdr->record[hdr->nrec++];
        else {
            LOG_Log(LOG_ALERT,
                "Number of records for %s exceeds max %u, "
                "DATA DISCARDED: [%.*s]",
                VSL_tags[tag], *max, len, ptr);
            hdr_overflows++;
            return;
        }
    }
    else
        rec = &lp->tag[tag2idx[tag]];

    CHECK_OBJ_NOTNULL(rec, RECORD_MAGIC);
    memcpy(rec->data, ptr, len);
    rec->len = len;
}

static int
event(void *priv, enum VSL_tag_e tag, unsigned fd,
    unsigned len, unsigned spec, const char *ptr, uint64_t bitmap)
{
    struct logline_t *lp = NULL;

    static double last_housekeep = 0.0;

    (void) priv;

    if (term && open == 0)
        return 1;

    LOG_Log(LOG_DEBUG, "Data: [%u %s %c %.*s]", fd, VSL_tags[tag],
        C(spec) ? 'c' : B(spec) ? 'b' : '-', len, ptr);
    
    if (fd > config.max_fd) {
        LOG_Log(LOG_ALERT, "fd %u exceeds max %u: %s [%.*s]", fd, config.max_fd,
            VSL_tags[tag], len, ptr);
        fd_overflows++;
        return reopen;
    }
    
    if (fd == 0 && !z_flag)
        return reopen;

    if (fd_tbl[fd].state != FD_EMPTY) {
        lp = fd_tbl[fd].ll;
        CHECK_OBJ_NOTNULL(lp, LOGLINE_MAGIC);
        assert(lp->state == DATA_OPEN);
    }
    else if (!term) {
        double t;
        
        AZ(fd_tbl[fd].ll);
        lp = take();
        CHECK_OBJ_NOTNULL(lp, LOGLINE_MAGIC);
        assert(lp->state == DATA_EMPTY);
        AZ(lp->incomplete);
        AZ(lp->bitmap);
        AZ(lp->spec);
        AZ(lp->t);
        assert(lp->rx_headers == NULL || lp->rx_headers->nrec == 0);
        assert(lp->tx_headers == NULL || lp->tx_headers->nrec == 0);
        assert(lp->vcl_log == NULL || lp->vcl_log->nrec == 0);
        assert(lp->vcl_call == NULL || lp->vcl_call->nrec == 0);

        t = TIM_real();
        fd_tbl[fd].ll = lp;
        lp->state = DATA_OPEN;
        lp->t = t;
        data_open++;
        if (data_open + data_done > data_occ_hi)
            data_occ_hi = data_open + data_done;
        fd_tbl[fd].state = FD_OPEN;
        fd_tbl[fd].t = t;
        VTAILQ_INSERT_TAIL(&insert_head, &fd_tbl[fd], insert_list);
        seen++;
        open++;
        if (open > occ_hi)
            occ_hi = open;

        if (t - last_housekeep > config.housekeep_interval) {
            expire(t - config.ttl);
            last_housekeep = t;
        }
    }

    if (lp) {
        collect(lp, tag, spec, ptr, len);
        
        lp->bitmap |= bitmap;
        if (fd == 0)
            lp->state = DATA_DONE;

        if (lp->state == DATA_DONE) {
            submit(&fd_tbl[fd]);
            fd_free(&fd_tbl[fd]);
        }
    }
    
    return reopen;
}

/*--------------------------------------------------------------------*/

static void
sigreopen(int sig)
{
    LOG_Log(LOG_WARNING, "Received signal %d (%s), reopening output",
        sig, strsignal(sig));
    reopen = 1;
}

static void
dump(int sig)
{
    (void) sig;
    CONF_Dump();
    DATA_Dump();
}

static void
terminate(int sig)
{
    (void) sig;
    term = 1;
}

static void
assert_fail(const char *func, const char *file, int line, const char *cond,
    int err, int xxx)
{
    (void) xxx;
    
    LOG_Log(LOG_ALERT, "Condition (%s) failed in %s(), %s line %d",
        cond, func, file, line);
    if (err)
        LOG_Log(LOG_ALERT, "errno = %d (%s)", err, strerror(err));
    abort();
}

/*--------------------------------------------------------------------*/

static void
read_default_config(void) {
    if (access(DEFAULT_CONFIG, F_OK) == 0) {
        if (access(DEFAULT_CONFIG, R_OK) != 0) {
            perror(DEFAULT_CONFIG);
            exit(EXIT_FAILURE);
        }
        printf("Reading config from %s\n", DEFAULT_CONFIG);
        if (CONF_ReadFile(DEFAULT_CONFIG) != 0)
            exit(EXIT_FAILURE);
    }
}

static void
usage(void)
{

    fprintf(stderr,
        "usage: varnishevent %s [-aDVg] [-n varnish_name] "
        "[-G configfile] [-P pidfile] [-w outputfile]\n", VSL_USAGE);
    exit(1);
}

int
main(int argc, char *argv[])
{
    int c, errnum, finite = 0, a_flag = 0, g_flag = 0, D_flag = 0,
        format_flag = 0;
    const char *P_arg = NULL, *w_arg = NULL;
    char scratch[BUFSIZ];
    struct vpf_fh *pfh = NULL;

    vd = VSM_New();
    VSL_Setup(vd);

    CONF_Init();
    read_default_config();

    while ((c = getopt(argc, argv, VSL_ARGS "aDP:Vw:fF:gG:")) != -1) {
        switch (c) {
        case 'a':
            a_flag = 1;
            break;
        case 'f':
            if (format_flag) {
                fprintf(stderr, "-f and -F can not be combined\n");
                exit(1);
            }
            strcpy(config.cformat, ALT_CFORMAT);
            format_flag = 1;
            break;
        case 'F':
            if (format_flag) {
                fprintf(stderr, "-f and -F can not be combined\n");
                exit(1);
            }
            format_flag = 1;
            strcpy(config.cformat, optarg);
            break;
        case 'D':
            D_flag = 1;
            break;
        case 'P':
            P_arg = optarg;
            break;
        case 'V':
            VCS_Message("varnishevent");
            exit(0);
        case 'w':
            w_arg = optarg;
            break;
        case 'g':
            g_flag = 1;
            break;
        case 'G':
            strcpy(cli_config_filename, optarg);
            break;
        case 'b':
        case 'i':
        case 'I':
        case 'c':
            fprintf(stderr, "-%c is not valid for varnishevent\n", c);
            exit(1);
            break;
        case 'm':
            m_flag = 1; /* Fall through */
        default:
            if (c == 'r') {
                finite = 1;
                strcpy(config.varnish_bindump, optarg);
            }
            if (VSL_Arg(vd, c, optarg) > 0)
                break;
            usage();
        }
    }

    if (! EMPTY(cli_config_filename)) {
        printf("Reading config from %s\n", cli_config_filename);
        if (CONF_ReadFile(cli_config_filename) != 0) {
            fprintf(stderr, "Error reading config from %s\n",
                cli_config_filename);
            exit(EXIT_FAILURE);
        }
    }

    if (VSL_Open(vd, 1))
        exit(1);

    if (P_arg && (pfh = VPF_Open(P_arg, 0644, NULL)) == NULL) {
        perror(P_arg);
        exit(1);
    }

    if (D_flag && varnish_daemon(0, 0) == -1) {
        perror("daemon()");
        if (pfh != NULL)
            VPF_Remove(pfh);
        exit(1);
    }

    terminate_action.sa_handler = terminate;
    AZ(sigemptyset(&terminate_action.sa_mask));
    terminate_action.sa_flags &= ~SA_RESTART;

    dump_action.sa_handler = dump;
    AZ(sigemptyset(&dump_action.sa_mask));
    dump_action.sa_flags |= SA_RESTART;

    reopen_action.sa_handler = sigreopen;
    AZ(sigemptyset(&reopen_action.sa_mask));
    reopen_action.sa_flags |= SA_RESTART;

    stacktrace_action.sa_handler = HNDL_Abort;

    ignore_action.sa_handler = SIG_IGN;
    default_action.sa_handler = SIG_DFL;

    HNDL_Init(argv[0]);

    /* Install signal handlers */
#include "signals.h"

    if (pfh != NULL)
        VPF_Write(pfh);
    
    if (w_arg)
        strcpy(config.output_file, w_arg);
    if (!EMPTY(config.output_file))
        SIGDISP(SIGHUP, reopen_action);
    else
        SIGDISP(SIGHUP, ignore_action);
    if (a_flag)
        config.append = 1;

    if (LOG_Open(config.syslog_ident) != 0) {
        exit(EXIT_FAILURE);
    }
    if (g_flag)
        LOG_SetLevel(LOG_DEBUG);

    LOG_Log(LOG_INFO, "initializing (%s)", VCS_version);

    VAS_Fail = assert_fail;

    if (FMT_Init(scratch) != 0) {
        LOG_Log(LOG_ALERT, "Error in output formats: %s", scratch);
        exit(EXIT_FAILURE);
    }

    if (!EMPTY(config.varnish_bindump))
        LOG_Log(LOG_INFO, "Reading from file: %s", config.varnish_bindump);
    else {
        strcpy(scratch, VSM_Name(vd));
        if (EMPTY(scratch))
            LOG_Log0(LOG_INFO, "Reading default varnish instance");
        else
            LOG_Log(LOG_INFO, "Reading varnish instance %s", scratch);
    }

    strcpy(scratch, FMT_Get_i_Arg());
    if (EMPTY(scratch)) {
        LOG_Log0(LOG_ALERT, "Not configured to read any log data, exiting");
        exit(EXIT_FAILURE);
    }
    assert(VSL_Arg(vd, 'i', scratch) > 0);
    LOG_Log(LOG_INFO, "Reading SHM tags: %s", scratch);
    
    if (!EMPTY(config.cformat))
        cb_flag |= VSL_S_CLIENT;
    if (!EMPTY(config.bformat))
        cb_flag |= VSL_S_BACKEND;
    if (!EMPTY(config.zformat))
        z_flag = 1;

    if ((errnum = DATA_Init()) != 0) {
        LOG_Log(LOG_ALERT, "Cannot init data table: %s\n",
                strerror(errnum));
        exit(EXIT_FAILURE);
    }

    fd_tbl = (fd_t *) calloc(config.max_fd, sizeof(fd_t));
    if (fd_tbl == NULL) {
        LOG_Log(LOG_ALERT, "Cannot init fd table: %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }
    for (int k = 0; k < config.max_fd; k++) {
        fd_tbl[k].magic = FD_MAGIC;
        fd_tbl[k].ll = NULL;
        fd_tbl[k].state = FD_EMPTY;
    }

    AZ(pthread_cond_init(&data_ready_cond, NULL));
    AZ(pthread_mutex_init(&data_ready_lock, NULL));
    AZ(pthread_cond_init(&spscq_ready_cond, NULL));
    AZ(pthread_mutex_init(&spscq_ready_lock, NULL));

    if (config.monitor_interval > 0)
        MON_Start();
    else
        LOG_Log0(LOG_INFO, "Monitoring thread not running");

    if ((errnum = WRT_Init()) != 0) {
        LOG_Log(LOG_ALERT, "Cannot init writer thread: %s\n", strerror(errnum));
        exit(EXIT_FAILURE);
    }

    rdr_free = DATA_Take_Freelist(&reader_freelist);
    assert(!VSTAILQ_EMPTY(&reader_freelist));
    assert(rdr_free == config.max_data);
    
    WRT_Start();
    /* XXX: configure wrt_waits and sleep interval? */
    int wrt_waits = 0;
    while (!WRT_Running()) {
        if (wrt_waits++ > 10) {
            LOG_Log0(LOG_ALERT, "Writer thread not running, giving up");
            exit(EXIT_FAILURE);
        }
        TIM_sleep(1);
    }

    /* Main loop */
    term = 0;
    /* XXX: TERM not noticed until request received */
    while (VSL_Dispatch(vd, event, NULL) >= 0)
        if (term || finite)
            break;
        else if (reopen) {
            WRT_Reopen();
            reopen = 0;
        }
        else
            LOG_Log0(LOG_WARNING, "Log read interrupted, continuing");

    if (term)
        LOG_Log0(LOG_INFO, "Termination signal received");
    else if (!finite)
        LOG_Log0(LOG_WARNING, "Varnish log closed");
    
    WRT_Halt();
    SPSCQ_Shutdown();
    MON_Shutdown();
    FMT_Shutdown();
    AZ(pthread_cond_destroy(&data_ready_cond));
    AZ(pthread_mutex_destroy(&data_ready_lock));
    AZ(pthread_cond_destroy(&spscq_ready_cond));
    AZ(pthread_mutex_destroy(&spscq_ready_lock));
    LOG_Log0(LOG_INFO, "Exiting");
    LOG_Close();

    exit(EXIT_SUCCESS);
}
