/*
 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
 *
 * U.S. Government Rights - Commercial software. Government users are subject
 * to the Sun Microsystems, Inc. standard license agreement and applicable
 * provisions of the FAR and its supplements.
 *
 *
 * This distribution may include materials developed by third parties. Sun,
 * Sun Microsystems, the Sun logo and Solaris are trademarks or registered
 * trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
 */


/*
 * Note: this file originally auto-generated by mib2c using
 *        : mib2c.iterate.conf,v 1.1.1.1 2003/03/26 18:12:29 pcarroll Exp $
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "demo_module_2.h"
/********************************************************************/
/* Implemented simple link list for demo purpose                    */
/********************************************************************/

fileEntry* fileList = 0;




int AddItem (char* fileName) {

    fileEntry *fprt = fileList;
    struct stat fAttrib;  /* Need to check if memory is valid */
    if (!fileName || !strlen(fileName)) {
        return FALSE;
    }
    if (stat(fileName, &fAttrib) == -1) {
      /* Unable to get the file information, it could be more than file not exists
        if (errno == ENOENT) {
	    return FALSE;
	}
	return FALSE;
     */
        DEBUGMSGTL(("demo_module_2", "Can't access the file %s", fileName));
    }

    if (fprt != NULL) {
	while (fprt->next != NULL){
	    fprt = fprt->next;
	}
	fprt->next = (fileEntry  *) malloc (sizeof(fileEntry));
	fprt->next->findex = fprt->findex + 1;
	fprt = fprt->next;
	fprt->next = NULL;
	strcpy(fprt->fileName, fileName);
        fprt->fileSize = fAttrib.st_size; 
	sprintf(fprt->filePerm, "%d" , fAttrib.st_mode);
    }
    else {
	fprt = (fileEntry  *) malloc (sizeof(fileEntry));
	fprt->next = NULL;
	fprt->findex = 1;
	strcpy(fprt->fileName, fileName);
        fprt->fileSize = fAttrib.st_size; 
	sprintf(fprt->filePerm, "%d" , fAttrib.st_mode);
	fileList = fprt;
    }
    return TRUE;
}

int ChangeItem (int fileIndex, char* fileName) {

    fileEntry * tempp = fileList, *prev = fileList;
    if (!fileName || !strlen(fileName)) {
	return FALSE;
    }
    while (tempp != NULL) {
	if (tempp->findex == fileIndex) {
	    strcpy(tempp->fileName, fileName);
	    return TRUE;
	}
	prev = tempp;
	tempp = tempp->next;
    }

    return FALSE;
}

char* GetFileName( int fIndex){
    fileEntry *fprt = fileList;
    while (fprt != NULL){
	if (fprt->findex == fIndex) {
	    return fprt->fileName;
	}
	fprt = fprt->next;
    }
    return NULL;

}

/********************************************************************/

/** Initialize the me1FileTable table by defining its contents and how it's structured */
void
initialize_table_me1FileTable(void)
{
    static oid me1FileTable_oid[] = {1,3,6,1,4,1,42,2,2,4,4,1,2,1};
    netsnmp_table_registration_info *table_info;
    netsnmp_handler_registration *my_handler;
    netsnmp_iterator_info *iinfo;

    /* create the table structure itself */
    table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
    iinfo = SNMP_MALLOC_TYPEDEF(netsnmp_iterator_info);

    /* if your table is read only, it's easiest to change the
       HANDLER_CAN_RWRITE definition below to HANDLER_CAN_RONLY */
    my_handler = netsnmp_create_handler_registration("me1FileTable",
                                             me1FileTable_handler,
                                             me1FileTable_oid,
                                             OID_LENGTH(me1FileTable_oid),
                                             HANDLER_CAN_RWRITE);
            
    if (!my_handler || !table_info || !iinfo){
        return; /* mallocs failed */
    }

    /***************************************************
     * Setting up the table's definition
     */
    netsnmp_table_helper_add_indexes(table_info,
                                  ASN_UNSIGNED, /* index: me1FileIndex */
                             0);

    table_info->min_column = 1;
    table_info->max_column = 4;

    /* iterator access routines */
    iinfo->get_first_data_point = me1FileTable_get_first_data_point;
    iinfo->get_next_data_point = me1FileTable_get_next_data_point;

    iinfo->table_reginfo = table_info;

    /***************************************************
     * registering the table with the master agent
     */
    DEBUGMSGTL(("initialize_table_me1FileTable",
                "Registering table me1FileTable as a table iterator\n"));		 
    netsnmp_register_table_iterator(my_handler, iinfo);
}

/** Initializes the demo_module_2 module */
void
init_demo_module_2(void)
{

  /* here we initialize all the tables we're planning on supporting */
    initialize_table_me1FileTable();
    if (!AddItem("/etc/hosts"))
      snmp_log(LOG_ERR, "Failed to add instance in init_demo_module_2\n");
    if (!AddItem("/etc/passwd"))
      snmp_log(LOG_ERR, "Failed to add instance in init_demo_module_2\n");
    if (!AddItem("/etc/cron"))
      snmp_log(LOG_ERR, "Failed to add instance in init_demo_module_2\n");
    if (!AddItem("/etc/system"))
      snmp_log(LOG_ERR, "Failed to add instance in init_demo_module_2\n");
}

/** returns the first data point within the me1FileTable 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 *
me1FileTable_get_first_data_point(void **my_loop_context, void **my_data_context,
                          netsnmp_variable_list *put_index_data,
                          netsnmp_iterator_info *mydata)
{

    netsnmp_variable_list *vptr;

    fileEntry* firstFile = fileList;
    if (!firstFile) {
	return NULL;
    }

    *my_loop_context = firstFile;
    *my_data_context = firstFile;


    vptr = put_index_data;
    
    snmp_set_var_value(vptr, (u_char *) &fileList->findex, sizeof(fileList->findex));
    vptr = vptr->next_variable;

    return put_index_data;
}

/** functionally the same as me1FileTable_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 and the 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 *
me1FileTable_get_next_data_point(void **my_loop_context, void **my_data_context,
                         netsnmp_variable_list *put_index_data,
                         netsnmp_iterator_info *mydata)
{

    netsnmp_variable_list *vptr;
    fileEntry *nextNode = (fileEntry *) *my_loop_context;
    nextNode = nextNode->next; 

    if (!nextNode) {
	return NULL;
    }
    *my_loop_context = nextNode;
    *my_data_context = nextNode;

    vptr = put_index_data;
    
    snmp_set_var_value(vptr, (u_char *) &nextNode->findex, sizeof(nextNode->findex));
    vptr = vptr->next_variable;

    return put_index_data;
}

/** handles requests for the me1FileTable table, if anything else needs to be done */
int
me1FileTable_handler(
    netsnmp_mib_handler               *handler,
    netsnmp_handler_registration      *reginfo,
    netsnmp_agent_request_info        *reqinfo,
    netsnmp_request_info              *requests) {

    netsnmp_request_info *request;
    netsnmp_table_request_info *table_info;
    netsnmp_variable_list *var;
    fileEntry *data;
    char*   fileName = NULL;
    char* undofn;
    
        for(request = requests; request; request = request->next) {
        var = request->requestvb;
        if (request->processed != 0)
            continue;

        /* perform anything here that you need to do before each
           request is processed. */

        /* the following extracts the my_data_context pointer set in
           the loop functions above.  You can then use the results to
           help return data for the columns of the me1FileTable table in question */
        data = (fileEntry *) netsnmp_extract_iterator_context(request);
        if ( data == NULL) {
            if (reqinfo->mode == MODE_GET) {
                netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE);
            } else {
                netsnmp_set_request_error(reqinfo, request,
                                          SNMP_ERR_NOCREATION); 
            }
            continue;
        } else {
	    struct stat fAttrib;
	    if (stat(GetFileName(data->findex), &fAttrib) != -1) {
		data->fileSize = fAttrib.st_size; 
		sprintf(data->filePerm, "%o" , fAttrib.st_mode & 0777);
	    } else {
		data->fileSize = 0; 
		sprintf(data->filePerm, "%d" , -1);
	    }

	}

        /* extracts the information about the table from the request */
        table_info = netsnmp_extract_table_info(request);
        /* table_info->colnum contains the column number requested */
        /* table_info->indexes contains a linked list of snmp variable
           bindings for the indexes of the table.  Values in the list
           have been set corresponding to the indexes of the
           request */
        if (table_info==NULL) {
            continue;
        }

        switch(reqinfo->mode) {
            /* the table_iterator helper should change all GETNEXTs
               into GETs for you automatically, so you don't have to
               worry about the GETNEXT case.  Only GETs and SETs need
               to be dealt with here */
            case MODE_GET:
                switch(table_info->colnum) {
                    case COLUMN_ME1FILEINDEX:
/* ASN_UNSIGNED */
                        snmp_set_var_typed_value(var, ASN_UNSIGNED, (u_char *) &data->findex, sizeof(data->findex));
                        break;

                    case COLUMN_ME1FILENAME:
                        snmp_set_var_typed_value(var, ASN_OCTET_STR, (u_char *) data->fileName, strlen(data->fileName));
                        break;

                    case COLUMN_ME1FILESIZE:
                        snmp_set_var_typed_value(var, ASN_UNSIGNED, (u_char *) &data->fileSize, sizeof(data->fileSize));
                        break;

                    case COLUMN_ME1FILEPERM:
                        snmp_set_var_typed_value(var, ASN_OCTET_STR, (u_char *) data->filePerm, strlen(data->filePerm));
                        break;

                    default:
                        /* We shouldn't get here */
                        snmp_log(LOG_ERR, "problem encountered in me1FileTable_handler: unknown column\n");
                }
                break;

            case MODE_SET_RESERVE1:
                /* set handling... */
                switch(table_info->colnum) {
/*
 *            Check that the value being set is acceptable
 */
                case COLUMN_ME1FILENAME:
		    if (var->type != ASN_OCTET_STR) {
			DEBUGMSGTL(("me1FileTable", "%x not octet string type", var->type));
                        netsnmp_set_request_error(reqinfo, request, SNMP_ERR_WRONGTYPE); 
			return SNMP_ERR_WRONGTYPE;
		    }
                    if (!var->val.string) {
                        DEBUGMSGTL(("me2FileTable", "Empty file name"));
                        netsnmp_set_request_error(reqinfo, request, SNMP_ERR_WRONGVALUE); 
                        return SNMP_ERR_WRONGVALUE;
                    }
                    break;
                default:
                    /* We shouldn't get here */
                    snmp_log(LOG_ERR, "problem encountered in me1FileTable_handler: unknown column\n");
                    netsnmp_set_request_error(reqinfo, request, SNMP_ERR_READONLY); 
                    return SNMP_ERR_NOTWRITABLE; 
                }
		break;
	    case MODE_SET_RESERVE2:
/*
 *              This is conventially where any necesary
 *              resources are allocated (e.g. calls to malloc)
 */

/*                 Store old info for undo later  */
        
                undofn = GetFileName(data->findex); 
                if (undofn) {
                    if (!(fileName = strdup(undofn))){
                        netsnmp_set_request_error(reqinfo, request,
                                      SNMP_ERR_RESOURCEUNAVAILABLE);
                    } else
                         netsnmp_request_add_list_data(request,
                                        netsnmp_create_data_list
                                       (ME1FILE_SET_FILENAME, fileName,
                                        free));

                } 
		break;
            case MODE_SET_FREE:
/*
 *             This is where any of the above resources
 *             are freed again (because one of the other
 *             values being SET failed for some reason).
 */
                /*The netsnmp_free_list_data should take care of the
                alocated resources */
		break;
            case MODE_SET_ACTION:
/*
 *             Set the variable as requested.
 *             Note that this may need to be reversed,
 *             so save any information needed to do this.
 */
                if (!ChangeItem(data->findex, (char *)var->val.string)){
                    netsnmp_set_request_error(reqinfo, request, 
                                              SNMP_ERR_COMMITFAILED);
                }

                break;

            case MODE_SET_COMMIT:
/*
 *             Everything worked, so we can discard any
 *             saved information, and make the change
 *             permanent (e.g. write to the config file).
 *             We also free any allocated resources.
 *
 */
                /*The netsnmp_free_list_data should take care of the
                alocated resources */
            break;            
            case MODE_SET_UNDO:
/*
 *             Something failed, so re-set the
 *             variable to its previous value
 *             (and free any allocated resources).
 */
                
                if(GetFileName(data->findex)){
                    /*******  Get the saved value ************/
                    undofn = (char *) netsnmp_request_get_list_data(request,
                                                           ME1FILE_SET_FILENAME);
                    if (!ChangeItem(data->findex, undofn)){
                        netsnmp_set_request_error(reqinfo, request, 
                                                  SNMP_ERR_UNDOFAILED);
                    }
                }
               break;

            default:
                snmp_log(LOG_ERR, "problem encountered in me1FileTable_handler: unsupported mode\n");
        }
    }
    return SNMP_ERR_NOERROR;
}
