/**
 * load systemstats information in specified container
 *
 * @param container empty container, or NULL to have one created for you
 * @param load_flags flags to modify behaviour.
 *
 * @retval NULL  error
 * @retval !NULL pointer to container
 */
netsnmp_container*
netsnmp_access_systemstats_container_load(netsnmp_container* container, u_int load_flags)
{
    int rc;

    DEBUGMSGTL(("access:systemstats:container", "load\n"));

    if (NULL == container) {
        container = netsnmp_access_systemstats_container_init(load_flags);
        if (NULL != container)
            container->container_name = strdup("systemstats_autocreate");
    }
    if (NULL == container) {
        snmp_log(LOG_ERR, "no container specified/found for access_systemstats\n");
        return NULL;
    }

    rc =  netsnmp_access_systemstats_container_arch_load(container, load_flags);
    if (0 != rc) {
        netsnmp_access_systemstats_container_free(container,
                                                NETSNMP_ACCESS_SYSTEMSTATS_FREE_NOFLAGS);
        container = NULL;
    }

    return container;
}
/*
 * initialization
 */
void
netsnmp_access_systemstats_init(void)
{
    netsnmp_container * ifcontainer;

    netsnmp_access_systemstats_arch_init();

    /*
     * load once to set up ifIndexes
     */
    ifcontainer = netsnmp_access_systemstats_container_load(NULL, 0);
    if(NULL != ifcontainer)
        netsnmp_access_systemstats_container_free(ifcontainer, 0);

}
/**
 * load initial data
 *
 * TODO:350:M: Implement ipIfStatsTable data load
 * This function will also be called by the cache helper to load
 * the container again (after the container free function has been
 * called to free the previous contents).
 *
 * @param container container to which items should be inserted
 *
 * @retval MFD_SUCCESS              : success.
 * @retval MFD_RESOURCE_UNAVAILABLE : Can't access data source
 * @retval MFD_ERROR                : other error.
 *
 *  This function is called to load the index(es) (and data, optionally)
 *  for the every row in the data set.
 *
 * @remark
 *  While loading the data, the only important thing is the indexes.
 *  If access to your data is cheap/fast (e.g. you have a pointer to a
 *  structure in memory), it would make sense to update the data here.
 *  If, however, the accessing the data invovles more work (e.g. parsing
 *  some other existing data, or peforming calculations to derive the data),
 *  then you can limit yourself to setting the indexes and saving any
 *  information you will need later. Then use the saved information in
 *  ipIfStatsTable_row_prep() for populating data.
 *
 * @note
 *  If you need consistency between rows (like you want statistics
 *  for each row to be from the same time frame), you should set all
 *  data here.
 *
 */
int
ipIfStatsTable_container_load(netsnmp_container * container)
{
    netsnmp_container *stats;

    DEBUGMSGTL(("verbose:ipIfStatsTable:ipIfStatsTable_container_load",
                "called\n"));

    netsnmp_assert(NULL != container);

    stats = netsnmp_access_systemstats_container_load(NULL, NETSNMP_ACCESS_SYSTEMSTATS_LOAD_IFTABLE);
    if (NULL == stats)
        return MFD_RESOURCE_UNAVAILABLE;        /* msg already logged */

    /*
     * TODO:351:M: |-> Load/update data in the ipIfStatsTable container.
     * loop over your ipIfStatsTable data, allocate a rowreq context,
     * set the index(es) [and data, optionally] and insert into
     * the container.
     */

    /*
     * we just got a fresh copy of data. compare it to
     * what we've already got, and make any adjustements...
     */
    CONTAINER_FOR_EACH(container, (netsnmp_container_obj_func *)
                       _check_for_updates, stats);

    /*
     * now add any new entries
     */
    CONTAINER_FOR_EACH(stats, (netsnmp_container_obj_func *)
                       _add_new, container);


    /*
      * free the container. we've either claimed each ifentry, or released it,
     * so the dal function doesn't need to clear the container.
     */
    netsnmp_access_systemstats_container_free(stats,
            NETSNMP_ACCESS_SYSTEMSTATS_FREE_DONT_CLEAR);

    DEBUGMSGT(("verbose:ipIfStatsTable:ipIfStatsTable_container_load",
               "%lu records\n", (unsigned long)CONTAINER_SIZE(container)));

    return MFD_SUCCESS;
}                               /* ipIfStatsTable_container_load */
/*
 * Based on load_flags, load ipSystemStatsTable or ipIfStatsTable for ipv4 entries. 
 */
static int
_systemstats_v4(netsnmp_container* container, u_int load_flags)
{
    FILE           *devin;
    char            line[1024];
    netsnmp_systemstats_entry *entry = NULL;
    int             scan_count;
    char           *stats, *start = line;
    int             len;
    unsigned long long scan_vals[19];

    DEBUGMSGTL(("access:systemstats:container:arch", "load v4 (flags %x)\n",
                load_flags));

    netsnmp_assert(container != NULL); /* load function shoulda checked this */

    if (load_flags & NETSNMP_ACCESS_SYSTEMSTATS_LOAD_IFTABLE) {
        /* we do not support ipIfStatsTable for ipv4 */
        return 0;
    }

    if (!(devin = fopen("/proc/net/snmp", "r"))) {
        DEBUGMSGTL(("access:systemstats",
                    "Failed to load Systemstats Table (linux1)\n"));
        NETSNMP_LOGONCE((LOG_ERR, "cannot open /proc/net/snmp ...\n"));
        return -2;
    }

    /*
     * skip header, but make sure it's the length we expect...
     */
    fgets(line, sizeof(line), devin);
    len = strlen(line);
    if (224 != len) {
        fclose(devin);
        snmp_log(LOG_ERR, "unexpected header length in /proc/net/snmp."
                 " %d != 224\n", len);
        return -4;
    }

    /*
     * This file provides the statistics for each systemstats.
     * Read in each line in turn, isolate the systemstats name
     *   and retrieve (or create) the corresponding data structure.
     */
    start = fgets(line, sizeof(line), devin);
    fclose(devin);
    if (start) {

        len = strlen(line);
        if (line[len - 1] == '\n')
            line[len - 1] = '\0';

        while (*start && *start == ' ')
            start++;

        if ((!*start) || ((stats = strrchr(start, ':')) == NULL)) {
            snmp_log(LOG_ERR,
                     "systemstats data format error 1, line ==|%s|\n", line);
            return -4;
        }

        DEBUGMSGTL(("access:systemstats", "processing '%s'\n", start));

        *stats++ = 0; /* null terminate name */
        while (*stats == ' ') /* skip spaces before stats */
            stats++;

        entry = netsnmp_access_systemstats_entry_create(1, 0,
                    "ipSystemStatsTable.ipv4");
        if(NULL == entry) {
            netsnmp_access_systemstats_container_free(container,
                                                      NETSNMP_ACCESS_SYSTEMSTATS_FREE_NOFLAGS);
            return -3;
        }

        /*
         * OK - we've now got (or created) the data structure for
         *      this systemstats, including any "static" information.
         * Now parse the rest of the line (i.e. starting from 'stats')
         *      to extract the relevant statistics, and populate
         *      data structure accordingly.
         */

        memset(scan_vals, 0x0, sizeof(scan_vals));
        scan_count = sscanf(stats,
                            "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu"
                            "%llu %llu %llu %llu %llu %llu %llu %llu %llu",
                            &scan_vals[0],&scan_vals[1],&scan_vals[2],
                            &scan_vals[3],&scan_vals[4],&scan_vals[5],
                            &scan_vals[6],&scan_vals[7],&scan_vals[8],
                            &scan_vals[9],&scan_vals[10],&scan_vals[11],
                            &scan_vals[12],&scan_vals[13],&scan_vals[14],
                            &scan_vals[15],&scan_vals[16],&scan_vals[17],
                            &scan_vals[18]);
        DEBUGMSGTL(("access:systemstats", "  read %d values\n", scan_count));

        if(scan_count != 19) {
            snmp_log(LOG_ERR,
                     "error scanning systemstats data (expected %d, got %d)\n",
                     19, scan_count);
            netsnmp_access_systemstats_entry_free(entry);
            return -4;
        }
        /* entry->stats. = scan_vals[0]; / * Forwarding */
        /* entry->stats. = scan_vals[1]; / * DefaultTTL */
        entry->stats.HCInReceives.low = scan_vals[2] & 0xffffffff;
        entry->stats.HCInReceives.high = scan_vals[2] >> 32;
        entry->stats.InHdrErrors = scan_vals[3];
        entry->stats.InAddrErrors = scan_vals[4];
        entry->stats.HCOutForwDatagrams.low = scan_vals[5] & 0xffffffff;
        entry->stats.HCOutForwDatagrams.high = scan_vals[5] >> 32;
        entry->stats.InUnknownProtos = scan_vals[6];
        entry->stats.InDiscards = scan_vals[7];
        entry->stats.HCInDelivers.low = scan_vals[8] & 0xffffffff;
        entry->stats.HCInDelivers.high = scan_vals[8] >> 32;
        entry->stats.HCOutRequests.low = scan_vals[9] & 0xffffffff;
        entry->stats.HCOutRequests.high = scan_vals[9] >> 32;
        entry->stats.HCOutDiscards.low = scan_vals[10] & 0xffffffff;;
        entry->stats.HCOutDiscards.high = scan_vals[10] >> 32;
        entry->stats.HCOutNoRoutes.low = scan_vals[11] & 0xffffffff;;
        entry->stats.HCOutNoRoutes.high = scan_vals[11] >> 32;
        /* entry->stats. = scan_vals[12]; / * ReasmTimeout */
        entry->stats.ReasmReqds = scan_vals[13];
        entry->stats.ReasmOKs = scan_vals[14];
        entry->stats.ReasmFails = scan_vals[15];
        entry->stats.HCOutFragOKs.low = scan_vals[16] & 0xffffffff;;
        entry->stats.HCOutFragOKs.high = scan_vals[16] >> 32;
        entry->stats.HCOutFragFails.low = scan_vals[17] & 0xffffffff;;
        entry->stats.HCOutFragFails.high = scan_vals[17] >> 32;
        entry->stats.HCOutFragCreates.low = scan_vals[18] & 0xffffffff;;
        entry->stats.HCOutFragCreates.high = scan_vals[18] >> 32;

        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCINRECEIVES] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_INHDRERRORS] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_INADDRERRORS] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCOUTFORWDATAGRAMS] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_INUNKNOWNPROTOS] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_INDISCARDS] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCINDELIVERS] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCOUTREQUESTS] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCOUTDISCARDS] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCOUTNOROUTES] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_REASMREQDS] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_REASMOKS] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_REASMFAILS] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCOUTFRAGOKS] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCOUTFRAGFAILS] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCOUTFRAGCREATES] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_DISCONTINUITYTIME] = 1;
        entry->stats.columnAvail[IPSYSTEMSTATSTABLE_REFRESHRATE] = 1;

        /*
         * load addtional statistics defined by RFC 4293
         * As these are supported linux 2.6.22 or later, it is no problem
         * if loading them are failed.
         */
        _additional_systemstats_v4(entry, load_flags);

        /*
         * add to container
         */
        if (CONTAINER_INSERT(container, entry) < 0)
        {
            DEBUGMSGTL(("access:systemstats:container","error with systemstats_entry: insert into container failed.\n"));
            netsnmp_access_systemstats_entry_free(entry);
        }
    }

    return 0;
}