/*****************************************************************************
* Copyright (c) 2004 Digi International Inc., All Rights Reserved
* 
* This software contains proprietary and confidential information of Digi
* International Inc.  By accepting transfer of this copy, Recipient agrees
* to retain this software in confidence, to prevent disclosure to others,
* and to make no use of this software other than that for which it was
* delivered.  This is an unpublished copyrighted work of Digi International
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
* prohibited.
* 
* Restricted Rights Legend
*
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
* Technical Data and Computer Software clause at DFARS 252.227-7031 or
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -
* Restricted Rights at 48 CFR 52.227-19, as applicable.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
*
*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "Npttypes.h"
#include "tx_api.h"
#include "sockapi.h"
#include "snmpv3api.h"
#include "appconf_api.h"
#include "snmpapi.h"
#include "cli.h"


#define MAX_PARAMS          (50)
#define UNKNOWN_COMMAND     (-1)
#define MAX_COMMANDS        (asizeof(command))
#define HELP_INDEX          (0)
#define STACK_SIZE          (16000)
#define MAX_LINE_SIZE       (250)
#define FAILURE             (-1)
#define MAX_ENUMS           (10)

#define AGENT_SOURCE_ADDR       0x0a342079     /* 10.52.32.121 */
#define MY_ENTERPRISE_NUMBER    6000

/*
 * Useful consts for CLI.
 */
static const char cr = '\r';
static const char lf = '\n';
static const char tab = '\t';
static const char space = ' ';
static const char backspace = 8;
static const char prompt[] = ";-> ";
static const char newline[] = "\r\n";
static const char delimiters[] = " \t\r\n";
static const char ERROR_MARKER[] = "*** ERROR ***";

typedef struct commandStruct commandType;

static void help(commandType *cmd, char *arg[], int argCount);
static void showVersions(commandType *cmd, char *arg[], int argCount);
static void setVersions(commandType *cmd, char *arg[], int argCount);
static void engineInfo(commandType *cmd, char *arg[], int argCount);
static void startSnmp(commandType *cmd, char *arg[], int argCount);
static void setId(commandType *cmd, char *arg[], int argCount);
static void setBoots(commandType *cmd, char *arg[], int argCount);
static void listUsm(commandType *cmd, char *arg[], int argCount);
static void listVtf(commandType *cmd, char *arg[], int argCount);
static void listAccess(commandType *cmd, char *arg[], int argCount);
static void listS2g(commandType *cmd, char *arg[], int argCount);
static void setS2g(commandType *cmd, char *arg[], int argCount);
static void setVtf(commandType *cmd, char *arg[], int argCount);
static void setAccess(commandType *cmd, char *arg[], int argCount);
static void updateUsm(commandType *cmd, char *arg[], int argCount);
static void deleteS2g(commandType *cmd, char *arg[], int argCount);
static void deleteVtf(commandType *cmd, char *arg[], int argCount);
static void deleteAccess(commandType *cmd, char *arg[], int argCount);
static void deleteUsm(commandType *cmd, char *arg[], int argCount);
static void createUsm(commandType *cmd, char *arg[], int argCount);
static void setContext(commandType *cmd, char *arg[], int argCount);
static void listContext(commandType *cmd, char *arg[], int argCount);
static void deleteContext(commandType *cmd, char *arg[], int argCount);
static void sendTrap(commandType *cmd, char *arg[], int argCount);


static TX_THREAD cliThread;
static WORD32 cliStack[STACK_SIZE/4];   /* using WORD32 to force stack alignment */
static int engineIdSet = FALSE;         /* will be set if user sets engine ID */

int readLine(char *buffer, int maxChars);

/*
 * This structure type is used to create a simple command line interface.
 */
struct commandStruct
{
    char *name;
    void (* fn)(commandType *cmd, char *arg[], int argCount);
    char *usage;
    struct commandStruct *subcommand;
    int subcommandCount;
};

static commandType showCommands[] =
{
    {
        "versions",
        showVersions,
        "show versions",
        NULL,
        0
    },
    {
        "engineInfo",
        engineInfo,
        "show engineInfo",
        NULL,
        0
    }
};

static commandType vacmSetCommands[] =
{
    {
        "s2g",
        setS2g,
        "set vacm s2g",
        NULL,
        0
    },
    {
        "vtf",
        setVtf,
        "set vacm vtf",
        NULL,
        0
    },
    {
        "access",
        setAccess,
        "set vacm access",
        NULL,
        0
    },
    {
        "context",
        setContext,
        "set vacm context",
        NULL,
        0
    }

};


static commandType vacmDeleteCommands[] =
{
    {
        "s2g",
        deleteS2g,
        "delete vacm s2g",
        NULL,
        0
    },
    {
        "vtf",
        deleteVtf,
        "delete vacm vtf",
        NULL,
        0
    },
    {
        "access",
        deleteAccess,
        "delete vacm access",
        NULL,
        0
    },
    {
        "context",
        deleteContext,
        "delete vacm context",
        NULL,
        0
    }
};


static commandType setCommands[] = 
{
    {
        "versions",
        setVersions,
        "set versions",
        NULL,
        0
    },
    {
        "id",
        setId,
        "set id",
        NULL,
        0
    },
    {
        "boots",
        setBoots,
        "set boots n",
        NULL,
        0
    },
    {
        "vacm",
        NULL,
        "set vacm",
        vacmSetCommands,
        asizeof(vacmSetCommands)
    }
};

static commandType deleteCommands[] = 
{
    {
        "user",
        deleteUsm,
        "delete user",
        NULL,
        0
    },
    {
        "vacm",
        NULL,
        "delete vacm",
        vacmDeleteCommands,
        asizeof(vacmDeleteCommands)
    }
};

static commandType vacmListCommands[] =
{
    {
        "vtf",
        listVtf,
        "list vacm vtf",
        NULL,
        0
    },
    {
        "s2g",
        listS2g,
        "list vacm s2g",
        NULL,
        0
    },
    {
        "access",
        listAccess,
        "list vacm access",
        NULL,
        0
    },
    {
        "context",
        listContext,
        "list vacm context",
        NULL,
        0
    }
};


static commandType listCommands[] =
{
    {
        "users",
        listUsm,
        "list users",
        NULL,
        0
    },
    {
        "vacm",
        NULL,
        "list vacm",
        vacmListCommands,
        asizeof(vacmListCommands)
    }
};


static commandType command[] =
{
    {
        "help",
        help,
        "help [command]",
        NULL,
        0
    },
    {
        "show",
        NULL,
        "show subcommand",
        showCommands,
        asizeof(showCommands)
    },
    {
        "set",
        NULL,
        "set subcommand",
        setCommands,
        asizeof(setCommands)
    },
    {
        "create",
        createUsm,
        "create",
        NULL,
        0
    },
    {
        "update",
        updateUsm,
        "update",
        NULL,
        0
    },
    {
        "delete",
        NULL,
        "delete subcommand",
        deleteCommands,
        asizeof(deleteCommands)
    },
    {
        "start",
        startSnmp,
        "start",
        NULL,
        0
    },
    {
        "list",
        NULL,
        "list subcommand",
        listCommands,
        asizeof(listCommands)
    },
    {
        "sendTrap",
        sendTrap,
        "sendTrap genericTrap interfaceIndex",
        NULL,
        0
    }
};

/*
 * This structure type defines look up tables used
 * to translate enumerated varies into strings and
 * vice versa.
 */
typedef struct
{
    char *name;
    char const *lookupTable[MAX_ENUMS];
    unsigned tableSize;
} lookupTableType;

/*
 * The string arrays below are lookup tables used to
 * translate enumerated values into strings.
 */
static const lookupTableType contextMatchTable =
{
    "context match",
    {
        ERROR_MARKER,
        "exact",
        "prefix"
    },
    3
};

static const lookupTableType storageTypeTable =
{
    "storage type",
    {
        ERROR_MARKER,
        "none",
        "volatile",
        "nonvolatile",
        "permanent",
        "read-only"
    },
    6
};

static const lookupTableType securityModelTable =
{
    "security model",
    {
        "any",
        "SNMPv1",
        "SNMPv2c",
        "USM"
    },
    4
};

static const lookupTableType securityLevelTable =
{
    "security level",
    {
        ERROR_MARKER,
        "none",
        "authentication",
        "authentication-and-privacy"
    },
    4
};


static const lookupTableType privProtocolTable =
{
    "privacy protocol",
    {
        "none",
        "DES"
    },
    2
};

static const lookupTableType authProtocolTable =
{
    "authentication protocol",
    {
        "none",
        "HMACMD5",
    },
    3
};


static const lookupTableType vacmTypeTable =
{
    "VACM type",
    {
        ERROR_MARKER,
        "included",
        "excluded"
    },
    3
};


static const lookupTableType rowStatusTypeTable =
{
    "row status",
    {
        "NO_CHANGE",
        "ACTIVE",
        "NOT_IN_SERVICE",
        "NOT_READY",
        "CREATE_AND_GO",
        "CREATE_AND_WAIT",
        "DESTROY"
    },
    7
};


#define SECURE_USER_NAME        "secureUser"
#define AUTH_USER_NAME          "authUser"
#define INSECURE_USER_NAME      "insecureUser"
#define AUTH_PASSWORD           "auth_key"
#define PRIV_PASSWORD           "priv_key"
#define PUBLIC_VALUE            "public"
#define RW_ENTIRE_MIB           "allOfMib_RW"
#define ALL_MIB                 "allmib"
#define SECURE_READ_VIEW        ALL_MIB
#define SECURE_WRITE_VIEW       ALL_MIB
#define SECURE_NOTIFY_VIEW      ALL_MIB

BYTE annas_key[24] = {0x18, 0x22, 0x39, 0x4e, 0x03, 0xf8, 0xec, 0x30, 0x87, 0xe7, 0x85, 0xbc, 0x11, 0xf8, 0x4d, 0x85};
/*
 * User with authentication and privacy.
 */
#if 1
NaSnmpUsmEntry_t secureUser =
{
    SECURE_USER_NAME,
    (sizeof SECURE_USER_NAME) - 1,
    SECURE_USER_NAME,
    (sizeof SECURE_USER_NAME) - 1,
    {1,3,6,1,4,1,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    8,
    NA_SNMP_HMACMD5_AUTH_PROTOCOL,
    AUTH_PASSWORD,
    (sizeof AUTH_PASSWORD) - 1,
    "",
    0,
    NA_SNMP_DES_PRIV_PROTOCOL,
    PRIV_PASSWORD,
    (sizeof PRIV_PASSWORD) - 1,
    "",
    0,
    PUBLIC_VALUE,
    (sizeof PUBLIC_VALUE) - 1,
    NA_SNMP_PERMANENT,
    NA_SNMP_ROW_STATUS_ACTIVE
};
#else

NaSnmpUsmEntry_t secureUser =
{
    SECURE_USER_NAME,
    (sizeof SECURE_USER_NAME) - 1,
	SECURE_USER_NAME,
    (sizeof SECURE_USER_NAME) - 1,
    {1,3,6,1,4,1,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    8,
    NA_SNMP_HMACMD5_AUTH_PROTOCOL,
    "",
    0,
    {0x18, 0x22, 0x39, 0x4e, 0x03, 0xf8, 0xec, 0x30, 0x87, 0xe7, 0x85, 0xbc, 0x11, 0xf8, 0x4d, 0x85},
    24,
    NA_SNMP_DES_PRIV_PROTOCOL,
    PRIV_PASSWORD,
    (sizeof PRIV_PASSWORD) - 1,
    "",
    0,
    PUBLIC_VALUE,
    (sizeof PUBLIC_VALUE) - 1,
    NA_SNMP_PERMANENT,
    NA_SNMP_ROW_STATUS_ACTIVE
};
#endif

NaSnmpUsmEntry_t authUser =
{
    AUTH_USER_NAME,
    (sizeof AUTH_USER_NAME) - 1,
    AUTH_USER_NAME,
    (sizeof AUTH_USER_NAME) - 1,
    {1,3,6,1,4,1,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    8,
    NA_SNMP_HMACMD5_AUTH_PROTOCOL,
    AUTH_PASSWORD,
    (sizeof AUTH_PASSWORD) - 1,
    "",
    0,
    NA_SNMP_NO_PRIV_PROTOCOL,
    "",
    0,
    "",
    0,
    PUBLIC_VALUE,
    (sizeof PUBLIC_VALUE) - 1,
    NA_SNMP_PERMANENT,
    NA_SNMP_ROW_STATUS_ACTIVE
};

/*
 * User with no authentication or privacy.
 */
NaSnmpUsmEntry_t insecureUser =
{
    INSECURE_USER_NAME,
    (sizeof INSECURE_USER_NAME) - 1,
    INSECURE_USER_NAME,
    (sizeof INSECURE_USER_NAME) - 1,
    {1,3,6,1,4,1,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
    8,
    NA_SNMP_NO_AUTH_PROTOCOL,
    "",
    0,
    "",
    0,
    NA_SNMP_NO_PRIV_PROTOCOL,
    "",
    0,
    "",
    0,
    PUBLIC_VALUE,
    (sizeof PUBLIC_VALUE) - 1,
    NA_SNMP_PERMANENT,
    NA_SNMP_ROW_STATUS_ACTIVE
};

NaSnmpVacmSecurityToGroupEntry_t s2gForSecureUser =
{
    NA_SNMP_VACM_SECURITY_MODEL_USM,
    SECURE_USER_NAME,
    (sizeof SECURE_USER_NAME) - 1,
    RW_ENTIRE_MIB,
    (sizeof RW_ENTIRE_MIB) - 1,
    NA_SNMP_PERMANENT
};

NaSnmpVacmAccessEntry_t accessForSecureUser =
{
    RW_ENTIRE_MIB,
    (sizeof RW_ENTIRE_MIB) - 1,
    "",
    0,
    NA_SNMP_VACM_SECURITY_MODEL_USM,
    NA_SNMP_VACM_SECURITY_LEVEL_AUTH_NO_PRIV,
    NA_SNMP_VACM_ACCESS_CONTEXT_MATCH_EXACT,
    SECURE_READ_VIEW,
    (sizeof SECURE_WRITE_VIEW) - 1,
    SECURE_WRITE_VIEW,
    (sizeof SECURE_WRITE_VIEW) - 1,
    SECURE_NOTIFY_VIEW,
    (sizeof SECURE_NOTIFY_VIEW) - 1,
    NA_SNMP_PERMANENT    
};    



/*
 * Translate a numeric error code into a string.
 */
char *getErrorString(NaStatus code)
{
    char *errorString[] =
    {
        "NA_SUCCESS",
        "NA_SNMP_BAD_PARAMETER",
        "NA_SNMP_OUT_OF_RESOURCES",
        "NA_SNMP_INDEX_OUT_OF_RANGE",
        "NA_SNMP_SET_FAILED",
        "NA_SNMP_DELETE_FAILED",
        "NA_SNMP_ALREADY_STARTED",
        "NA_SNMP_AGENT_NOT_RUNNING",
        "NA_SNMP_CLONE_NOT_FOUND",
        "NA_SNMP_DUP_USER",
        "NA_SNMP_MUST_SUPPLY_PRIV_KEY",
        "NA_SNMP_MUST_SUPPLY_AUTH_KEY"
    };
    char *result;

    if (code >= asizeof(errorString))
    {
        static char buffer[40];
        
        sprintf (buffer, "Unexpected error code:  %d", (int) code);
        result = buffer;
    }
    else
    {
        result = errorString[code];
    }
    
    return result;
}


/*
 * Print an error message given an error code.
 */
static void printError(NaStatus ccode)
{
    fputs("***ERROR:  ", stdout);
    fputs(getErrorString(ccode), stdout);
    fputs("\n", stdout);
}

/*
 * Print the contents of a byte array as a colon seperated
 * string of hex digits.
 */
static void printHex(BYTE *byte, int count)
{
    int i;
    
    for (i = 0; i < count; i++)
    {
        if (i != 0)
        {
            fputs(":", stdout);
        }
        printf ("%2.2X", byte[i]);
    }
}


static int started = FALSE;

static NaSnmpUsmEntry_t					changed_usm_entry;
static NaSnmpVacmSecurityToGroupEntry_t changed_vacm_s2g_entry;
static NaSnmpVacmAccessEntry_t			changed_vacm_access_entry;
static NaSnmpVacmViewTreeFamilyEntry_t  changed_vacm_vtf_entry;

void myUserEntryChanged(const NaSnmpUsmEntry_t * entry, BOOLEAN deleted)
{
	memcpy(&changed_usm_entry, entry, sizeof changed_usm_entry);
}
void myVacmS2GEntryChanged(const NaSnmpVacmSecurityToGroupEntry_t * entry, BOOLEAN deleted)
{
	memcpy(&changed_vacm_s2g_entry, entry, sizeof changed_vacm_s2g_entry);
}
void myVacmAccessEntryChanged(const NaSnmpVacmAccessEntry_t * entry, BOOLEAN deleted)
{
	memcpy(&changed_vacm_access_entry, entry, sizeof changed_vacm_access_entry);
}
void myVacmVtfEntryChanged(const NaSnmpVacmViewTreeFamilyEntry_t * entry, BOOLEAN deleted)
{
	memcpy(&changed_vacm_vtf_entry, entry, sizeof changed_vacm_vtf_entry);
}


/*
 * Start the SNMP agent. Default users .
 */
static void startSnmp(commandType *cmd, char *arg[], int argCount)
{    
    /*   Start the SNMP Agent    */
    if (!started)
    {
        started = TRUE;

		naSnmpRegisterUserChangedCallback(myUserEntryChanged);
		naSnmpRegisterVacmS2GChangedCallback(myVacmS2GEntryChanged);
		naSnmpRegisterVacmAccessChangedCallback(myVacmAccessEntryChanged);
        naSnmpRegisterVacmVtfChangedCallback(myVacmVtfEntryChanged);

        naSnmpSetEnterpriseNumber(MY_ENTERPRISE_NUMBER, 8);
        
        if (!engineIdSet)
        {
            struct sockaddr_storage src_addr;
	        memset(&src_addr, 0, sizeof src_addr);
	        src_addr.ss_family = AF_INET;
	        src_addr.addr.ipv4.sin_addr.s_addr  = htonl(AGENT_SOURCE_ADDR);
            /*
             * The SNMP agent will create a bunch of database entries if it is started
             * without any, so we are going to insert our entries first and then 
             * start the SNMP agent.  However, this creates a problem.  Since some
             * of the database entries use the SNMP engine ID as part of their index,
             * and a default engine ID is not set until the engine is started, we must
             * set one now before we create our database entries.
             */

            /* You can use NULL pointer to let Treck use default address */ 
            naSnmpSetDefaultEngineId(&src_addr);
        }

        fputs ("Adding users and security entries\n", stdout);
        /*
         * Create a default user needed for SilverCreek tests.
         */

        if (naSnmpInsertUserEntry(&secureUser) == NA_SUCCESS)
        {
            fputs ("Secure user created.\n", stdout);
        }
        else
        {
            fputs ("Error creating secure user.\n", stdout);
        }

        if (naSnmpInsertUserEntry(&authUser) == NA_SUCCESS)
        {
            fputs ("Authenticated user created.\n", stdout);
        }
        else
        {
            fputs ("Error creating authenticated user.\n", stdout);
        }

        if (naSnmpInsertUserEntry(&insecureUser) == NA_SUCCESS)
        {
            fputs ("Insecure user created.\n", stdout);
        }
        else
        {
            fputs ("Error creating insecure user.\n", stdout);
        }

        if (naSnmpInsertS2GEntry(&s2gForSecureUser) == NA_SUCCESS)
        {
            fputs ("Security to group set up for secure user.\n", stdout);
        }
        else
        {
            fputs ("Error setting up security to group for secure user.\n", stdout);
        }

        if (naSnmpInsertVacmAccessEntry(&accessForSecureUser) == NA_SUCCESS)
        {
            fputs ("Access set up for secure user.\n", stdout);
        }
        else
        {
            fputs ("Error setting up access for secure user.\n", stdout);
        }
        

        if (naSnmpStart() == NA_SUCCESS)
        {
            fputs ("SNMP agent has been started.\n", stdout);
        }
        else
        {
            fputs ("Error starting SNMP agent.\n", stdout);
        }

        fputs ("Access and security to group has NOT been setup for the insecure or authenticated users!\n", stdout);
        fputs ("Use the 'set vacm s2g' and 'set vacm access' commands to do this if desired.\n", stdout);
    }
    else
    {
        fputs ("SNMP agent was already running.\n", stdout);
    }


}

/*
 * Convert a hex digit into a numeric value.
 */
static int hexDigitValue(char digit, int *nibble)
{
    int result = NA_SUCCESS;
    int ucDigit = toupper(digit);
    
    if ((ucDigit >= '0') && (ucDigit <= '9'))
    {
        *nibble = ucDigit - '0';
    }
    else if ((ucDigit >= 'A') && (ucDigit <= 'F'))
    {
        *nibble = (ucDigit - 'A') + 10;
    }
    else
    {
        result = FAILURE;
    }

    return result;
}


/*
 * Parse a colon seperate string of hex values and place the result in
 * an array of bytes.
 */
static int parseHexValues(BYTE *output, char *input, int maxBytes, unsigned *bytesRead)
{
    int result = FAILURE;
    int i;
    const char hexDelimiters[] = ":; ";
    char *byteString = strtok(input, hexDelimiters);
    
    *bytesRead = 0;
    for (i = 0; i < maxBytes; i++)
    {
        int byte = 0;
        int nibble = 0;
        
        if (byteString == NULL)
        {
            result = NA_SUCCESS;
            goto done;
        }
        
        if ((strlen(byteString) > 2))
        {
            goto done;
        }
        
        if (hexDigitValue(*byteString, &nibble) == FAILURE)
        {
            goto done;
        }
        byte = nibble;
        byteString++;
        if (*byteString)
        {
            if (hexDigitValue(*byteString, &nibble) == FAILURE)
            {
                goto done;
            }
            byte <<= 4;
            byte |= nibble;
        }
        output[i] = byte;
        byteString = strtok(NULL, hexDelimiters);
        *bytesRead += 1;
    }
    result = NA_SUCCESS;;
    
done:
    return result;
}



/*
 * Set the SNMP engine ID.
 */
static void setId(commandType *cmd, char *arg[], int argCount)
{
    NaStatus ccode;
    BYTE id[NA_SNMP_ENGINE_ID_LENGTH];
    unsigned idLength;
    static char buffer[MAX_LINE_SIZE];
    unsigned bytesRead = 0;
    
    naSnmpGetEngineInfo(id, &idLength, NULL, NULL);
    fputs("Enter ID as hex byte values seperated by colons.\n", stdout);
    fputs("Old: ", stdout);
    printHex(id, idLength);
    fputs("\nNew: ", stdout);
    readLine(buffer, sizeof buffer);
    if (parseHexValues(id, buffer, idLength, &bytesRead) != NA_SUCCESS)
    {
        fputs("Error in input.  Id has not been changed.\n",stdout);
        goto done;
    }
    ccode = naSnmpSetEngineId(id, idLength);
    if (ccode != NA_SUCCESS)
    {
        printf("naSnmpSetEngineId() returned %s.\n", getErrorString(ccode));
        printError(ccode);
    }
    else
    {
        engineIdSet = TRUE;
    }

done:
    return;
}



/*
 * Set the number of times the agent has been rebooted.
 */
static void setBoots(commandType *cmd, char *arg[], int argCount)
{
    NaStatus ccode;
    int boots;
    
    if (argCount != 1)
    {
        printf("Usage:  %s.\n", cmd->usage);
        goto done;
    }
    
    boots = atoi(arg[0]);
    
    ccode = naSnmpSetEngineBoots(boots);
    if (ccode != NA_SUCCESS)
    {
        printf("naSnmpSetEngineBoots() returned %s.\n", getErrorString(ccode));
        printError(ccode);
    }

done:
    return;
}
   
/*
 * Display the engine ID and number of reboots.
 */
static void engineInfo(commandType *cmd, char *arg[], int argCount)
{
    NaStatus ccode;
    BYTE id[NA_SNMP_ENGINE_ID_LENGTH];
    unsigned idLength;
    unsigned boots;
    unsigned time;
    
    ccode = naSnmpGetEngineInfo(id, &idLength, &boots, &time);

    if (ccode == NA_SUCCESS)
    {
        if (!started)
        {
            /* For version 4.0.2.53 of the Treck SNMP, we use tfGetMibUpTime() */
            /* to obtain the elaped time (in seconds) since the system was up. */
            /* Thus, we set the time variable to 0 until the SNMP agent is up  */
            time = 0;
        }
        fputs ("EngineID = ", stdout);
        printHex(id, idLength);
        fputs ("\n", stdout);
        printf ("Boots = %d\n", boots);
        printf ("Number of seconds since engine was started = %d\n", time);
    }
    else
    {
        printf("naSnmpGetEngineInfo() returned error code %s.\n", getErrorString(ccode));
        printError(ccode);
    }
}


/*
 * Convert an octet string into a nul terminated string.
 */
static char *getLengthString(BYTE *input, unsigned length)
{
    static char buffer[MAX_LINE_SIZE];
    char *result = buffer;
    
    if (length >= MAX_LINE_SIZE)
    {
        result = "string overflows buffer";
    }
    else
    {
        memcpy(buffer, input, length);
        buffer[length] = 0;
    }
    
    return result;
}


/*
 * Convert an OID into a readable nul terminated string.
 */
static char *getOidString(oid *input, unsigned length)
{
    static char buffer[MAX_LINE_SIZE];
    static char smallBuffer[20];
    char *result = buffer;
    int i = 0;
    
    buffer[0] = 0;
    for (i = 0; i < length; i++)
    {
        if (i == 0)
        {
            sprintf(smallBuffer, "%u", (unsigned int) input[i]);
        }
        else
        {
            sprintf(smallBuffer, ".%u", (unsigned int) input[i]);
        }
        if ((strlen(buffer) + strlen(smallBuffer)) >= MAX_LINE_SIZE)
        {
            result = "OID overflows string buffer";
            break;
        }
        strcat(buffer, smallBuffer);
    }
    
    return result;
}



/*
 * Print an octet string.
 */
static void printKey(char *name, BYTE *input, unsigned inputLength)
{
    fputs(name, stdout);
    
    if (inputLength == 0)
    {
        fputs("(empty)", stdout);
    }
    else
    {
        printHex(input, inputLength);
    }
    fputs(newline, stdout);
}




/*
 * Convert an enum value into a string using a look up table.
 */
static char *lookupString(unsigned index, lookupTableType const * table)
{
    char *result;
    
    if (index >= table->tableSize)
    {
        static char buffer[50];
        
        sprintf (buffer, "%s: invalid value %d.\n", ERROR_MARKER, index);
        result = buffer;
    }
    else
    {
        result = (char *) table->lookupTable[index];
    }
    
    return result;
}    

/*
 * List the VACM access entries.
 */
static void listAccess(commandType *cmd, char *arg[], int argCount)
{
    NaStatus ccode;
    unsigned listIndex = 0;
    NaSnmpVacmAccessEntry_t access;
        
    while(1)
    {
        ccode = naSnmpGetVacmAccessEntry(listIndex, &access);
        if (ccode == NA_SUCCESS)
        {
            printf  ("       groupName: %s\n", getLengthString(access.groupName, access.groupNameLength));
            printf  ("   contextPrefix: %s\n", getLengthString(access.contextPrefix, access.contextPrefixLength));
            printf  ("   securityModel: %s\n", lookupString((unsigned) access.securityModel, &securityModelTable));
            printf  ("   securityLevel: %s\n", lookupString((unsigned) access.securityLevel, &securityLevelTable));
            printf  ("    contextMatch: %s\n", lookupString((unsigned) access.contextMatch, &contextMatchTable));
            printf  ("        readView: %s\n", getLengthString(access.readView, access.readViewLength));
            printf  ("       writeView: %s\n", getLengthString(access.writeView, access.writeViewLength));
            printf  ("      notifyView: %s\n", getLengthString(access.notifyView, access.notifyViewLength));
            printf  ("     storageType: %s\n\n", lookupString((unsigned) access.storageType, &storageTypeTable));
            listIndex++;
       }
        else if (ccode == NA_SNMP_INDEX_OUT_OF_RANGE)
        {
            if (listIndex == 0)
            {
                fputs("no access entries in this group\n", stdout);
            }
            break;
        }
        else
        {
            printf("naSnmpGetVacmAccessEntry() returned error code %s.\n", getErrorString(ccode));
            printError(ccode);
            break;
        }
    }
}


/*
 * List the security to group entries.
 */
static void listS2g(commandType *cmd, char *arg[], int argCount)
{
    NaStatus ccode;
    unsigned listIndex = 0;
    NaSnmpVacmSecurityToGroupEntry_t s2g;
    
    while(1)
    {
        ccode = naSnmpGetS2GEntry(listIndex, &s2g);
        if (ccode == NA_SUCCESS)
        {
            printf  ("   securityModel: %s\n", lookupString((unsigned) s2g.securityModel, &securityModelTable));
            printf  ("    securityName: %s\n", getLengthString((BYTE *) s2g.securityName, s2g.securityNameLength));
            printf  ("       groupName: %s\n", getLengthString((BYTE *) s2g.groupName, s2g.groupNameLength));
            printf  ("     storageType: %s\n\n", lookupString((unsigned) s2g.storageType, &storageTypeTable));
            listIndex++;
       }
        else if (ccode == NA_SNMP_INDEX_OUT_OF_RANGE)
        {
            if (listIndex == 0)
            {
                fputs("\nNo users.\n", stdout);
            }
            break;
        }
        else
        {
            printf("naSnmpGetS2GEntry() returned error code %s.\n", getErrorString(ccode));
            printError(ccode);
            break;
        }
    }
}


/*
 * List the view tree family entries.
 */
static void listVtf(commandType *cmd, char *arg[], int argCount)
{
    NaStatus ccode;
    unsigned listIndex = 0;
    NaSnmpVacmViewTreeFamilyEntry_t vacm;
    
    while(1)
    {
        ccode = naSnmpGetVtfEntry(listIndex, &vacm);
        if (ccode == NA_SUCCESS)
        {
            printf  ("            name: %s\n", getLengthString((BYTE *) vacm.name, vacm.nameLength));
            printf  ("         subtree: %s\n", getOidString(vacm.subtree, vacm.subtreeLength));
            printKey("            mask: ", vacm.mask, vacm.maskLength);
            printf  ("            type: %s\n", lookupString((unsigned) vacm.type, &vacmTypeTable));
            printf  ("     storageType: %s\n\n", lookupString((unsigned) vacm.storageType, &storageTypeTable));
            listIndex++;
        }
        else if (ccode == NA_SNMP_INDEX_OUT_OF_RANGE)
        {
            if (listIndex == 0)
            {
                fputs("\nNo users.\n", stdout);
            }
            break;
        }
        else
        {
            printf("naSnmpGetVacmAccessEntry() returned error code %s.\n", getErrorString(ccode));
            printError(ccode);
            break;
        }
    }
}
    



/*
 * List the user table.
 */
static void listUsm(commandType *cmd, char *arg[], int argCount)
{
    NaStatus ccode;
    unsigned listIndex = 0;
    static NaSnmpUsmEntry_t usm;
    
    while(1)
    {
        ccode = naSnmpGetUserEntry(listIndex, &usm);
        if (ccode == NA_SUCCESS)
        {
            printf  ("            name: %s\n", getLengthString((BYTE *) usm.name, usm.nameLength));
            printf  ("    securityName: %s\n", getLengthString((BYTE *) usm.securityName, usm.securityNameLength));
            printf  ("       cloneFrom: %s\n", getOidString(usm.cloneFrom, usm.cloneFromLength));
            printf  ("    authProtocol: %s\n", lookupString((unsigned) usm.authProtocol, &authProtocolTable));
            printf  ("    privProtocol: %s\n", lookupString((unsigned) usm.privProtocol, &privProtocolTable));
            printf  ("     publicValue: %s\n", getLengthString((BYTE *) usm.publicValue, usm.publicValueLength));
            printf  ("      userStatus: %s\n", lookupString((unsigned) usm.userStatus, &rowStatusTypeTable));
            printf  ("     storageType: %s\n\n", lookupString((unsigned) usm.storageType, &storageTypeTable));
            listIndex++;
        }
        else if (ccode == NA_SNMP_INDEX_OUT_OF_RANGE)
        {
            if (listIndex == 0)
            {
                fputs("\nNo users.\n", stdout);
            }
            break;
        }
        else
        {
            printf("naSnmpGetUserEntry() returned error code %s.\n", getErrorString(ccode));
            printError(ccode);
            break;
        }
    }
}

/*
 * Input an octet string from the console as string.
 */
static unsigned readLengthString(char *name, BYTE *dest, unsigned maxLength)
{
    static BYTE buffer[MAX_LINE_SIZE];
    unsigned actualLength;

    fputs("Enter ", stdout);
    fputs(name, stdout);
    fputs(": ", stdout);
    
    readLine((char *) buffer, sizeof buffer);
    buffer[maxLength - 1] = 0;
    actualLength = strlen((char *) buffer);
    
    memcpy(dest, buffer, actualLength);
    if (actualLength < maxLength)
    {
        dest[actualLength] = 0;
    }
    
    return actualLength;
}




/*
 * Prompt for an enumerated value from the console.
 */
unsigned readEnumValue(lookupTableType const *table)
{
    unsigned i;
    static char buffer[MAX_LINE_SIZE];
    unsigned result = 0;
    BOOLEAN gotIt = FALSE;
    
    do
    {
        BOOLEAN first = TRUE;
        char *token;
        const char enumValueDelimiter[] = " \t";
        
        fputs("Enter ", stdout);
        fputs(table->name, stdout);
        fputs(" (", stdout);
        
        for (i = 0; i < table->tableSize; i++)
        {
            if (table->lookupTable[i] != ERROR_MARKER)
            {
                if (!first)
                {
                    fputs(" ", stdout);
                }
                fputs(table->lookupTable[i], stdout);
                first = FALSE;
            }
        }
        fputs("): ", stdout);
        readLine(buffer, sizeof buffer);
        token = strtok(buffer, enumValueDelimiter);
        
        for (i = 0; i < table->tableSize; i++)
        {
            if (table->lookupTable[i] != ERROR_MARKER)
            {
                if (strcmp(token, table->lookupTable[i]) == 0)
                {
                    result = i;
                    gotIt = TRUE;
                    break;
                }
            }
        }
        if (!gotIt)
        {
            printf ("%s invalid entry '%s', try again\n", ERROR_MARKER, token);
        }
    } while (!gotIt);
    
    return result;
}

/*
 * Read an octet string as a set of : seperated hex values.
 */
static unsigned readHexString(char *name, BYTE *dest, unsigned maxLength)
{
    static char buffer[MAX_LINE_SIZE];
    unsigned bytesRead = 0;

    do
    {    
        printf ("Enter %s: ", name);
        readLine(buffer, sizeof buffer);
        if (parseHexValues(dest, buffer, maxLength, &bytesRead) != NA_SUCCESS)
        {
            fputs("Invalid input.  Please enter %s as a colon seperated hex string.\n",stdout);
        }
        else
        {
            break;
        }
    } while(1);
    
    return bytesRead;
}


/*
 * Converte an oid in dotted notation into an oid array.
 */
static int parseOid(oid *dest, char *input, unsigned maxElements)
{
    char oidDelimiter[] = ".";
    char *element;
    int result = FAILURE;
    int pos = 0;
    
    element = strtok(input, oidDelimiter);
    while ((element != NULL) && (pos < maxElements))
    {
        int i;
        
        for (i = 0; i < strlen(element); i++)
        {
            if (!isdigit(element[i]))
            {
                goto done;
            }
        }
        dest[pos] = atoi(element);
        pos++;
        element = strtok(NULL, oidDelimiter);
    }
    
    if ((element == NULL) && (pos > 0))
    {
        result = pos;
    }

done:
    return result;
}



/*
 * Read an oid from the console in dotted notation.
 */
static unsigned readOid(char *name, oid *dest, unsigned maxLength)
{
    static char buffer[MAX_LINE_SIZE];
    int oidLength = 0;

    do
    {
        printf ("Enter %s: ", name);
        readLine(buffer, sizeof buffer);
        oidLength = parseOid(dest, buffer, maxLength);
        if (oidLength == FAILURE)
        {
            fputs("Invalid input.  Please enter %s as a dot seperated OID.\n",stdout);
        }
        else
        {
            break;
        }
    } while(1);
    
    return (unsigned) oidLength;
}

/*
 * Change an existing user entry.
 */
static void updateUsm(commandType *cmd, char *arg[], int argCount)
{
    NaSnmpUsmEntry_t *usm;
    NaStatus ccode;
    
    usm = malloc (sizeof *usm);
    if (usm == NULL)
    {
        fputs(ERROR_MARKER, stdout);
        fputs(": Unable to allocate memory.\n", stdout);
        goto done;
    }
	memset(usm, 0, sizeof *usm);

    usm->nameLength = readLengthString("name", (BYTE *) usm->name, sizeof usm->name);
    usm->securityNameLength = readLengthString("security Name", (BYTE *) usm->securityName, sizeof usm->securityName);
    usm->cloneFromLength = readOid("clone from OID", usm->cloneFrom, asizeof(usm->cloneFrom));
    usm->authProtocol = (NaSnmpAuthProtocol_t) readEnumValue(&authProtocolTable);
    usm->authPasswordLength = readLengthString("auth password", (BYTE *) usm->authPassword, sizeof usm->authPassword);
    usm->privProtocol = (NaSnmpPrivProtocol_t) readEnumValue(&privProtocolTable);
    usm->privPasswordLength = readLengthString("privacy password", (BYTE *) usm->privPassword, sizeof usm->privPassword);
    usm->publicValueLength = readLengthString("public value", (BYTE *) usm->publicValue, sizeof usm->publicValue);
    usm->storageType = (NaSnmpStorageType_t) readEnumValue(&storageTypeTable);
    usm->userStatus = (NaSnmpRowStatusType_t) readEnumValue(&rowStatusTypeTable);
    
/*    ccode = naSnmpUpdateUserEntry(usm); */
    ccode = naSnmpInsertUserEntry(usm);
    if (ccode == NA_SUCCESS)
    {
        fputs("succeeded\n", stdout);
    }
    else
    {
        fputs(ERROR_MARKER, stdout);
        printf("naSnmpUpdateUserEntry returned %s\n", getErrorString(ccode));
    }
    
    free(usm);
    
done:
    return;
}
  


/*
 * Create a new user entry.
 */
static void createUsm(commandType *cmd, char *arg[], int argCount)
{
    NaSnmpUsmEntry_t *usm;
    NaStatus ccode;
    char *op;
    
    usm = malloc (sizeof *usm);
    if (usm == NULL)
    {
        fputs(ERROR_MARKER, stdout);
        fputs(": Unable to allocate memory.\n", stdout);
        goto done;
    }

    usm->nameLength = readLengthString("name", (BYTE *) usm->name, sizeof usm->name);
    usm->userStatus = (NaSnmpRowStatusType_t) readEnumValue(&rowStatusTypeTable);
    usm->storageType = (NaSnmpStorageType_t) readEnumValue(&storageTypeTable);
    usm->securityNameLength = readLengthString("security Name", (BYTE *) usm->securityName, sizeof usm->securityName);
    usm->cloneFromLength = readOid("clone from OID", usm->cloneFrom, asizeof(usm->cloneFrom));
    usm->authProtocol = (NaSnmpAuthProtocol_t) readEnumValue(&authProtocolTable);
    usm->authPasswordLength = readLengthString("auth password", (BYTE *) usm->authPassword, sizeof usm->authPassword);
    usm->privProtocol = (NaSnmpPrivProtocol_t) readEnumValue(&privProtocolTable);
    usm->privPasswordLength = readLengthString("privacy password", (BYTE *) usm->privPassword, sizeof usm->privPassword);
    usm->publicValueLength = readLengthString("public value", (BYTE *) usm->publicValue, sizeof usm->publicValue);
	usm->authKeyLength = 0;
	memset(usm->authKey, 0, sizeof usm->authKey);
	usm->privKeyLength = 0;
	memset(usm->privKey, 0, sizeof usm->privKey);

    ccode = naSnmpInsertUserEntry(usm);
    op = "naSnmpInsertUserEntry()";


    if (ccode == NA_SUCCESS)
    {
        fputs("succeeded\n", stdout);
    }
    else
    {
        fputs(ERROR_MARKER, stdout);
        printf("%s returned %s\n", op, getErrorString(ccode));
    }
    
    free(usm);
    
done:
    return;
}
  

/*
 * Delete a user entry.
 */
static void deleteUsm(commandType *cmd, char *arg[], int argCount)
{
    NaSnmpUsmEntry_t *usm;
    NaStatus ccode;
    BYTE name[NA_SNMP_USM_NAME_LENGTH];
    unsigned length;
    
    usm = malloc (sizeof *usm);
    if (usm == NULL)
    {
        fputs(ERROR_MARKER, stdout);
        fputs(": Unable to allocate memory.\n", stdout);
        goto done;
    }

    length = readLengthString("name", name, sizeof name);

    ccode = naSnmpDeleteUserEntry(name, length);
    if (ccode == NA_SUCCESS)
    {
        fputs("deleted.\n", stdout);
    }
    else
    {
        fputs(ERROR_MARKER, stdout);
        fputs("naSnmpInsertUserEntry() returned ", stdout);
        fputs(getErrorString(ccode), stdout);
        fputs("\n", stdout);
    }
    
    free(usm);
done:
    return;
}
  

/*
 * Create or update a VACM access entry.
 */
static void setAccess(commandType *cmd, char *arg[], int argCount)
{
    NaSnmpVacmAccessEntry_t *access;
    NaStatus ccode;
    
    access = malloc(sizeof *access);
    if (access == NULL)
    {
        fputs(ERROR_MARKER, stdout);
        fputs(": Unable to allocate memory.\n", stdout);
        goto done;
    }

    access->groupNameLength = readLengthString("group name", access->groupName, sizeof access->groupName);
    access->contextPrefixLength = readLengthString("context prefix", access->contextPrefix, sizeof access->contextPrefix);
    access->securityModel = (NaSnmpSecurityModel_t) readEnumValue(&securityModelTable);
    access->securityLevel = (NaSnmpSecurityLevel_t) readEnumValue(&securityLevelTable);
    access->contextMatch = (NaSnmpAccessContextMatch_t) readEnumValue(&contextMatchTable);
    access->readViewLength = readLengthString("read view", access->readView, sizeof access->readView);
    access->writeViewLength = readLengthString("write view", access->writeView, sizeof access->writeView);
    access->notifyViewLength = readLengthString("notify view", access->notifyView, sizeof access->notifyView);
    access->storageType = (NaSnmpStorageType_t) readEnumValue(&storageTypeTable);
    
    ccode = naSnmpInsertVacmAccessEntry(access);
    if (ccode == NA_SUCCESS)
    {
        fputs("succeeded\n", stdout);
    }
    else
    {
        fputs(ERROR_MARKER, stdout);
        fputs("naSnmpInsertVacmAccessEntry() returned ", stdout);
        fputs(getErrorString(ccode), stdout);
        fputs("\n", stdout);
    }
    
    free(access);
done:
    return;
}
    



/*
 * Delete a VACM access entry.
 */
static void deleteAccess(commandType *cmd, char *arg[], int argCount)
{
    NaSnmpVacmAccessEntry_t *access;
    NaStatus ccode;
    
    access = malloc(sizeof *access);
    if (access == NULL)
    {
        fputs(ERROR_MARKER, stdout);
        fputs(": Unable to allocate memory.\n", stdout);
        goto done;
    }

    access->groupNameLength = readLengthString("group name", access->groupName, sizeof access->groupName);
    access->contextPrefixLength = readLengthString("context prefix", access->contextPrefix, sizeof access->contextPrefix);
    access->securityModel = (NaSnmpSecurityModel_t) readEnumValue(&securityModelTable);
    access->securityLevel = (NaSnmpSecurityLevel_t) readEnumValue(&securityLevelTable);
    
    ccode = naSnmpDeleteVacmAccessEntry(access);
    if (ccode == NA_SUCCESS)
    {
        fputs("deleted\n", stdout);
    }
    else
    {
        fputs(ERROR_MARKER, stdout);
        fputs("naSnmpDeleteVacmAccessEntry() returned ", stdout);
        fputs(getErrorString(ccode), stdout);
        fputs("\n", stdout);
    }
    
    free(access);
done:
    return;
}
    



/*
 * Create or update a VACM view tree family entry.
 */
static void setVtf(commandType *cmd, char *arg[], int argCount)
{
    NaSnmpVacmViewTreeFamilyEntry_t *vtf;
    NaStatus ccode;
    
    vtf = malloc(sizeof *vtf);
    if (vtf == NULL)
    {
        fputs(ERROR_MARKER, stdout);
        fputs(": Unable to allocate memory.\n", stdout);
        goto done;
    }

    vtf->nameLength = readLengthString("name", (BYTE *) vtf->name, sizeof vtf->name);
    vtf->subtreeLength = readOid("subtree", vtf->subtree, asizeof(vtf->subtree));
    vtf->maskLength = readHexString("mask", vtf->mask, asizeof(vtf->mask));
    vtf->type = (NaSnmpVacmViewTreeFamilyType_t) readEnumValue(&vacmTypeTable);
    vtf->storageType = (NaSnmpStorageType_t) readEnumValue(&storageTypeTable);
    
    ccode = naSnmpInsertVtfEntry(vtf);
    if (ccode == NA_SUCCESS)
    {
        fputs("succeeded\n", stdout);
    }
    else
    {
        fputs(ERROR_MARKER, stdout);
        fputs("naSnmpInsertVtfEntry() returned ", stdout);
        fputs(getErrorString(ccode), stdout);
        fputs("\n", stdout);
    }
    
    free(vtf);
done:
    return;
}




/*
 * Delete a view tree family entry.
 */
static void deleteVtf(commandType *cmd, char *arg[], int argCount)
{
    NaSnmpVacmViewTreeFamilyEntry_t *vtf;
    NaStatus ccode;
    
    vtf = malloc(sizeof *vtf);
    if (vtf == NULL)
    {
        fputs(ERROR_MARKER, stdout);
        fputs(": Unable to allocate memory.\n", stdout);
        goto done;
    }

    vtf->nameLength = readLengthString("name", (BYTE *) vtf->name, sizeof vtf->name);
    vtf->subtreeLength = readOid("subtree", vtf->subtree, asizeof(vtf->subtree));
    
    ccode = naSnmpDeleteVtfEntry(vtf);
    if (ccode == NA_SUCCESS)
    {
        fputs("deleted\n", stdout);
    }
    else
    {
        fputs(ERROR_MARKER, stdout);
        fputs("naSnmpDeleteVtfEntry() returned ", stdout);
        fputs(getErrorString(ccode), stdout);
        fputs("\n", stdout);
    }
    
    free(vtf);
done:
    return;
}




/*
 * Create or update a VACM security to group entry.
 */
static void setS2g(commandType *cmd, char *arg[], int argCount)
{
    NaSnmpVacmSecurityToGroupEntry_t *s2g;
    NaStatus ccode;
    
    s2g = malloc(sizeof *s2g);
    if (s2g == NULL)
    {
        fputs(ERROR_MARKER, stdout);
        fputs(": Unable to allocate memory.\n", stdout);
        goto done;
    }
    s2g->securityModel = (NaSnmpSecurityModel_t) readEnumValue(&securityModelTable);
    s2g->securityNameLength = readLengthString("security name", (BYTE *) s2g->securityName, sizeof s2g->securityName);
    s2g->groupNameLength = readLengthString("group name", (BYTE *) s2g->groupName, sizeof s2g->groupName);
    s2g->storageType = (NaSnmpStorageType_t) readEnumValue(&storageTypeTable);

    ccode = naSnmpInsertS2GEntry(s2g);
    if (ccode == NA_SUCCESS)
    {
        fputs("succeeded\n", stdout);
    }
    else
    {
        fputs(ERROR_MARKER, stdout);
        fputs(" naSnmpInsertS2GEntry() returned ", stdout);
        fputs(getErrorString(ccode), stdout);
        fputs("\n", stdout);
    }
    
    free(s2g);
done:
    return;
}


/*
 * Delete a VACM security to group entry.
 */
static void deleteS2g(commandType *cmd, char *arg[], int argCount)
{
    NaSnmpVacmSecurityToGroupEntry_t *s2g;
    NaStatus ccode;
    
    s2g = malloc(sizeof *s2g);
    if (s2g == NULL)
    {
        fputs(ERROR_MARKER, stdout);
        fputs(": Unable to allocate memory.\n", stdout);
        goto done;
    }
    s2g->securityModel = (NaSnmpSecurityModel_t) readEnumValue(&securityModelTable);
    s2g->securityNameLength = readLengthString("security name", (BYTE *) s2g->securityName, sizeof s2g->securityName);

    ccode = naSnmpDeleteS2GEntry(s2g);
    if (ccode == NA_SUCCESS)
    {
        fputs("deleted\n", stdout);
    }
    else
    {
        fputs(ERROR_MARKER, stdout);
        fputs(" naSnmpDeleteS2GEntry() returned ", stdout);
        fputs(getErrorString(ccode), stdout);
        fputs("\n", stdout);
    }
    
    free(s2g);
done:
    return;
}


/*
 * Show which versions of the SNMP protocol will be supported.
 */
static void showVersions(commandType *cmd, char *arg[], int argCount)
{
    NaStatus ccode;
    WORD32 versionMask;
    
    ccode = naSnmpGetAllowedVersions(&versionMask);
    if (ccode == NA_SUCCESS)
    {
        printf ("naSnmpGetAllowedVersions() reports 0x%X\n", (unsigned int) versionMask);
        fputs("The following SNMP protocols are allowed:", stdout);
        if (versionMask & NA_SNMP_VERSION_V1_ALLOWED)
        {
            fputs(" v1", stdout);
        }
        if (versionMask & NA_SNMP_VERSION_V2c_ALLOWED)
        {
            fputs(" v2c", stdout);
        }
        if (versionMask & NA_SNMP_VERSION_V3_ALLOWED)
        {
            fputs(" v3", stdout);
        }
        if (versionMask == 0)
        {
            fputs(" none", stdout);
        }
        fputs("\n", stdout);
    }
    else
    {
        printf("naSnmpGetAllowedVersions() returned error code %s.\n", getErrorString(ccode));
        printError(ccode);
    }
}

static void sendTrap(commandType *cmd, char *arg[], int argCount)
{
    NaStatus ret;
    struct variable_t variables[3];
    int oid1value               = 0x1010;
    unsigned long oid1[]        = {1, 3, 6, 1, 4, 1, 6000, 1, 3, 1};
    char oid2value[]            = "Value of OID #2";
    unsigned long oid2[]        = {1, 3, 6, 1, 4, 1, 6000, 1, 3, 2, 1};
    unsigned long oid3value[]   = {1, 3, 6, 1, 4, 1, 6000, 1, 3, 3};
    unsigned long oid3[]        = {1, 3, 6, 1, 4, 1, 6000, 1, 3, 2, 2};
    int genericTrap, ifIndex;
    
    if (argCount != 2)
    {
        printf("\nUsage: sendTrap genericTrap interfaceIndex\n\n");
        return;
    }

    /*  variable 1 is integer 0x1010 at 1.3.6.1.4.1.6000.1.3.1 */
    memcpy (variables[0].value, &oid1value, sizeof (oid1value));
    memcpy (variables[0].oid_value, &oid1, sizeof (oid1));
    variables[0].oid_length     = sizeof (oid1)/sizeof (unsigned long);
    variables[0].value_type     = ASN_INTEGER;
    variables[0].value_length   = sizeof (oid1value)/sizeof (int); 

    /*  variable 2 is string 'Value of OID #2' at 1.3.6.1.4.1.6000.1.3.2.1 */
    memcpy (variables[1].value, &oid2value, strlen (oid2value));
    memcpy (variables[1].oid_value, &oid2, sizeof (oid2));
    variables[1].oid_length     = sizeof (oid2)/sizeof (unsigned long);
    variables[1].value_type     = ASN_OCTET_STR;
    variables[1].value_length   = strlen (oid2value);

    /*  variable 3 is object 1.3.6.1.4.1.6000.1.3.3 at 1.3.6.1.4.1.6000.1.3.2.2 */
    memcpy (variables[2].value, &oid3value, sizeof (oid3value));
    memcpy (variables[2].oid_value, &oid3, sizeof (oid3));
    variables[2].oid_length     = sizeof (oid3)/sizeof (unsigned long);
    variables[2].value_type     = ASN_OBJECT_ID;
    variables[2].value_length   = sizeof (oid3value)/sizeof(unsigned long);

    genericTrap  = atoi(arg[0]);
    ifIndex = atoi(arg[1]);

    if (genericTrap == SNMP_TRAP_ENTERPRISESPECIFIC)
    {
        ret = naSnmpSendNotifications(genericTrap, ESPECIFIC_NOT_DEFINED, ifIndex, 6000, 8, 3, variables);
    }
    else 
    {
        ret = naSnmpSendNotifications(genericTrap, 0, ifIndex, 6000, 8, 0, NULL);
    }   
    if (ret != NA_SUCCESS)
    {
        printf("naSnmpSendNotifications failure: errno [%d], ret[0x%X]\n", getErrno(), (unsigned int) ret);
    }
}    



/*
 * Set the versions of the SNMP protocol we will support.
 */
static void setVersions(commandType *cmd, char *arg[], int argCount)
{
    NaStatus ccode;
    WORD32 mask = 0;
    int i;
    
    for (i = 0; i < argCount; i++)
    {
        if (strcmp(arg[i], "v1") == 0)
        {
            mask |= NA_SNMP_VERSION_V1_ALLOWED;
        }
        else if (strcmp(arg[i], "v2c") == 0)
        {
            mask |= NA_SNMP_VERSION_V2c_ALLOWED;
        }
        else if (strcmp(arg[i], "v3") == 0)
        {
            mask |= NA_SNMP_VERSION_V3_ALLOWED;
        }
        else
        {
            printf ("Unrecognized parameter: '%s'\n", arg[i]);
            printf ("Usage: %s \n", cmd->usage);
            return;
        }
    }
    
    ccode = naSnmpSetAllowedVersions(mask);
    if (ccode != NA_SUCCESS)
    {
        printf ("naSnmpSetAllowedVersions() returned %s\n", getErrorString(ccode));
        printError(ccode);
    }
    
}

/*
 * List the context entries.
 */
static void listContext(commandType *cmd, char *arg[], int argCount)
{
    NaStatus ccode;
    unsigned listIndex = 0;
    NaSnmpVacmContextEntry_t context;
    
    while(1)
    {
        ccode = naSnmpGetContextEntry(listIndex, &context);
        if (ccode == NA_SUCCESS)
        {
            printf  ("   contextName: %s\n\n", getLengthString((BYTE *) context.contextName,
                                                              context.contextNameLen));
            listIndex++;
        }
        else
        if (ccode == NA_SNMP_INDEX_OUT_OF_RANGE)
        {
            if (listIndex == 0)
            {
                fputs("\nNo contexts.\n", stdout);
            }
            break;
        }
        else
        {
            printf("naSnmpGetContextEntry() returned error code %s.\n", getErrorString(ccode));
            printError(ccode);
            break;
        }
    }
}


/*
 * Delete a context entry.
 */
static void deleteContext(commandType *cmd, char *arg[], int argCount)
{
    NaSnmpVacmContextEntry_t *context;
    NaStatus ccode;
    
    context = malloc(sizeof *context);
    if (context == NULL)
    {
        fputs(ERROR_MARKER, stdout);
        fputs(": Unable to allocate memory.\n", stdout);
        goto done;
    }

    context->contextNameLen = readLengthString("contextName", (BYTE *) context->contextName,
                                                               sizeof context->contextName);
    
    ccode = naSnmpDeleteContextEntry(context);
    if (ccode == NA_SUCCESS)
    {
        fputs("deleted\n", stdout);
    }
    else
    {
        fputs(ERROR_MARKER, stdout);
        fputs("naSnmpDeleteContextEntry() returned ", stdout);
        fputs(getErrorString(ccode), stdout);
        fputs("\n", stdout);
    }
    
    free(context);
done:
    return;
}

/*
 * Create or update context entry.
 */
static void setContext(commandType *cmd, char *arg[], int argCount)
{
    NaSnmpVacmContextEntry_t *context;
    NaStatus ccode;
    
    context = malloc(sizeof *context);
    if (context == NULL)
    {
        fputs(ERROR_MARKER, stdout);
        fputs(": Unable to allocate memory.\n", stdout);
        goto done;
    }

    context->contextNameLen = readLengthString("contextName", (BYTE *) context->contextName,
                                                            sizeof context->contextNameLen);
    
    ccode = naSnmpInsertContextEntry(context);
    if (ccode == NA_SUCCESS)
    {
        fputs("succeeded\n", stdout);
    }
    else
    {
        fputs(ERROR_MARKER, stdout);
        fputs("naSnmpInsertContextEntry() returned ", stdout);
        fputs(getErrorString(ccode), stdout);
        fputs("\n", stdout);
    }
    
    free(context);
done:
    return;
}

/*
 * Print help for a particular command and all subcommands.
 *
 */
static void printHelpTree(commandType *command, int count)
{
    int i;
    
    for (i = 0; i < count; i++)
    {
        if (command[i].subcommand == NULL)
        {
            fputs(command[i].usage, stdout);
            fputs(newline, stdout);
        }
        else
        {
            printHelpTree(command[i].subcommand, command[i].subcommandCount);
        }
    }
}


/*
 * Print help for all commands and subcommands.
 */
static void printAllHelp(void)
{
    fputs("\nThe following commands are supported:\n\n", stdout);
    printHelpTree(command, asizeof(command));
    fputs("\n", stdout);
}

/*
 * Print help.
 */
static void help(commandType *cmd, char *arg[], int argCount)
{
    printAllHelp();
}




/*
 * Read a line of text from stdin.  Echo characters as they are received.  Process
 * backspaces to mean deletes.  Terminate the line on receipt of a carriage return
 * or a newline.  Convert tabs to spaces because they are annoying.
 */
int readLine(char *buffer, int maxChars)
{
    int pos = 0;
    int ch;
    
    do
    {
        ch = fgetc(stdin);
        if ((ch == EOF) || (ch == cr) || (ch == lf))
        {
            break;
        }
        if (ch == tab)
        {
            ch = space;
        }
        if (ch == backspace) 
        {
            if (pos > 0)
            {
                pos--;
                fputc(backspace, stdout);
                fputc(space, stdout);
                fputc(backspace, stdout);
            }
        }
        else
        {
            fputc(ch, stdout);
            buffer[pos++] = ch;
        }
        fflush(stdout);
    } while (pos < (maxChars - 1));
    
    fputs (newline, stdout);
    
    buffer[pos] = 0;
    
    return pos;
}

/*
 * Search a list of commands and associated subcommands.
 */
static commandType *searchList(commandType *list, int listSize, char *arg[], int *argCount)
{
    int i;
    commandType *result = NULL;
    
    for (i = 0; i < listSize; i++)
    {
        if (strcmp(list[i].name, arg[0]) == 0)
        {
            if (list[i].subcommand == NULL)
            {
                *argCount -= 1;
                result = &list[i];
            }
            else
            {
                if (*argCount > 0)
                {
                    *argCount -= 1;
                    result = searchList(list[i].subcommand, list[i].subcommandCount, &arg[1], argCount);
                }
                else
                {
                    result = NULL;
                }
            }
            break;
        }
    }
    
    return result;
}




/*
 * Prompt for user input from the console.  Determine what operation is requested
 * and do it.
 */
void cli(WORD32 notUsed)
{
    static char buffer[MAX_LINE_SIZE];
    static char *arg[MAX_PARAMS];
    int i;
    int argCount;
    commandType *cmd;
    
    NA_UNUSED_PARAM(notUsed);
    
    fputs(newline, stdout);

    while(1)
    {
        do
        {
            do
            {
                fputs(prompt, stdout);
                fflush(stdout);
            } while (readLine(buffer, sizeof buffer) == 0);
        
            arg[0] = strtok(buffer, delimiters);
        } while (arg[0] == NULL);
        
        for (i = 1; i < MAX_PARAMS; i++)
        {
            arg[i] = strtok(NULL, delimiters);
            if (arg[i] == NULL)
            {
                break;
            }
        }
        
        argCount = i;
        cmd = searchList(command, asizeof(command), arg, &argCount);
        if (cmd == NULL)
        {
            command[HELP_INDEX].fn(&command[HELP_INDEX], arg, i);
        }
        else
        {
            cmd->fn(cmd, &arg[i - argCount], argCount);
        }
    }
}

/*
 * Spawn a seperate thread which will implement the command line interface.
 */
void startCli(void)
{
    tx_thread_create(&cliThread, "cli", cli, 0, cliStack, sizeof cliStack, NAGetAppRootPriority(), NAGetAppRootPriority(), 1, TX_AUTO_START);
}
