/*
 * Copyright (c) 2001 Fenris, Inc.
 * All rights reserved.
 *
 * 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 THE 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.
 */

/*
 * File:  main.c
 *
 * Purpose:  This source file basically steals the main functionality that
 * was in engine.c  This was necessary b/c of choices made in ownership of
 * the parent thread (between frontend or backend).  We decided to give the
 * choice of running the backend without the gui, for use with populating
 * a database, or for tool automation.  That decision led to the following
 * implementation that grabs command line arguments and starts the appropriate
 * number of threads and/or processes.
 *
 */

#define _BSD_SOURCE 1

#include <fcntl.h>
#include <gtk/gtk.h>
#include <pcap.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#include "db.h"
#include "engine.h"
#include "globals.h"
#include "interface.h"
#include "realtime.h"
#include "cache_list.h"

#ifndef BINDIR
#error noinstalldirset
#define BINDIR "./"
#endif

pid_t fork_resolver(void);
void *start_gui(void *arg);


int main(int argc, char *argv[])
{
    pthread_attr_t       attr;
    pthread_mutexattr_t  mutex_attr;
    pthread_t            gui_thread;
    pthread_t            db_thread;
    pcap_t              *pcap;
    char                 ebuf[PCAP_ERRBUF_SIZE];
    char                *dev = NULL;
    bpf_u_int32          localnet;
    bpf_u_int32          netmask;
    struct bpf_program   fcode;
    struct db_info      *db;
    int                  option;
    char                *conffile = NULL;

    if ((opts = malloc(sizeof(Filter_Opts))) == NULL)
        die("malloc");

    opts->menu = all;
    opts->net_submenu.filter_on_src_addr = false;
    opts->net_submenu.filter_on_dst_addr = false;
    opts->db = FALSE;
    opts->gui = FALSE;

    gtk_set_locale();
    gtk_init(&argc, &argv);

    /* Get commandline options. */
    while ((option = getopt(argc, argv, "df:gi:")) != -1) {
        switch (option) {
        case 'f':
            conffile = optarg;
            /* FALLTHROUGH */
        case 'd':
            opts->db = TRUE;
            break;
        case 'g':
            opts->gui = TRUE;
        case 'i':
            dev = optarg;
            break;
        case '?':
        default:
            usage();
            /* NOTREACHED */
            break;
        }
    }
    argc -= optind;
    argv += optind;

    /* Default to running just the gui. */
    if (!(opts->db || opts->gui))
        opts->gui = TRUE;

    pthread_attr_init(&attr);
    pthread_mutexattr_init(&mutex_attr);
    pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);

    if ((filter_mutex = malloc(sizeof(pthread_mutexattr_t))) == NULL)
        die("malloc");
    pthread_mutex_init(filter_mutex, &mutex_attr);

    if (1) { /* Resolve hostnames? */
        init_cache_list(&dnscache);
        resolver_pid = fork_resolver();
    }

    if (opts->gui) { /* Running gui? */
        pthread_create(&gui_thread, &attr, start_gui, NULL);

        if ((net_mutex = malloc(sizeof(pthread_mutexattr_t))) == NULL)
            die("malloc");
        pthread_mutex_init(net_mutex, &mutex_attr);

        if ((net = malloc(sizeof(Network))) == NULL)
            die("malloc");
        init_net(net);
    }

    if (opts->db) { /* Running db? */
        STAILQ_INIT(&db_q);
        init_db(&db, conffile);

        pthread_create(&db_thread, &attr, db_engine, db);

        if ((db_mutex = malloc(sizeof(pthread_mutexattr_t))) == NULL)
            die("malloc");
        pthread_mutex_init(db_mutex, &mutex_attr);
    }

    if (dev == NULL) {
        if ((dev = pcap_lookupdev(ebuf)) == NULL)
            die(ebuf);
    }
    if ((pcap = pcap_open_live(dev, SNAPLEN, 1, 1000, ebuf)) == NULL)
        die(ebuf);
    if (pcap_lookupnet(dev, &localnet, &netmask, ebuf) < 0)
        die(ebuf);
    if (pcap_compile(pcap, &fcode, "", 1, netmask) < 0)
        die(ebuf);
    if (pcap_setfilter(pcap, &fcode) < 0)
        die(ebuf);

    printf("tvark: listening on %s\n", dev);
    if (pcap_loop(pcap, 0, realtime, NULL) < 0)
        die(pcap_geterr(pcap));

    pcap_close(pcap);
    return 0;
}


pid_t fork_resolver(void)
{
    pid_t       pid;

    if (pipe(net_to_rslv) == -1)
        die("pipe");
    if (pipe(rslv_to_net) == -1)
        die("pipe");

    if (fcntl(net_to_rslv[1], F_SETFL, O_NONBLOCK) == -1)
        die("fcntl");
    if (fcntl(rslv_to_net[0], F_SETFL, O_NONBLOCK) == -1)
        die("fcntl");

    switch (pid = fork()) {
    case -1:
        close(net_to_rslv[0]);
        close(net_to_rslv[1]);
        close(rslv_to_net[0]);
        close(rslv_to_net[1]);
        die("fork");
        break;
    case 0:
        close(net_to_rslv[1]);
        if (dup2(net_to_rslv[0], 0) == -1)
            die("dup2");
        close(net_to_rslv[0]);

        close(rslv_to_net[0]);
        if (dup2(rslv_to_net[1], 1) == -1)
            die("dup2");
        close(rslv_to_net[1]);

        execvp(BINDIR "/tvark-resolver", NULL);
        die("execvp");
        break;
    default:
        close(net_to_rslv[0]);
        close(rslv_to_net[1]);
        break;
    }

    atexit(kill_resolver);
    signal(SIGINT, sig_exit);
    signal(SIGTERM, sig_exit);

    return pid;
}


void *start_gui(void *arg)
{
    GtkWidget   *gui;

    gui = create_gui();
    gtk_widget_show(gui);

    gtk_main();
    gtk_exit(0);

    return NULL;
}


syntax highlighted by Code2HTML, v. 0.9.1