
/*
 * Note: this file originally auto-generated by mib2c using
 *        : mib2c.access_functions.conf,v 1.3 2003/05/31 00:11:57 hardaker Exp $
 */

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "netSnmpHostsTable_access.h"
#include "netSnmpHostsTable_enums.h"
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>

#define MAX_HOSTS_LINE 4096

/* XXX: make .conf token */
#define HOSTS_FILE "/etc/hosts"

typedef struct my_loop_info_s {
   FILE *filep;
   in_addr_t theaddr;
   char line[MAX_HOSTS_LINE];
   char hostname[64];
   int lineno;
   char *current_ptr;
} my_loop_info;

typedef struct my_data_info_s {
   in_addr_t theaddr;
   in_addr_t theoldaddr;
   char hostname[64];
   int lineno;
} my_data_info;   

/** NOTE:
 * - these get_ routines MUST return data that will not be freed (ie,
 *   use static variables or persistent data).  It will be copied, if
 *   needed, immediately after the get_ routine has been called.
 * - these SET routines must copy the incoming data and can not take
 *   ownership of the memory passed in by the val pointer.
 */


/** returns the first data point within the netSnmpHostsTable table data.

    Set the my_loop_context variable to the first data point structure
    of your choice (from which you can find the next one).  This could
    be anything from the first node in a linked list, to an integer
    pointer containing the beginning of an array variable.

    Set the my_data_context variable to something to be returned to
    you later that will provide you with the data to return in a given
    row.  This could be the same pointer as what my_loop_context is
    set to, or something different.

    The put_index_data variable contains a list of snmp variable
    bindings, one for each index in your table.  Set the values of
    each appropriately according to the data matching the first row
    and return the put_index_data variable at the end of the function.
*/
netsnmp_variable_list *
netSnmpHostsTable_get_first_data_point(void **my_loop_context,
                                       void **my_data_context,
                                       netsnmp_variable_list *
                                       put_index_data,
                                       netsnmp_iterator_info *mydata)
{
    my_loop_info *loopctx;

    loopctx = SNMP_MALLOC_TYPEDEF(my_loop_info);

    if (!loopctx)
        return NULL; /*XXX log err */

    loopctx->filep = fopen("/etc/hosts","r");

    if (!loopctx->filep) {
        free(loopctx);
        return NULL;
    }

    /* at this point, we need to get the first name and address from
       the file.  But since our get_next_data_point function does
       this, we'll use it instead of duplicating code */
    *my_loop_context = loopctx;

    return netSnmpHostsTable_get_next_data_point(my_loop_context,
                                                 my_data_context,
                                                 put_index_data,
                                                 mydata);
}

/** functionally the same as netSnmpHostsTable_get_first_data_point, but
   my_loop_context has already been set to a previous value and should
   be updated to the next in the list.  For example, if it was a
   linked list, you might want to cast it to your local data type and
   then return my_loop_context->next.  The my_data_context pointer
   should be set to something you need later and the indexes in
   put_index_data updated again. */
netsnmp_variable_list *
netSnmpHostsTable_get_next_data_point(void **my_loop_context,
                                      void **my_data_context,
                                      netsnmp_variable_list *
                                      put_index_data,
                                      netsnmp_iterator_info *mydata)
{
    my_loop_info *loopctx = *my_loop_context;
    char tmpstring[64];

    if (!loopctx)
        return NULL;

    while(loopctx->filep) {
        if (!loopctx->current_ptr) {
            if (!fgets(loopctx->line, sizeof(loopctx->line), loopctx->filep)) {
                /* we're done */
                fclose(loopctx->filep);
                loopctx->filep = NULL;
                return NULL;
            }
            loopctx->lineno++;
            loopctx->current_ptr = loopctx->line;
            loopctx->current_ptr = skip_white(loopctx->current_ptr);

            if (loopctx->current_ptr == NULL || *loopctx->current_ptr == '#') {
                loopctx->current_ptr = NULL;
                continue;
            }

            loopctx->current_ptr =
                copy_nword(loopctx->current_ptr, tmpstring, sizeof(tmpstring));
            loopctx->theaddr = inet_addr(tmpstring);

            if (!loopctx->current_ptr)
                continue;
        }

        loopctx->current_ptr =
            copy_nword(loopctx->current_ptr, loopctx->hostname, sizeof(loopctx->hostname));
        
        snmp_set_var_value(put_index_data, (u_char *) loopctx->hostname,
                           strlen(loopctx->hostname));
        return put_index_data;
    }
    
    /* we're out of data */
    *my_loop_context = NULL;
    return NULL;
}

void *
netSnmpHostsTable_context_convert_function(void *loop_context,
                                           netsnmp_iterator_info *iinfo)
{
    my_loop_info *loopctx = loop_context;
    my_data_info *datactx = SNMP_MALLOC_TYPEDEF(my_data_info);
    if (!datactx)
        return NULL;
    datactx->theoldaddr = datactx->theaddr = loopctx->theaddr;
    datactx->lineno = loopctx->lineno;
    strcpy(datactx->hostname, loopctx->hostname);
    return datactx;
}

/** Create a data_context for non-existent rows that SETs are performed on.
 *  return a void * pointer which will be passed to subsequent get_XXX
 *  and set_XXX functions for data retrieval and modification during
 *  this SET request.
 *
 *  The indexs are encoded (in order) into the index_data pointer if it
 *  would be helpful to use that information.
 */
void           *
netSnmpHostsTable_create_data_context(netsnmp_variable_list * index_data)
{
    my_data_info *datactx = SNMP_MALLOC_TYPEDEF(my_data_info);

    if (!datactx)
        return NULL;
    strlcpy(datactx->hostname, (const char *) index_data->val.string,
            sizeof(datactx->hostname));
    return datactx;
}

void
netSnmpHostsTable_data_free(void *data, netsnmp_iterator_info *iinfo)
{
    free(data);
}

void
netSnmpHostsTable_loop_free(void *loopctx, netsnmp_iterator_info *iinfo)
{
    free(loopctx);
}

/** If the implemented set_* functions don't operate directly on the
   real-live data (which is actually recommended), then this function
   can be used to take a given my_data_context pointer and "commit" it
   to wherever the modified data needs to be put back to.  For
   example, if this was a routing table you could publish the modified
   routes back into the kernel at this point.

   rowStatus will be set to 1 if new, 0 if not or -1 if it should
   be deleted.

   If you free the data yourself, make sure to *my_data_context = NULL */
int
netSnmpHostsTable_commit_row(void **my_data_context, int new_or_del)
{
    /** Add any necessary commit code here */
    FILE *in, *out;
    char line[MAX_HOSTS_LINE], line2[MAX_HOSTS_LINE];
    char myaddr[64], *cp;
    my_data_info *datactx = *my_data_context;
    size_t line2_sz;
    int foundit = 0;

    if (datactx->theaddr == datactx->theoldaddr && new_or_del != -1)
        return SNMP_ERR_NOERROR; /* no change in the value */

    if ((out = fopen(HOSTS_FILE ".snmp", "w")) == NULL)
        return SNMP_ERR_COMMITFAILED;
    
    if ((in = fopen(HOSTS_FILE, "r")) == NULL) {
        fclose(out);
        return SNMP_ERR_COMMITFAILED;
    }

    while(fgets(line, sizeof(line), in)) {
        copy_nword(line,myaddr,sizeof(myaddr));
        if (inet_addr(myaddr) == datactx->theaddr && new_or_del != -1) {
            foundit = 1;
            /* right line to append to */
            line[strlen(line)-1] = '\0'; /* nuke the new line */
            fprintf(out, "%s %s\n", line, datactx->hostname);
        } else if (inet_addr(myaddr) == datactx->theoldaddr) {
            /* find and remove the name from the current line */
            int count = 0;
            cp = copy_nword(line, line2, sizeof(line2)); /* pass the addr */
            if (strlen(line2) > sizeof(line2)-2) {
              errorit:
                fclose(in);
                fclose(out);
                unlink(HOSTS_FILE ".snmp");
                return SNMP_ERR_RESOURCEUNAVAILABLE;
            }
            line2_sz = strlen(line2);
            line2[line2_sz++] = '\t';
            while(cp) {
                cp = copy_nword(cp, &line2[line2_sz], sizeof(line2)-line2_sz);
                if (strcmp(&line2[line2_sz], datactx->hostname) == 0) {
                    /* a match, so don't add it to line2 (which means
                       don't update the write line2_sz index */
                } else {
                    if (strlen(line2) > sizeof(line2)-2) {
                        goto errorit;
                    }
                    line2_sz = strlen(line2);
                    line2[line2_sz++] = ' ';
                    count++;
                }
            }
            if (count) {
                /* at least one name was still present on the line, so
                   save it to the new file */
                line2[line2_sz] = '\0';
                fprintf(out, "%s\n", line2);
            }
        } else {
            fputs(line, out);
        }
    }

    if (!foundit && new_or_del != -1) {
        /* couldn't add it to an existing line, so append a new one */
        fprintf(out, "%d.%d.%d.%d\t%s\n",
                (0x000000ff & datactx->theaddr),
                (0x0000ff00 & datactx->theaddr) >> 8,
                (0x00ff0000 & datactx->theaddr) >> 16,
                (0xff000000 & datactx->theaddr) >> 24,
                datactx->hostname);
    }
    fclose(out); /* close out first to minimize race condition */
    fclose(in);
    /*
     * race condition here - someone else could open the file after
     *  we close it but before we can rename it.
     */
    if (!rename(HOSTS_FILE ".snmp", HOSTS_FILE))
        return SNMP_ERR_COMMITFAILED;
        
    /*
     * return no errors.  And there shouldn't be any!!!  Ever!!!  You
     * should have checked the values long before this. 
     */
    return SNMP_ERR_NOERROR;
}


/*
 * User-defined data access functions (per column) for data in table
 * netSnmpHostsTable
 */


long           *
get_netSnmpHostAddressType(void *data_context, size_t * ret_len)
{
    static long ret = NETSNMPHOSTADDRESSTYPE_IPV4;
    *ret_len = sizeof(ret);
    return &ret;
}

int
set_netSnmpHostAddressType(void *data_context, long *val, size_t val_len)
{
    return SNMP_ERR_NOERROR; /* always ipv4 */
}

char           *
get_netSnmpHostAddress(void *data_context, size_t * ret_len)
{
    my_data_info *datainfo = data_context;
    *ret_len = sizeof(in_addr_t);  /* XXX: make sure it's 4 */
    return (char *) &datainfo->theaddr;
}

int
set_netSnmpHostAddress(void *data_context, char *val, size_t val_len)
{
    my_data_info *datainfo = data_context;
    memcpy(&datainfo->theaddr, val, val_len);
    return SNMP_ERR_NOERROR;
}

long           *
get_netSnmpHostStorage(void *data_context, size_t * ret_len)
{
    static long ret = ST_NONVOLATILE;
    *ret_len = sizeof(ret);
    return &ret;
}

int
set_netSnmpHostStorage(void *data_context, long *val, size_t val_len)
{
    return SNMP_ERR_NOERROR;
}

long           *
get_netSnmpHostRowStatus(void *data_context, size_t * ret_len)
{
    static long ret = RS_ACTIVE;
    *ret_len = sizeof(ret);
    return &ret;
}

int
set_netSnmpHostRowStatus(void *data_context, long *val, size_t val_len)
{
    /* XXX */
    return SNMP_ERR_NOERROR;
}
