uint8_t md_sqlite_handle_munin_event(struct md_writer_sqlite *mws,
                                   struct md_munin_event *mme)
{
    json_object *value;
    int64_t boottime    = 0;

    json_object *session_obj;
    if (!json_object_object_get_ex(mme->json_blob, "session", &session_obj)) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to read data from session module (Munin)\n");
        return RETVAL_FAILURE;
    }
    if (!json_object_object_get_ex(session_obj, "start", &value)) 
        return RETVAL_FAILURE;
    if ((boottime = json_object_get_int64(value)) < 1400000000 ) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to read valid start time from session module (Munin): %" PRId64 "\n", boottime);
        return RETVAL_FAILURE;
    }

    sqlite3_stmt *stmt = mws->insert_monitor;
    sqlite3_clear_bindings(stmt);
    sqlite3_reset(stmt);

    if (sqlite3_bind_int(stmt,    1, mws->node_id)  ||
        sqlite3_bind_int(stmt,    2, mme->tstamp)   ||
        sqlite3_bind_int(stmt,    3, mme->sequence) || 
        sqlite3_bind_int64(stmt,  4, boottime)       ){ 
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to bind values to INSERT query (Monitor)\n");
        return RETVAL_FAILURE;
    }

    if (sqlite3_step(stmt) != SQLITE_DONE)
        return RETVAL_FAILURE;
    else
        return RETVAL_SUCCESS;
}
static int32_t md_sqlite_execute_insert_update(struct md_writer_sqlite *mws,
        struct md_conn_event *mce)
{
    sqlite3_stmt *stmt = mws->insert_update;

    sqlite3_clear_bindings(stmt);
    sqlite3_reset(stmt);

    if (sqlite3_bind_int(stmt, 1, mws->node_id) ||
            sqlite3_bind_int64(stmt, 2, mws->session_id) ||
            sqlite3_bind_int64(stmt, 3, mws->session_id_multip) ||
            sqlite3_bind_int64(stmt, 4, mce->tstamp) ||
            sqlite3_bind_int(stmt, 5, mce->sequence) ||
            sqlite3_bind_int(stmt, 6, mce->l3_session_id) ||
            sqlite3_bind_int(stmt, 7, mce->l4_session_id) ||
            sqlite3_bind_text(stmt, 8, mce->event_value_str, strlen(mce->event_value_str), SQLITE_STATIC) ||
            sqlite3_bind_int(stmt, 9, mce->interface_type) ||
            sqlite3_bind_text(stmt, 10, mce->interface_id, strlen(mce->interface_id), SQLITE_STATIC) ||
            sqlite3_bind_text(stmt, 11, mce->network_address, strlen(mce->network_address), SQLITE_STATIC)) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to bind values to INSERT query\n");
        return SQLITE_ERROR;
    }

    if (mce->network_provider &&
            sqlite3_bind_int(stmt, 12, mce->network_provider)) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to bind network provider\n");
        return SQLITE_ERROR;
    }

    return sqlite3_step(stmt);
}
static void md_sqlite_insert_fake_mode(struct md_writer_sqlite *mws,
                                       struct md_conn_event *mce,
                                       uint8_t mode)
{
    const char *event_value_str = mce->event_value_str;
    int32_t retval;

    //TODO: Not a nice way to access parent variable
    mce->sequence = mde_inc_seq(mws->parent);
    mce->event_value = mode;
    mce->event_param = CONN_EVENT_META_MODE_UPDATE;
    mce->event_value_str = NULL;

    retval = md_sqlite_execute_insert(mws, mce);

    if (retval == SQLITE_DONE)
        META_PRINT_SYSLOG(mws->parent, LOG_INFO, "Inserted fake mode update\n");
    else
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to insert fake mode update\n");

    //Restore/update query after mode insert
    mce->event_param = CONN_EVENT_META_UPDATE;
    mce->event_value = 0;
    mce->event_value_str = event_value_str;
}
uint8_t md_sqlite_handle_gps_event(struct md_writer_sqlite *mws,
                                   struct md_gps_event *mge)
{
    if (mge->speed)
        mws->gps_speed = mge->speed;

    if (mge->minmea_id == MINMEA_SENTENCE_RMC)
        return RETVAL_IGNORE;

    //We dont need EVERY gps event, some devices send updates very frequently
    //Some of the devices we work with have timers that are ... strange
    if (mws->last_gps_insert > mge->tstamp_tv.tv_sec ||
        mge->tstamp_tv.tv_sec - mws->last_gps_insert < GPS_EVENT_INTVL)
        return RETVAL_IGNORE;

    sqlite3_stmt *stmt = mws->insert_gps;
    sqlite3_clear_bindings(stmt);
    sqlite3_reset(stmt);

    if (sqlite3_bind_int(stmt, 1, mws->node_id) ||
        sqlite3_bind_int(stmt, 2, mws->session_id) ||          // BootCount
        sqlite3_bind_int(stmt, 3, mws->session_id_multip) ||   // BootMultiplier
        sqlite3_bind_int(stmt, 4, mge->tstamp_tv.tv_sec) ||
        sqlite3_bind_int(stmt, 5, mge->sequence) ||
        sqlite3_bind_double(stmt, 6, mge->latitude) ||
        sqlite3_bind_double(stmt, 7, mge->longitude)) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to bind values to INSERT query (GPS)\n");
        return RETVAL_FAILURE;
    }

    if (mge->altitude &&
        sqlite3_bind_double(stmt, 8, mge->altitude)) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to bind altitude\n");
        return RETVAL_FAILURE;
    }

    if (mws->gps_speed &&
        sqlite3_bind_double(stmt, 9, mws->gps_speed)) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to bind speed\n");
        return RETVAL_FAILURE;
    }

    if (mge->satellites_tracked &&
        sqlite3_bind_int(stmt, 10, mge->satellites_tracked)) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to bind num. satelites\n");
        return RETVAL_FAILURE;
    }

    if (sqlite3_step(stmt) != SQLITE_DONE) {
        return RETVAL_FAILURE;
    } else {
        mws->last_gps_insert = mge->tstamp_tv.tv_sec;
        return RETVAL_SUCCESS;
    }
}
static int32_t md_sqlite_execute_insert(struct md_writer_sqlite *mws,
                                        struct md_conn_event *mce)
{
    int32_t retval;

    sqlite3_stmt *stmt = mws->insert_event;
    sqlite3_clear_bindings(stmt);
    sqlite3_reset(stmt);

    if (sqlite3_bind_int(stmt, 1, mws->node_id) ||
            sqlite3_bind_int64(stmt, 2, mws->session_id) ||
            sqlite3_bind_int64(stmt, 3, mws->session_id_multip) ||
            sqlite3_bind_int64(stmt, 4, mce->tstamp) ||
            sqlite3_bind_int(stmt, 5, mce->sequence) ||
            sqlite3_bind_int(stmt, 6, mce->l3_session_id) ||
            sqlite3_bind_int(stmt, 7, mce->l4_session_id) ||
            sqlite3_bind_int(stmt, 8, mce->event_type) ||
            sqlite3_bind_int(stmt, 9, mce->event_param) ||
            sqlite3_bind_int(stmt, 12, mce->interface_type) ||
            sqlite3_bind_int(stmt, 13, mce->interface_id_type) ||
            sqlite3_bind_text(stmt, 14, mce->interface_id, strlen(mce->interface_id), SQLITE_STATIC) ||
            sqlite3_bind_int(stmt, 16, mce->network_address_family) ||
            sqlite3_bind_text(stmt, 17, mce->network_address, strlen(mce->network_address), SQLITE_STATIC)) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to bind values to INSERT query\n");
        return SQLITE_ERROR;
    }

    if (mce->event_value != UINT8_MAX &&
            sqlite3_bind_int(stmt, 10, mce->event_value)) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed bind event value (int)\n");
        return SQLITE_ERROR;
    }

    if (mce->event_value_str != NULL &&
            sqlite3_bind_text(stmt, 11, mce->event_value_str, strlen(mce->event_value_str), SQLITE_STATIC)) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to bind event value (string)\n");
        return SQLITE_ERROR;
    }

    if (mce->network_provider) {
        retval = sqlite3_bind_int(stmt, 15, mce->network_provider);

        if (retval) {
            META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to bind provider to INSERT query\n");
            return SQLITE_ERROR;
        }
    }

    return sqlite3_step(stmt);
}
static void md_sqlite_insert_fake_events(struct md_writer_sqlite *mws,
        struct md_conn_event *mce,
        int32_t update_exists)
{
    //TODO: Find a way to respect const
    int16_t mode_in_update = -1, mode_in_table = -1;
    int16_t quality_in_update = -1, quality_in_table = -1;
    char event_str_cpy[EVENT_STR_LEN];
    size_t event_str_len;
    struct timeval t_now;

    if (mws->first_fake_update.tv_sec != 0) {
        gettimeofday(&t_now, NULL);

        if (t_now.tv_sec > mws->first_fake_update.tv_sec &&
                t_now.tv_sec - mws->first_fake_update.tv_sec > FAKE_UPDATE_LIMIT) {
            mws->do_fake_updates = 0;
            return;
        }
    } else {
        gettimeofday(&(mws->first_fake_update), NULL);
    }

    event_str_len = strlen(mce->event_value_str);

    if (event_str_len >= EVENT_STR_LEN) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Event string too long\n");
        return;
    }

    memcpy(event_str_cpy, mce->event_value_str, event_str_len);
    event_str_cpy[event_str_len] = '\0';

    mode_in_update = metadata_utils_get_csv_pos(event_str_cpy, 2);
    quality_in_update = metadata_utils_get_csv_pos(event_str_cpy, 3);

    //If there was no update messge from before, then insert the fake mode/quality messages (just to be sure)
    //and return
    if (update_exists == SQLITE_DONE) {
        //Always insert mode on the first update, for consistency (it should be
        //possible to follow the mode update messages exclusively)
        if (mode_in_update != -1)
            md_sqlite_insert_fake_mode(mws, mce, mode_in_update);

        if (quality_in_update != -1)
            md_sqlite_insert_fake_quality(mws, mce, quality_in_update);

        return;
    }

    md_sqlite_get_last_update(mws, mce, &mode_in_table, &quality_in_table);

    //Get mode from last update message. If we can read modem mode, then this
    //value will be 0 or larger
    if (mode_in_update != -1 && mode_in_update != mode_in_table)
        md_sqlite_insert_fake_mode(mws, mce, mode_in_update);

    if (quality_in_update != -1 && quality_in_update != quality_in_table)
        md_sqlite_insert_fake_quality(mws, mce, quality_in_update);
}
static int16_t md_sqlite_get_last_update(struct md_writer_sqlite *mws,
        struct md_conn_event *mce,
        int16_t *mode, int16_t *quality)
{
    int16_t retval = -1;
    int numbytes = 0;
    const unsigned char *event_value_str = NULL;
    char event_str_cpy[EVENT_STR_LEN];
    sqlite3_stmt *stmt = mws->last_update;
    sqlite3_clear_bindings(stmt);
    sqlite3_reset(stmt);

    *mode = -1;
    *quality = -1;

    if (sqlite3_bind_int(stmt, 1, mce->l3_session_id) ||
            sqlite3_bind_int(stmt, 2, mce->l4_session_id) ||
            sqlite3_bind_text(stmt, 3, mce->interface_id, strlen(mce->interface_id), SQLITE_STATIC) ||
            sqlite3_bind_text(stmt, 4, mce->network_address, strlen(mce->network_address), SQLITE_STATIC)) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to bind values to SELECT query\n");
        return retval;
    }

    while (sqlite3_step(stmt) == SQLITE_ROW) {
        event_value_str = sqlite3_column_text(stmt, 0);
        numbytes = sqlite3_column_bytes(stmt, 0);

        if (numbytes >= EVENT_STR_LEN) {
            META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Event value string will not fit in buffer\n");
            return SQLITE_ERROR;
        }

        memcpy(event_str_cpy, event_value_str, numbytes);
        event_str_cpy[numbytes] = '\0';

        *mode = metadata_utils_get_csv_pos(event_str_cpy, 2);
        *quality = metadata_utils_get_csv_pos(event_str_cpy, 3);
        break;
    }

    return retval;
}
static uint8_t md_sqlite_handle_insert_conn_event(struct md_writer_sqlite *mws,
        struct md_conn_event *mce)
{
    int32_t retval = md_sqlite_execute_insert(mws, mce);

    if (retval != SQLITE_DONE) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "INSERT failed: %s\n", sqlite3_errstr(retval));
        return RETVAL_FAILURE;
    }

    return RETVAL_SUCCESS;
}
static uint8_t md_sqlite_monitor_delete_db(struct md_writer_sqlite *mws)
{
    int32_t retval = 0;
    sqlite3_reset(mws->delete_monitor);

    retval = sqlite3_step(mws->delete_monitor);

    if (retval == SQLITE_DONE) {
        return RETVAL_SUCCESS;
    } else {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to delete monitor table\n");
        return RETVAL_FAILURE;
    }
}
static void md_sqlite_insert_fake_quality(struct md_writer_sqlite *mws,
        struct md_conn_event *mce,
        uint8_t quality)
{
    const char *event_value_str = mce->event_value_str;
    int32_t retval;

    mce->sequence = mde_inc_seq(mws->parent);
    mce->event_value = quality;
    mce->event_param = CONN_EVENT_META_QUALITY_UPDATE;
    mce->event_value_str = NULL;

    retval = md_sqlite_execute_insert(mws, mce);

    if (retval == SQLITE_DONE)
        META_PRINT_SYSLOG(mws->parent, LOG_INFO, "Inserted fake quality update\n");
    else
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to insert fake quality update\n");

    //Restore/update query after quality insert
    mce->event_param = CONN_EVENT_META_UPDATE;
    mce->event_value = 0;
    mce->event_value_str = event_value_str;
}
Beispiel #11
0
static void run_test_mode(struct md_exporter *mde, uint32_t packets)
{
    pthread_t thread;
    pthread_attr_t attr;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    pthread_create(&thread, &attr, mde_run, mde);

    test_netlink(packets);

    pthread_join(thread, NULL);

    META_PRINT_SYSLOG(mde, LOG_ERR, "Threads should NEVER exit\n");
}
static int32_t md_sqlite_update_event(struct md_writer_sqlite *mws,
                                      struct md_conn_event *mce)
{
    sqlite3_stmt *stmt = mws->update_update;

    sqlite3_clear_bindings(stmt);
    sqlite3_reset(stmt);

    if (sqlite3_bind_int64(stmt, 1, mce->tstamp) ||
            sqlite3_bind_text(stmt, 2, mce->event_value_str, strlen(mce->event_value_str), SQLITE_STATIC) ||
            sqlite3_bind_int(stmt, 3, mce->l3_session_id) ||
            sqlite3_bind_int(stmt, 4, mce->l4_session_id) ||
            sqlite3_bind_text(stmt, 5, mce->network_address, strlen(mce->network_address), SQLITE_STATIC) ||
            sqlite3_bind_text(stmt, 6, mce->interface_id, strlen(mce->interface_id), SQLITE_STATIC)) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "Failed to bind values to UPDATE query\n");
        return SQLITE_ERROR;
    }

    return sqlite3_step(stmt);
}
static uint8_t md_sqlite_handle_update_event(struct md_writer_sqlite *mws,
        struct md_conn_event *mce)
{
    //Check if update is present in update table by doing an insert
    int32_t retval = md_sqlite_execute_insert_update(mws, mce);

    if (mws->do_fake_updates)
        md_sqlite_insert_fake_events(mws, mce, retval);

    //No need to do UPDATE if INSERT was successful
    if (retval == SQLITE_DONE)
        return RETVAL_SUCCESS;

    //Update in update table
    retval = md_sqlite_update_event(mws, mce);

    if (retval != SQLITE_DONE) {
        META_PRINT_SYSLOG(mws->parent, LOG_ERR, "UPDATE failed: %s\n", sqlite3_errstr(retval));
        return RETVAL_FAILURE;
    }

    return RETVAL_SUCCESS;
}
Beispiel #14
0
int main(int argc, char *argv[])
{
    struct md_exporter *mde;
    int32_t i, option_index = 0;
    uint32_t packets = 0;
    uint8_t test_mode = 0, show_help = 0, num_writers = 0, num_inputs = 0;
    const char *logfile_path = NULL;

    static struct option core_options[] = {
        {"netlink",      no_argument,        0,  'n'},
#ifdef SQLITE_SUPPORT
        {"sqlite",       no_argument,        0,  's'},
#endif
#ifdef NSB_GPS
        {"nsb_gps",      no_argument,        0,  0  },
#endif
#ifdef ZEROMQ_SUPPORT
        {"zeromq",       no_argument,        0,  'z'},
#endif
#ifdef NNE_SUPPORT
        {"nne",          no_argument,        0,  0  },
#endif
#ifdef GPSD_SUPPORT
        {"gpsd",         no_argument,        0,  'g'},
#endif
#ifdef MUNIN_SUPPORT
        {"munin",        no_argument,        0,  'm'},
#endif
#ifdef SYSEVENT_SUPPORT
        {"sysevent",     no_argument,        0,  'y'},
#endif
        {"packets",      required_argument,  0,  'p'},
        {"test",         no_argument,        0,  't'},
        {"logfile",      required_argument,  0,  'l'},
        {"help",         no_argument,        0,  'h'},
        {0,              0,                  0,   0 }};

    //Try to configure core before we set up the outputters
    if (configure_core(&mde))
        exit(EXIT_FAILURE);

    //Process core options, short options allowed. We do this here since we need
    //an allocated writers array
    opterr = 0;
    while (1) {
        //Use glic extension to avoid getopt permuting array while processing
        i = getopt_long(argc, argv, "--szhmgtnkp:l:", core_options, &option_index);

        if (i == -1)
            break;

        if (i == 0) {
#ifdef NSB_GPS
            if (strcmp(core_options[option_index].name, "nsb_gps") == 0) {
                mde->md_inputs[MD_INPUT_GPS_NSB] = calloc(sizeof(struct md_input_gps_nsb), 1);

                if (mde->md_inputs[MD_INPUT_GPS_NSB] == NULL) {
                    META_PRINT_SYSLOG(mde, LOG_ERR, "Could not allocate NSB GPS input\n");
                    exit(EXIT_FAILURE);
                }

                md_gps_nsb_setup(mde, (struct md_input_gps_nsb*) mde->md_inputs[MD_INPUT_GPS_NSB]);
                num_inputs++;
            }
#endif
#ifdef NNE_SUPPORT
            if (strcmp(core_options[option_index].name, "nne") == 0) {
                mde->md_writers[MD_WRITER_NNE] = calloc(sizeof(struct md_writer_nne), 1);

                if (mde->md_writers[MD_WRITER_NNE] == NULL) {
                    META_PRINT_SYSLOG(mde, LOG_ERR, "Could not allocate NNE  writer\n");
                    exit(EXIT_FAILURE);
                }

                md_nne_setup(mde, (struct md_writer_nne*) mde->md_writers[MD_WRITER_NNE]);
                num_writers++;
            }
#endif
            continue;
        }

        switch (i) {
        case 'n':
            mde->md_inputs[MD_INPUT_NETLINK] = calloc(sizeof(struct md_input_netlink),1);

            if (mde->md_inputs[MD_INPUT_NETLINK] == NULL) {
                META_PRINT_SYSLOG(mde, LOG_ERR, "Could not allocate Netlink input\n");
                exit(EXIT_FAILURE);
            }

            md_netlink_setup(mde, (struct md_input_netlink*) mde->md_inputs[MD_INPUT_NETLINK]);
            num_inputs++;
            break;
#ifdef GPSD_SUPPORT
        case 'g':
            mde->md_inputs[MD_INPUT_GPSD] = calloc(sizeof(struct md_input_gpsd), 1);

            if (mde->md_inputs[MD_INPUT_GPSD] == NULL) {
                META_PRINT_SYSLOG(mde, LOG_ERR, "Could not allocate GPSD input\n");
                exit(EXIT_FAILURE);
            }

            md_gpsd_setup(mde, (struct md_input_gpsd*) mde->md_inputs[MD_INPUT_GPSD]);
            num_inputs++;
            break;
#endif
#ifdef MUNIN_SUPPORT
        case 'm':
            mde->md_inputs[MD_INPUT_MUNIN] = calloc(sizeof(struct md_input_munin), 1);

            if (mde->md_inputs[MD_INPUT_MUNIN] == NULL) {
                META_PRINT_SYSLOG(mde, LOG_ERR, "Could not allocate Munin input\n");
                exit(EXIT_FAILURE);
            }

            md_munin_setup(mde, (struct md_input_munin*) mde->md_inputs[MD_INPUT_MUNIN]);
            num_inputs++;
            break;
#endif
#ifdef SYSEVENT_SUPPORT
        case 'y':
            mde->md_inputs[MD_INPUT_SYSEVENT] = calloc(sizeof(struct md_input_sysevent), 1);

            if (mde->md_inputs[MD_INPUT_SYSEVENT] == NULL) {
                META_PRINT(mde->logfile, "Could not allocate Sysevent input\n");
                exit(EXIT_FAILURE);
            }

            md_sysevent_setup(mde, (struct md_input_sysevent*) mde->md_inputs[MD_INPUT_SYSEVENT]);
            num_inputs++;
            break;
#endif 
#ifdef SQLITE_SUPPORT
        case 's':
            mde->md_writers[MD_WRITER_SQLITE] = calloc(sizeof(struct md_writer_sqlite), 1);

            if (mde->md_writers[MD_WRITER_SQLITE] == NULL) {
                META_PRINT_SYSLOG(mde, LOG_ERR, "Could not allocate SQLite writer\n");
                exit(EXIT_FAILURE);
            }

            md_sqlite_setup(mde, (struct md_writer_sqlite*) mde->md_writers[MD_WRITER_SQLITE]);
            num_writers++;
            break;
#endif
#ifdef ZEROMQ_SUPPORT
        case 'z':
            mde->md_writers[MD_WRITER_ZEROMQ] = calloc(sizeof(struct md_writer_zeromq), 1);

            if (mde->md_writers[MD_WRITER_ZEROMQ] == NULL) {
                META_PRINT_SYSLOG(mde, LOG_ERR, "Could not allocate SQLite writer\n");
                exit(EXIT_FAILURE);
            }

            md_zeromq_setup(mde, (struct md_writer_zeromq*) mde->md_writers[MD_WRITER_ZEROMQ]);
            num_writers++;
            break;
#endif
        case 't':
            test_mode = 1;
            break;
        case 'p':
            packets = (uint32_t) atoi(optarg);
            break;
        case 'l':
            logfile_path = optarg;
            break;
        case 'k':
            mde->use_syslog = 1;
            break;
        case 'h':
            show_help = 1;
            break;
        }
    }

    if (show_help) {
        print_usage(mde);
        exit(EXIT_SUCCESS);
    }

    if (num_writers == 0 || num_inputs == 0) {
        fprintf(stderr, "No input(s)/writer(s) specified\n");
        exit(EXIT_FAILURE);
    }

    if (logfile_path) {
        mde->logfile = fopen(logfile_path, "a");

        if (mde->logfile == NULL) {
            fprintf(stderr, "Could not open logfile: %s\n", logfile_path);
            exit(EXIT_FAILURE);
        }
    }

    for (i=0; i<=MD_INPUT_MAX; i++) {
        if (mde->md_inputs[i] != NULL) {
            META_PRINT_SYSLOG(mde, LOG_INFO, "Will configure input %d\n", i);
            //glic requires optind to be 0 for internal state to be reset when
            //using extensions
            optind = 0;
            if (mde->md_inputs[i]->init(mde->md_inputs[i], argc, argv))
                exit(EXIT_FAILURE);
        }
    }

    for (i=0; i<=MD_WRITER_MAX; i++) {
        if (mde->md_writers[i] != NULL) {
            META_PRINT_SYSLOG(mde, LOG_INFO, "Will configure writer %d\n", i);
            //glic requires optind to be 0 for internal state to be reset when
            //using extensions
            optind = 0;
            if (mde->md_writers[i]->init(mde->md_writers[i], argc, argv))
                exit(EXIT_FAILURE);
        }
    }

    if (test_mode)
        run_test_mode(mde, packets);
    else
        backend_event_loop_run(mde->event_loop);

    META_PRINT_SYSLOG(mde, LOG_ERR, "Threads should NEVER exit\n");
    exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
    struct md_exporter *mde;
    int32_t i;
    uint32_t packets = 0;
    uint8_t test_mode = 0, num_writers = 0, num_inputs = 0;
    const char *logfile_path = NULL;
    json_object *config = NULL;

    //Try to configure core before we set up the outputters
    if (configure_core(&mde))
        exit(EXIT_FAILURE);

    //Process core options, short options allowed. We do this here since we need
    //an allocated writers array
    opterr = 0;
    while ((i = getopt(argc, argv, "c:h")) != -1) {
        if (i == -1) {
            break;
        } else if (i == 'c') {
            read_config(optarg, &config);
        } else if (i == 'h') { 
            print_usage();
            exit(EXIT_SUCCESS);
        }
    }

    if (config == NULL) {
        META_PRINT_SYSLOG(mde, LOG_ERR, "Parameter -c is required to run.\n");
        exit(EXIT_FAILURE);
    }

    json_object_object_foreach(config, key, val) { 
        if (!strcmp(key, "netlink")) {
            mde->md_inputs[MD_INPUT_NETLINK] = calloc(sizeof(struct md_input_netlink),1);

            if (mde->md_inputs[MD_INPUT_NETLINK] == NULL) {
                META_PRINT_SYSLOG(mde, LOG_ERR, "Could not allocate Netlink input\n");
                exit(EXIT_FAILURE);
            }

            md_netlink_setup(mde, (struct md_input_netlink*) mde->md_inputs[MD_INPUT_NETLINK]);
            num_inputs++;
        }
#ifdef NSB_GPS
        else if (!strcmp(key, "nsb_gps")) {
            mde->md_inputs[MD_INPUT_GPS_NSB] = calloc(sizeof(struct md_input_gps_nsb), 1);

            if (mde->md_inputs[MD_INPUT_GPS_NSB] == NULL) {
                META_PRINT_SYSLOG(mde, LOG_ERR, "Could not allocate NSB GPS input\n");
                exit(EXIT_FAILURE);
            }

            md_gps_nsb_setup(mde, (struct md_input_gps_nsb*) mde->md_inputs[MD_INPUT_GPS_NSB]);
            num_inputs++;
        }
#endif
#ifdef NNE_SUPPORT
        else if (!strcmp(key, "nne")) {
            mde->md_writers[MD_WRITER_NNE] = calloc(sizeof(struct md_writer_nne), 1);

            if (mde->md_writers[MD_WRITER_NNE] == NULL) {
                META_PRINT_SYSLOG(mde, LOG_ERR, "Could not allocate NNE  writer\n");
                exit(EXIT_FAILURE);
            }

            md_nne_setup(mde, (struct md_writer_nne*) mde->md_writers[MD_WRITER_NNE]);
            num_writers++;
        }
#endif
#ifdef GPSD_SUPPORT
        else if (!strcmp(key, "gpsd")) {
            mde->md_inputs[MD_INPUT_GPSD] = calloc(sizeof(struct md_input_gpsd), 1);

            if (mde->md_inputs[MD_INPUT_GPSD] == NULL) {
                META_PRINT_SYSLOG(mde, LOG_ERR, "Could not allocate GPSD input\n");
                exit(EXIT_FAILURE);
            }

            md_gpsd_setup(mde, (struct md_input_gpsd*) mde->md_inputs[MD_INPUT_GPSD]);
            num_inputs++;
        }
#endif
#ifdef MUNIN_SUPPORT
        else if (!strcmp(key, "munin")) {
            mde->md_inputs[MD_INPUT_MUNIN] = calloc(sizeof(struct md_input_munin), 1);

            if (mde->md_inputs[MD_INPUT_MUNIN] == NULL) {
                META_PRINT_SYSLOG(mde, LOG_ERR, "Could not allocate Munin input\n");
                exit(EXIT_FAILURE);
            }

            md_munin_setup(mde, (struct md_input_munin*) mde->md_inputs[MD_INPUT_MUNIN]);
            num_inputs++;
        }
#endif
#ifdef SYSEVENT_SUPPORT
        else if (!strcmp(key, "sysevent")) {
            mde->md_inputs[MD_INPUT_SYSEVENT] = calloc(sizeof(struct md_input_sysevent), 1);

            if (mde->md_inputs[MD_INPUT_SYSEVENT] == NULL) {
                META_PRINT(mde->logfile, "Could not allocate Sysevent input\n");
                exit(EXIT_FAILURE);
            }

            md_sysevent_setup(mde, (struct md_input_sysevent*) mde->md_inputs[MD_INPUT_SYSEVENT]);
            num_inputs++;
        } 
#endif 
#ifdef SQLITE_SUPPORT
        else if (!strcmp(key, "sqlite")) {
            mde->md_writers[MD_WRITER_SQLITE] = calloc(sizeof(struct md_writer_sqlite), 1);

            if (mde->md_writers[MD_WRITER_SQLITE] == NULL) {
                META_PRINT_SYSLOG(mde, LOG_ERR, "Could not allocate SQLite writer\n");
                exit(EXIT_FAILURE);
            }

            md_sqlite_setup(mde, (struct md_writer_sqlite*) mde->md_writers[MD_WRITER_SQLITE]);
            num_writers++;
        }
#endif
#ifdef ZEROMQ_SUPPORT
        else if (!strcmp(key, "zmq")) {
            mde->md_writers[MD_WRITER_ZEROMQ] = calloc(sizeof(struct md_writer_zeromq), 1);

            if (mde->md_writers[MD_WRITER_ZEROMQ] == NULL) {
                META_PRINT_SYSLOG(mde, LOG_ERR, "Could not allocate SQLite writer\n");
                exit(EXIT_FAILURE);
            }

            md_zeromq_setup(mde, (struct md_writer_zeromq*) mde->md_writers[MD_WRITER_ZEROMQ]);
            num_writers++;
        }
#endif
        else if (!strcmp(key, "test")) {
            test_mode = 1;
        } 
        else if (!strcmp(key, "packets")) {
            packets = (uint32_t) json_object_get_int(val);
        } 
        else if (!strcmp(key, "logfile")) {
            logfile_path = json_object_get_string(val); 
        } 
        else if (!strcmp(key, "syslog")) {
            mde->use_syslog = json_object_get_int(val);
        }
    }

    if (num_writers == 0 || num_inputs == 0) {
        fprintf(stderr, "No input(s)/writer(s) specified\n");
        exit(EXIT_FAILURE);
    }

    if (logfile_path) {
        mde->logfile = fopen(logfile_path, "a");

        if (mde->logfile == NULL) {
            fprintf(stderr, "Could not open logfile: %s\n", logfile_path);
            exit(EXIT_FAILURE);
        }
    }

    for (i=0; i<=MD_INPUT_MAX; i++) {
        if (mde->md_inputs[i] != NULL) {
            META_PRINT_SYSLOG(mde, LOG_INFO, "Will configure input %d\n", i);
            //glic requires optind to be 0 for internal state to be reset when
            //using extensions
            optind = 0;
            if (mde->md_inputs[i]->init(mde->md_inputs[i], config))
                exit(EXIT_FAILURE);
        }
    }

    for (i=0; i<=MD_WRITER_MAX; i++) {
        if (mde->md_writers[i] != NULL) {
            META_PRINT_SYSLOG(mde, LOG_INFO, "Will configure writer %d\n", i);
            //glic requires optind to be 0 for internal state to be reset when
            //using extensions
            optind = 0;
            if (mde->md_writers[i]->init(mde->md_writers[i], config))
                exit(EXIT_FAILURE);
        }
    }

    json_object_put(config);

    if (test_mode)
        run_test_mode(mde, packets);
    else
        backend_event_loop_run(mde->event_loop);

    META_PRINT_SYSLOG(mde, LOG_ERR, "Threads should NEVER exit\n");
    exit(EXIT_FAILURE);
}
static uint8_t md_input_netlink_parse_conn_event(struct md_input_netlink *min,
        struct json_object *meta_obj)
{
    struct md_conn_event *mce = min->mce;

    json_object_object_foreach(meta_obj, key, val) {
        if (!strcmp(key, "md_seq"))
            mce->sequence = (uint16_t) json_object_get_int(val);

        if (!strcmp(key, "timestamp"))
            mce->tstamp = json_object_get_int64(val);

        if (!strcmp(key, "event_type"))
            mce->event_type = (uint8_t) json_object_get_int(val);

        if (!strcmp(key, "event_param"))
            mce->event_param = (uint8_t) json_object_get_int(val);

        if (!strcmp(key, "event_value"))
            mce->event_value = (uint8_t) json_object_get_int(val);

        if (!strcmp(key, "event_value_str"))
            mce->event_value_str = json_object_get_string(val);

        if (!strcmp(key, "interface_id_type"))
            mce->interface_id_type = (uint8_t) json_object_get_int(val);

        if (!strcmp(key, "interface_id"))
            mce->interface_id = json_object_get_string(val);

        if (!strcmp(key, "imei"))
            mce->imei = json_object_get_string(val);

        if (!strcmp(key, "imsi"))
            mce->imsi = json_object_get_string(val);

        if (!strcmp(key, "interface_name"))
            mce->interface_name = json_object_get_string(val);

        if (!strcmp(key, "interface_type"))
            mce->interface_type = (uint8_t) json_object_get_int(val);

        if (!strcmp(key, "network_address_family"))
            mce->network_address_family = (uint8_t) json_object_get_int(val);

        if (!strcmp(key, "network_address"))
            mce->network_address = json_object_get_string(val);

        if (!strcmp(key, "network_provider_type"))
            mce->network_provider_type = (uint8_t) json_object_get_int(val);

        if (!strcmp(key, "network_provider"))
            mce->network_provider = json_object_get_int(val);

        if (!strcmp(key, "l3_session_id"))
            mce->l3_session_id = (uint64_t) json_object_get_int64(val);

        if (!strcmp(key, "l4_session_id"))
            mce->l4_session_id = (uint64_t) json_object_get_int64(val);

        if (!strcmp(key, "signal_strength"))
            mce->signal_strength = (int8_t) json_object_get_int(val);

        if (!strcmp(key, "rx_bytes"))
            mce->rx_bytes = (uint64_t) json_object_get_int64(val);

        if (!strcmp(key, "tx_bytes"))
            mce->tx_bytes = (uint64_t) json_object_get_int64(val);
    }

    if (mce->event_param == CONN_EVENT_DATA_USAGE_UPDATE) {
        if (!mce->tstamp || !mce->event_param || !mce->interface_id || (mce->imei && !mce->imsi) ||
            (mce->imsi && !mce->imei)) {
            META_PRINT_SYSLOG(min->parent, LOG_ERR, "Missing required argument in usage JSON\n");
            return RETVAL_FAILURE;
        } else {
            return RETVAL_SUCCESS;
        }
    }

    if (!mce->tstamp || !mce->sequence ||
        !mce->l3_session_id || !mce->event_param ||
        !mce->interface_type || !mce->network_address_family ||
        !mce->network_address || !mce->interface_id ||
        !mce->interface_id_type) {
        META_PRINT_SYSLOG(min->parent, LOG_ERR, "Missing required argument in JSON\n");
        return RETVAL_FAILURE;
    }

    //We need to update the value in case of a connection event update, since it
    //is a string
    //TODO: Implement a more elegant technique if we get more cases like this
    if (mce->event_param == CONN_EVENT_META_UPDATE && !mce->event_value_str) {
        META_PRINT_SYSLOG(min->parent, LOG_ERR, "Missing event value for connection update\n");
        return RETVAL_FAILURE;
    }

    return RETVAL_SUCCESS;
}