/*
 * snmpTargetAddrEntry MIB
 * 
 * This file was generated by mib2c and is intended for use as a mib module
 * for the ucd-snmp snmpd agent. Edited by Michael Baer
 * 
 * last changed 2/2/99.
 */

#include <net-snmp/net-snmp-config.h>
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#include <stdlib.h>
#include <ctype.h>
#if HAVE_WINSOCK_H
#include <winsock.h>
#endif

#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>

#include "snmpTargetAddrEntry.h"
#include "util_funcs.h"

#define snmpTargetAddrOIDLen 11 /*This is base+column, 
                                 * i.e. everything but index */

oid             snmpTargetAddrOID[snmpTargetAddrOIDLen] =
    { 1, 3, 6, 1, 6, 3, 12, 1, 2, 1, 0 };

static unsigned long snmpTargetSpinLock = 0;
static struct targetAddrTable_struct *aAddrTable = 0;


/*
 * Utility routines 
 */
struct targetAddrTable_struct *
get_addrTable(void)
{
    return aAddrTable;
}

struct targetAddrTable_struct *
get_addrForName(char *name)
{
    struct targetAddrTable_struct *ptr;
    for (ptr = aAddrTable; ptr != NULL; ptr = ptr->next) {
        if (ptr->name && strcmp(ptr->name, name) == 0)
            return ptr;
    }
    return NULL;
}


/*
 * TargetAddrTable_create creates and returns a pointer
 * to a targetAddrTable_struct with default values set 
 */
struct targetAddrTable_struct
               *
snmpTargetAddrTable_create(void)
{
    struct targetAddrTable_struct *newEntry;

    newEntry = (struct targetAddrTable_struct *)
        malloc(sizeof(struct targetAddrTable_struct));

    if (newEntry) {
        newEntry->name = 0;

        newEntry->tDomainLen = 0;
        newEntry->tAddress = 0;

        newEntry->timeout = 1500;
        newEntry->retryCount = 3;

        newEntry->tagList = strdup("");
        newEntry->params = 0;

        newEntry->storageType = SNMP_STORAGE_NONVOLATILE;
        newEntry->rowStatus = SNMP_ROW_NONEXISTENT;
        newEntry->sess = (netsnmp_session *) NULL;
        newEntry->next = 0;
    }

    return newEntry;
}                               /* snmpTargetAddrTable_create */


/*
 * TargetAddrTable_dispose frees the space allocated to a
 * targetAddrTable_struct 
 */
void
snmpTargetAddrTable_dispose(struct targetAddrTable_struct *reaped)
{
    if (reaped->sess != NULL) {
        snmp_close(reaped->sess);
    }
    SNMP_FREE(reaped->name);
    SNMP_FREE(reaped->tAddress);
    SNMP_FREE(reaped->tagList);
    SNMP_FREE(reaped->params);

    SNMP_FREE(reaped);
}                               /* snmpTargetAddrTable_dispose  */

/*
 * snmpTargetAddrTable_addToList adds a targetAddrTable_struct 
 * to a list passed in. The list is assumed to be in a sorted order,
 * low to high and this procedure inserts a new struct in the proper 
 * location. Sorting uses OID values based on name. A new equal value 
 * overwrites a current one. 
 */
void
snmpTargetAddrTable_addToList(struct targetAddrTable_struct *newEntry,
                              struct targetAddrTable_struct **listPtr)
{
    static struct targetAddrTable_struct *curr_struct, *prev_struct;
    int             i;
    size_t          newOIDLen = 0, currOIDLen = 0;
    oid             newOID[128], currOID[128];

    /*
     * if the list is empty, add the new entry to the top 
     */
    if ((prev_struct = curr_struct = *listPtr) == 0) {
        *listPtr = newEntry;
        return;
    } else {
        /*
         * get the 'OID' value of the new entry 
         */
        newOIDLen = strlen(newEntry->name);
        for (i = 0; i < (int) newOIDLen; i++) {
            newOID[i] = newEntry->name[i];
        }

        /*
         * search through the list for an equal or greater OID value 
         */
        while (curr_struct != 0) {
            currOIDLen = strlen(curr_struct->name);
            for (i = 0; i < (int) currOIDLen; i++) {
                currOID[i] = curr_struct->name[i];
            }

            i = snmp_oid_compare(newOID, newOIDLen, currOID, currOIDLen);
            if (i == 0) {       /* Exact match, overwrite with new struct */
                newEntry->next = curr_struct->next;
                /*
                 * if curr_struct is the top of the list 
                 */
                if (*listPtr == curr_struct)
                    *listPtr = newEntry;
                else
                    prev_struct->next = newEntry;
                snmpTargetAddrTable_dispose(curr_struct);
                return;
            } else if (i < 0) { /* Found a greater OID, insert struct in front of it. */
                newEntry->next = curr_struct;
                /*
                 * if curr_struct is the top of the list 
                 */
                if (*listPtr == curr_struct)
                    *listPtr = newEntry;
                else
                    prev_struct->next = newEntry;
                return;
            }
            prev_struct = curr_struct;
            curr_struct = curr_struct->next;
        }
    }
    /*
     * if we're here, no larger OID was ever found, insert on end of list 
     */
    prev_struct->next = newEntry;
}                               /* snmpTargeAddrTable_addToList  */


void
snmpTargetAddrTable_add(struct targetAddrTable_struct *newEntry)
{
    snmpTargetAddrTable_addToList(newEntry, &aAddrTable);
}


/*
 * snmpTargetAddrTable_remFromList removes a targetAddrTable_struct 
 * from the list passed in 
 */
void
snmpTargetAddrTable_remFromList(struct targetAddrTable_struct *oldEntry,
                                struct targetAddrTable_struct **listPtr)
{
    struct targetAddrTable_struct *tptr;

    if ((tptr = *listPtr) == 0)
        return;
    else if (tptr == oldEntry) {
        *listPtr = (*listPtr)->next;
        snmpTargetAddrTable_dispose(tptr);
        return;
    } else {
        while (tptr->next != 0) {
            if (tptr->next == oldEntry) {
                tptr->next = tptr->next->next;
                snmpTargetAddrTable_dispose(oldEntry);
                return;
            }
            tptr = tptr->next;
        }
    }
}                               /* snmpTargetAddrTable_remFromList  */


/*
 * lookup OID in the link list of Addr Table Entries 
 */
struct targetAddrTable_struct *
search_snmpTargetAddrTable(oid * baseName,
                           size_t baseNameLen,
                           oid * name, size_t * length, int exact)
{
    static struct targetAddrTable_struct *temp_struct;
    int             i;
    size_t          myOIDLen = 0;
    oid             newNum[128];

    /*
     * lookup entry in addrTable linked list, Get Current MIB ID 
     */
    memcpy(newNum, baseName, baseNameLen * sizeof(oid));

    for (temp_struct = aAddrTable; temp_struct != 0;
         temp_struct = temp_struct->next) {
        for (i = 0; i < (int) strlen(temp_struct->name); i++) {
            newNum[baseNameLen + i] = temp_struct->name[i];
        }
        myOIDLen = baseNameLen + strlen(temp_struct->name);
        i = snmp_oid_compare(name, *length, newNum, myOIDLen);
        /*
         * Assumes that the linked list sorted by OID, low to high 
         */
        if ((i == 0 && exact != 0) || (i < 0 && exact == 0)) {
            if (exact == 0) {
                memcpy(name, newNum, myOIDLen * sizeof(oid));
                *length = myOIDLen;
            }
            return temp_struct;
        }
    }
    return (0);
}                               /* search_snmpTargetAddrTable  */


/*
 * snmpTargetAddr_rowStatusCheck is boolean funciton that  checks 
 * the status of a row's values in order to determine whether
 * the row should be notReady or notInService  
 */
int
snmpTargetAddr_rowStatusCheck(struct targetAddrTable_struct *entry)
{
    if ((entry->tDomainLen == 0) || (entry->tAddress == 0) ||
        (entry->params == 0))
        return 0;
    else
        return 1;
}                               /* snmtpTargetAddrTable_rowStatusCheck */


/*
 * Init routines 
 */

/*
 * this variable defines function callbacks and type return information 
 * for the snmpTargetAddrEntry mib 
 */

struct variable2 snmpTargetAddrEntry_variables[] = {
    {SNMPTARGETADDRTDOMAIN, ASN_OBJECT_ID, RWRITE,
     var_snmpTargetAddrEntry, 1, {SNMPTARGETADDRTDOMAINCOLUMN}},
    {SNMPTARGETADDRTADDRESS, ASN_OCTET_STR, RWRITE,
     var_snmpTargetAddrEntry, 1, {SNMPTARGETADDRTADDRESSCOLUMN}},
    {SNMPTARGETADDRTIMEOUT, ASN_INTEGER, RWRITE,
     var_snmpTargetAddrEntry, 1, {SNMPTARGETADDRTIMEOUTCOLUMN}},
    {SNMPTARGETADDRRETRYCOUNT, ASN_INTEGER, RWRITE,
     var_snmpTargetAddrEntry, 1, {SNMPTARGETADDRRETRYCOUNTCOLUMN}},
    {SNMPTARGETADDRTAGLIST, ASN_OCTET_STR, RWRITE,
     var_snmpTargetAddrEntry, 1, {SNMPTARGETADDRTAGLISTCOLUMN}},
    {SNMPTARGETADDRPARAMS, ASN_OCTET_STR, RWRITE,
     var_snmpTargetAddrEntry, 1, {SNMPTARGETADDRPARAMSCOLUMN}},
    {SNMPTARGETADDRSTORAGETYPE, ASN_INTEGER, RWRITE,
     var_snmpTargetAddrEntry, 1, {SNMPTARGETADDRSTORAGETYPECOLUMN}},
    {SNMPTARGETADDRROWSTATUS, ASN_INTEGER, RWRITE,
     var_snmpTargetAddrEntry, 1, {SNMPTARGETADDRROWSTATUSCOLUMN}},

};

struct variable2 snmpTargetSpinLock_var[] = {
    {SNMPTARGETSPINLOCK, ASN_INTEGER, RWRITE, var_targetSpinLock, 1, {1}}
};

static oid      snmpTargetSpinLock_oid[] = { 1, 3, 6, 1, 6, 3, 12, 1 };

/*
 * now load this mib into the agents mib table 
 */
oid             snmpTargetAddrEntry_variables_oid[] =
    { 1, 3, 6, 1, 6, 3, 12, 1, 2, 1 };


void
init_snmpTargetAddrEntry(void)
{
    aAddrTable = 0;
    DEBUGMSGTL(("snmpTargetAddrEntry", "init\n"));
    REGISTER_MIB("target/snmpTargetAddrEntry",
                 snmpTargetAddrEntry_variables, variable2,
                 snmpTargetAddrEntry_variables_oid);
    REGISTER_MIB("target/snmpTargetSpinLock", snmpTargetSpinLock_var,
                 variable2, snmpTargetSpinLock_oid);

    snmpd_register_config_handler("targetAddr",
                                  snmpd_parse_config_targetAddr, 0, NULL);

    /*
     * we need to be called back later 
     */
    snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA,
                           store_snmpTargetAddrEntry, NULL);

}                               /* init_snmpTargetAddrEntry */


int
snmpTargetAddr_addName(struct targetAddrTable_struct *entry, char *cptr)
{
    size_t          len;
    if (cptr == 0) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargetAddrEntry: no name in config string\n"));
        return (0);
    } else {
        len = strlen(cptr);
        /*
         * spec check for string 1-32 
         */
        if (len < 1 || len > 32) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "ERROR snmpTargetAddrEntry: name out of range in config string\n"));
            return (0);
        }
        entry->name = (char *) malloc(len + 1);
        strncpy(entry->name, cptr, len);
        entry->name[len] = '\0';
    }
    return (1);
}                               /* addName */


int
snmpTargetAddr_addTDomain(struct targetAddrTable_struct *entry, char *cptr)
{
    size_t          len = 128;

    if (cptr == 0) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargetAddrEntry: no tDomain in config string\n"));
        return (0);
    }

    if (!read_objid(cptr, entry->tDomain, &len)) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargetAddrEntry: tDomain unreadable in config string\n"));
        return (0);
    }

    /*
     * spec check for oid 1-128 
     */
    if (len < 1 || len > 128) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargetAddrEntry: tDomain out of range in config string\n"));
        return (0);
    }

    entry->tDomainLen = len;
    return (1);
}                               /* snmpTargetAddr_addTDomain */


int
snmpTargetAddr_addTAddress(struct targetAddrTable_struct *entry,
                           char *cptr, size_t len)
{
    if (cptr == 0) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargetAddrEntry: no tAddress in config string\n"));
        return (0);
    } else {
        /*
         * spec check for string 1-32 
         */
        /*
         * if (len < 1 || len > 32)  {
         * DEBUGMSGTL(("snmpTargetAddrEntry","ERROR snmpTargetAddrEntry: name out of range in config string\n"));
         * return(0);
         * } 
         */
        SNMP_FREE(entry->tAddress);
        entry->tAddress = (u_char *) malloc(len);
        entry->tAddressLen = len;
        memcpy(entry->tAddress, cptr, len);
    }
    return (1);
}                               /* snmpTargetAddr_addTAddress */


int
snmpTargetAddr_addTimeout(struct targetAddrTable_struct *entry, char *cptr)
{
    if (cptr == 0) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargetParamsEntry: no Timeout in config string\n"));
        return (0);
    } else if (!(isdigit(*cptr))) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargeParamsEntry: Timeout is not a digit in config string\n"));
        return (0);
    }
    /*
     * check Timeout >= 0 
     */
    else if ((entry->timeout = (int) strtol(cptr, (char **) NULL, 0)) < 0) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargeParamsEntry: Timeout out of range in config string\n"));
        return (0);
    }
    return (1);
}                               /* snmpTargetAddr_addTimeout  */


int
snmpTargetAddr_addRetryCount(struct targetAddrTable_struct *entry,
                             char *cptr)
{
    if (cptr == 0) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargetParamsEntry: no Retry Count in config string\n"));
        return (0);
    } else if (!(isdigit(*cptr))) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargeParamsEntry: Retry Count is not a digit in config string\n"));
        return (0);
    }
    /*
     * spec check 0..255 
     */
    else {
        entry->retryCount = (int) strtol(cptr, (char **) NULL, 0);
        if ((entry->retryCount < 0) || (entry->retryCount > 255)) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "ERROR snmpTargeParamsEntry: Retry Count is out of range in config string\n"));
            return (0);
        }
    }
    return (1);
}                               /* snmpTargetAddr_addRetryCount  */


int
snmpTargetAddr_addTagList(struct targetAddrTable_struct *entry, char *cptr)
{
    size_t          len;
    if (cptr == 0) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargetAddrEntry: no tag list in config string\n"));
        return (0);
    } else {
        len = strlen(cptr);
        /*
         * spec check for string 0-255 
         */
        if (len < 0 || len > 255) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "ERROR snmpTargetAddrEntry: tag list out of range in config string\n"));
            return (0);
        }
        SNMP_FREE(entry->tagList);
        entry->tagList = (char *) malloc(len + 1);
        strncpy(entry->tagList, cptr, len);
        entry->tagList[len] = '\0';
    }
    return (1);
}                               /* snmpTargetAddr_addTagList */


int
snmpTargetAddr_addParams(struct targetAddrTable_struct *entry, char *cptr)
{
    size_t          len;
    if (cptr == 0) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargetAddrEntry: no params in config string\n"));
        return (0);
    } else {
        len = strlen(cptr);
        /*
         * spec check for string 1-32 
         */
        if (len < 1 || len > 32) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "ERROR snmpTargetAddrEntry: params out of range in config string\n"));
            return (0);
        }
        entry->params = (char *) malloc(len + 1);
        strncpy(entry->params, cptr, len);
        entry->params[len] = '\0';
    }
    return (1);
}                               /* snmpTargetAddr_addParams */


int
snmpTargetAddr_addStorageType(struct targetAddrTable_struct *entry,
                              char *cptr)
{
    char            buff[1024];

    if (cptr == 0) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargetAddrEntry: no storage type in config string\n"));
        return (0);
    } else if (!(isdigit(*cptr))) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargetAddrEntry: storage type is not a digit in config string\n"));
        return (0);
    }
    /*
     * check that storage type is a possible value 
     */
    else if (((entry->storageType = (int) strtol(cptr, (char **) NULL, 0))
              != SNMP_STORAGE_OTHER) &&
             (entry->storageType != SNMP_STORAGE_VOLATILE) &&
             (entry->storageType != SNMP_STORAGE_NONVOLATILE) &&
             (entry->storageType != SNMP_STORAGE_PERMANENT) &&
             (entry->storageType != SNMP_STORAGE_READONLY)) {
        snprintf(buff, sizeof(buff),
                "ERROR snmpTargetAddrEntry: storage type not a valid value of other(%d), volatile(%d), nonvolatile(%d), permanent(%d), or readonly(%d) in config string.\n",
                SNMP_STORAGE_OTHER, SNMP_STORAGE_VOLATILE,
                SNMP_STORAGE_NONVOLATILE, SNMP_STORAGE_PERMANENT,
                SNMP_STORAGE_READONLY);
        buff[ sizeof(buff)-1 ] = 0;
        DEBUGMSGTL(("snmpTargetAddrEntry", buff));

        return (0);
    }
    return (1);
}                               /* snmpTargetAddr_addStorageType */


int
snmpTargetAddr_addRowStatus(struct targetAddrTable_struct *entry,
                            char *cptr)
{
    char            buff[1024];

    if (cptr == 0) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargetAddrEntry: no Row Status in config string\n"));
        return (0);
    } else if (!(isdigit(*cptr))) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargetAddrEntry: Row Status is not a digit in config string\n"));
        return (0);
    }
    /*
     * check that row status is a valid value 
     */
    else if (((entry->rowStatus = (int) strtol(cptr, (char **) NULL, 0))
              != SNMP_ROW_ACTIVE) &&
             (entry->rowStatus != SNMP_ROW_NOTINSERVICE) &&
             (entry->rowStatus != SNMP_ROW_NOTREADY)) {
        snprintf(buff, sizeof(buff),
                "ERROR snmpTargetAddrEntry: Row Status is not a valid value of active(%d), notinservice(%d), or notready(%d) in config string.\n",
                SNMP_ROW_ACTIVE, SNMP_ROW_NOTINSERVICE, SNMP_ROW_NOTREADY);
        buff[ sizeof(buff)-1 ] = 0;
        DEBUGMSGTL(("snmpTargetAddrEntry", buff));

        return (0);
    }
    return (1);
}                               /* snmpTargetAddr_addRowStatus  */


void
snmpd_parse_config_targetAddr(const char *token, char *char_ptr)
{
    char           *cptr = char_ptr, buff[1024];
    struct targetAddrTable_struct *newEntry;
    int             i;

    newEntry = snmpTargetAddrTable_create();

    cptr = copy_nword(cptr, buff, sizeof(buff));
    if (snmpTargetAddr_addName(newEntry, buff) == 0) {
        snmpTargetAddrTable_dispose(newEntry);
        return;
    }
    cptr = copy_nword(cptr, buff, sizeof(buff));
    if (snmpTargetAddr_addTDomain(newEntry, buff) == 0) {
        snmpTargetAddrTable_dispose(newEntry);
        return;
    }
    cptr =
        read_config_read_octet_string(cptr,
                                      (u_char **) & newEntry->tAddress,
                                      &newEntry->tAddressLen);
    if (!cptr || !(newEntry->tAddress)) {
        DEBUGMSGTL(("snmpTargetAddrEntry",
                    "ERROR snmpTargetAddrEntry: no TAddress in config string\n"));
        snmpTargetAddrTable_dispose(newEntry);
        return;
    }
    cptr = copy_nword(cptr, buff, sizeof(buff));
    if (snmpTargetAddr_addTimeout(newEntry, buff) == 0) {
        snmpTargetAddrTable_dispose(newEntry);
        return;
    }
    cptr = copy_nword(cptr, buff, sizeof(buff));
    if (snmpTargetAddr_addRetryCount(newEntry, buff) == 0) {
        snmpTargetAddrTable_dispose(newEntry);
        return;
    }
    cptr = copy_nword(cptr, buff, sizeof(buff));
    if (snmpTargetAddr_addTagList(newEntry, buff) == 0) {
        snmpTargetAddrTable_dispose(newEntry);
        return;
    }
    cptr = copy_nword(cptr, buff, sizeof(buff));
    if (snmpTargetAddr_addParams(newEntry, buff) == 0) {
        snmpTargetAddrTable_dispose(newEntry);
        return;
    }
    cptr = copy_nword(cptr, buff, sizeof(buff));
    if (snmpTargetAddr_addStorageType(newEntry, buff) == 0) {
        snmpTargetAddrTable_dispose(newEntry);
        return;
    }
    cptr = copy_nword(cptr, buff, sizeof(buff));
    if (snmpTargetAddr_addRowStatus(newEntry, buff) == 0) {
        snmpTargetAddrTable_dispose(newEntry);
        return;
    }
    snprintf(buff, sizeof(buff), "snmp_parse_config_targetAddr, read: %s\n",
            newEntry->name);
    buff[ sizeof(buff)-1 ] = 0;
    for (i = 0; i < newEntry->tDomainLen; i++) {
        snprintf(&buff[strlen(buff)], sizeof(buff)-strlen(buff)-1,
                 ".%d", (int) newEntry->tDomain[i]);
        buff[ sizeof(buff)-1 ] = 0;
    }
    snprintf(&buff[strlen(buff)], sizeof(buff)-strlen(buff)-1,
            " %s %d %d %s %s %d %d\n",
            newEntry->tAddress, newEntry->timeout, newEntry->retryCount,
            newEntry->tagList, newEntry->params, newEntry->storageType,
            newEntry->rowStatus);
    buff[ sizeof(buff)-1 ] = 0;
    DEBUGMSGTL(("snmpTargetAddrEntry", buff));

    snmpTargetAddrTable_addToList(newEntry, &aAddrTable);
}                               /* snmpd_parse_config_target */


/*
 * Shutdown routines 
 */


/*
 * store_snmpTargetAddrEntry handles the persistent storage proccess 
 * for this MIB table. It writes out all the non-volatile rows 
 * to permanent storage on a shutdown  
 */
int
store_snmpTargetAddrEntry(int majorID, int minorID, void *serverarg,
                          void *clientarg)
{
    struct targetAddrTable_struct *curr_struct;
    char            line[1024];
    int             i;

    if ((curr_struct = aAddrTable) != 0) {
        while (curr_struct != 0) {
            if ((curr_struct->storageType == SNMP_STORAGE_NONVOLATILE ||
                 curr_struct->storageType == SNMP_STORAGE_PERMANENT)
                &&
                (curr_struct->rowStatus == SNMP_ROW_ACTIVE ||
                 curr_struct->rowStatus == SNMP_ROW_NOTINSERVICE)) {
                snprintf(line, sizeof(line),
                        "targetAddr %s ", curr_struct->name);
                line[ sizeof(line)-1 ] = 0;
                for (i = 0; i < curr_struct->tDomainLen; i++) {
                    snprintf(&line[strlen(line)],
                            sizeof(line)-strlen(line)-1, ".%i",
                            (int) curr_struct->tDomain[i]);
                    line[ sizeof(line)-1 ] = 0;
                }
                if ( strlen(line)+2 < sizeof(line) ) {
                    line[ strlen(line)+1 ] = 0;
                    line[ strlen(line)   ] = ' ';
                }
                read_config_save_octet_string(&line[strlen(line)],
                                              curr_struct->tAddress,
                                              curr_struct->tAddressLen);

                snprintf(&line[strlen(line)], sizeof(line)-strlen(line)-1,
                        " %i %i \"%s\" %s %i %i",
                        curr_struct->timeout,
                        curr_struct->retryCount, curr_struct->tagList,
                        curr_struct->params, curr_struct->storageType,
                        curr_struct->rowStatus);
                line[ sizeof(line)-1 ] = 0;

                /*
                 * store to file 
                 */
                snmpd_store_config(line);
            }
            curr_struct = curr_struct->next;
        }
    }
    return SNMPERR_SUCCESS;

}                               /*  store_snmpTargetAddrEntry  */


/*
 * MIB table access routines 
 */


u_char         *
var_snmpTargetAddrEntry(struct variable * vp,
                        oid * name,
                        size_t * length,
                        int exact,
                        size_t * var_len, WriteMethod ** write_method)
{

    /*
     * variables we may use later 
     */
    static long     long_ret;
    static char     string[1500];
    static oid      objid[128];
    struct targetAddrTable_struct *temp_struct;
    int             i = 0;

    /*
     * Set up write_method first, in case we return NULL before getting to the
     * switch (vp->magic) below.  In some of these cases, we still want to call
     * the appropriate write_method, if only to have it return the appropriate
     * error.  
     */

    switch (vp->magic) {
    case SNMPTARGETADDRTDOMAIN:
        *write_method = write_snmpTargetAddrTDomain;
        break;
    case SNMPTARGETADDRTADDRESS:
        *write_method = write_snmpTargetAddrTAddress;
        break;
    case SNMPTARGETADDRRETRYCOUNT:
        *write_method = write_snmpTargetAddrRetryCount;
        break;
    case SNMPTARGETADDRTIMEOUT:
        *write_method = write_snmpTargetAddrTimeout;
        break;
    case SNMPTARGETADDRTAGLIST:
        *write_method = write_snmpTargetAddrTagList;
        break;
    case SNMPTARGETADDRPARAMS:
        *write_method = write_snmpTargetAddrParams;
        break;
    case SNMPTARGETADDRSTORAGETYPE:
        *write_method = write_snmpTargetAddrStorageType;
        break;
    case SNMPTARGETADDRROWSTATUS:
        *write_method = write_snmpTargetAddrRowStatus;
        break;
    default:
        *write_method = NULL;
    }

    *var_len = sizeof(long_ret);        /* assume an integer and change later if not */

    /*
     * look for OID in current table 
     */
    if ((temp_struct = search_snmpTargetAddrTable(vp->name, vp->namelen,
                                                  name, length,
                                                  exact)) == 0) {
        return (0);
    }

    /*
     * We found what we were looking for, either the next OID or the exact OID 
     */
    /*
     * this is where we do the value assignments for the mib results. 
     */
    switch (vp->magic) {
    case SNMPTARGETADDRTDOMAIN:
        if (temp_struct->tDomainLen <= 0) {
            return (0);
        } else {
            for (i = 0; i < temp_struct->tDomainLen; i++) {
                objid[i] = temp_struct->tDomain[i];
            }
            *var_len = temp_struct->tDomainLen * sizeof(oid);
        }
        return (unsigned char *) objid;

    case SNMPTARGETADDRTADDRESS:
        if (temp_struct->tAddress == 0)
            return (0);
        *var_len = temp_struct->tAddressLen;
        return (unsigned char *) temp_struct->tAddress;

    case SNMPTARGETADDRTIMEOUT:
        long_ret = temp_struct->timeout;
        return (unsigned char *) &long_ret;

    case SNMPTARGETADDRRETRYCOUNT:
        long_ret = temp_struct->retryCount;
        return (unsigned char *) &long_ret;

    case SNMPTARGETADDRTAGLIST:
        if (temp_struct->tagList != NULL) {
            strcpy(string, temp_struct->tagList);
            *var_len = strlen(string);
            return (unsigned char *) string;
        } else {
            return NULL;
        }

    case SNMPTARGETADDRPARAMS:
        if (temp_struct->params == 0)
            return (0);
        strcpy(string, temp_struct->params);
        *var_len = strlen(string);
        return (unsigned char *) string;

    case SNMPTARGETADDRSTORAGETYPE:
        long_ret = temp_struct->storageType;
        return (unsigned char *) &long_ret;

    case SNMPTARGETADDRROWSTATUS:
        long_ret = temp_struct->rowStatus;
        return (unsigned char *) &long_ret;

    default:
        DEBUGMSGTL(("snmpd",
                    "unknown sub-id %d in var_snmpTargetAddrEntry\n",
                    vp->magic));
    }
    return 0;
}                               /* var_snmpTargetAddrEntry */


int
write_snmpTargetAddrTDomain(int action,
                            u_char * var_val,
                            u_char var_val_type,
                            size_t var_val_len,
                            u_char * statP, oid * name, size_t name_len)
{
    struct targetAddrTable_struct *target = NULL;
    static oid      old_oid[MAX_OID_LEN];
    static size_t   old_oid_len;

    if (action == RESERVE1) {
        if (var_val_type != ASN_OBJECT_ID) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrTDomain not ASN_OBJECT_ID\n"));
            return SNMP_ERR_WRONGTYPE;
        }
        if ((var_val_len > (MAX_OID_LEN * sizeof(oid))) ||
            (var_val_len % sizeof(oid) != 0)) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrTDomain: bad length\n"));
            return SNMP_ERR_WRONGLENGTH;
        }
    } else if (action == RESERVE2) {
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRTDOMAINCOLUMN;
        if ((target =
             search_snmpTargetAddrTable(snmpTargetAddrOID,
                                        snmpTargetAddrOIDLen, name,
                                        &name_len, 1)) == 0) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrTDomain: BAD OID!\n"));
            return SNMP_ERR_INCONSISTENTNAME;
        } else {
            if (target->storageType == SNMP_STORAGE_READONLY) {
                DEBUGMSGTL(("snmpTargetAddrEntry",
                            "write to snmpTargetAddrTDomain: row is read only\n"));
                return SNMP_ERR_NOTWRITABLE;
            }
            if (target->rowStatus == SNMP_ROW_ACTIVE) {
                DEBUGMSGTL(("snmpTargetAddrEntry",
                            "write to snmpTargetAddrTDomain: not allowed in active row.\n"));
                return SNMP_ERR_INCONSISTENTVALUE;
            }

            /*
             * Finally, we're golden, save current value.  
             */

            memcpy(old_oid, target->tDomain,
                   target->tDomainLen * sizeof(oid));
            old_oid_len = target->tDomainLen;

            memcpy((u_char *) target->tDomain, var_val, var_val_len);
            target->tDomainLen = var_val_len / sizeof(oid);

            /*
             * If row is new, check if its status can be updated.  
             */
            if ((target->rowStatus == SNMP_ROW_NOTREADY) &&
                (snmpTargetAddr_rowStatusCheck(target) != 0)) {
                target->rowStatus = SNMP_ROW_NOTINSERVICE;
            }
        }
    } else if (action == FREE || action == UNDO) {
        /*
         * Try to undo the SET here (abnormal usage of FREE clause)  
         */
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRTDOMAINCOLUMN;
        if ((target =
             search_snmpTargetAddrTable(snmpTargetAddrOID,
                                        snmpTargetAddrOIDLen, name,
                                        &name_len, 1)) != NULL) {
            if (target->storageType != SNMP_STORAGE_READONLY
                && target->rowStatus != SNMP_ROW_ACTIVE) {
                memcpy((u_char *) target->tDomain, (u_char *) old_oid,
                       (old_oid_len * sizeof(oid)));
                target->tDomainLen = old_oid_len;
                if (target->rowStatus == SNMP_ROW_NOTINSERVICE &&
                    snmpTargetAddr_rowStatusCheck(target) == 0) {
                    target->rowStatus = SNMP_ROW_NOTREADY;
                }
            }
        }
    }
    return SNMP_ERR_NOERROR;
}                               /* write_snmpTargetAddrTDomain */


int
write_snmpTargetAddrTAddress(int action,
                             u_char * var_val,
                             u_char var_val_type,
                             size_t var_val_len,
                             u_char * statP, oid * name, size_t name_len)
{
    struct targetAddrTable_struct *target = NULL;
    static char    *old_addr = NULL;
    static size_t   old_len = 0;

    if (action == RESERVE1) {
        if (var_val_type != ASN_OCTET_STR) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrTAddress not ASN_OCTET_STR\n"));
            return SNMP_ERR_WRONGTYPE;
        } else if (var_val_len < 1 || var_val_len > 255) {
            return SNMP_ERR_WRONGLENGTH;
        }
    } else if (action == RESERVE2) {
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRTADDRESSCOLUMN;
        if ((target =
             search_snmpTargetAddrTable(snmpTargetAddrOID,
                                        snmpTargetAddrOIDLen, name,
                                        &name_len, 1)) == 0) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrTAddress: BAD OID!\n"));
            return SNMP_ERR_INCONSISTENTNAME;
        } else {
            if (target->storageType == SNMP_STORAGE_READONLY) {
                DEBUGMSGTL(("snmpTargetAddrEntry",
                            "write to snmpTargetAddrTAddress: row is read only\n"));
                return SNMP_ERR_NOTWRITABLE;
            }
            if (target->rowStatus == SNMP_ROW_ACTIVE) {
                DEBUGMSGTL(("snmpTargetAddrEntry",
                            "write to snmpTargetAddrTAddress: not allowed in active row.\n"));
                return SNMP_ERR_INCONSISTENTVALUE;
            }

            old_addr = target->tAddress;
            old_len = target->tAddressLen;
            target->tAddress = (u_char *) malloc(var_val_len);
            if (target->tAddress == NULL) {
                return SNMP_ERR_RESOURCEUNAVAILABLE;
            }
            memcpy(target->tAddress, var_val, var_val_len);
            target->tAddressLen = var_val_len;

            /*
             * If row is new, check if its status can be updated.  
             */
            if ((target->rowStatus == SNMP_ROW_NOTREADY) &&
                (snmpTargetAddr_rowStatusCheck(target) != 0)) {
                target->rowStatus = SNMP_ROW_NOTINSERVICE;
            }
        }
    } else if (action == COMMIT) {
        SNMP_FREE(old_addr);
        old_addr = NULL;
    } else if (action == FREE || action == UNDO) {
        /*
         * Try to undo the SET here (abnormal usage of FREE clause)  
         */
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRTADDRESSCOLUMN;
        if ((target =
             search_snmpTargetAddrTable(snmpTargetAddrOID,
                                        snmpTargetAddrOIDLen, name,
                                        &name_len, 1)) != NULL) {
            if (target->storageType != SNMP_STORAGE_READONLY
                && target->rowStatus != SNMP_ROW_ACTIVE) {
                SNMP_FREE(target->tAddress);
                target->tAddress = old_addr;
                target->tAddressLen = old_len;
                if (target->rowStatus == SNMP_ROW_NOTINSERVICE &&
                    snmpTargetAddr_rowStatusCheck(target) == 0) {
                    target->rowStatus = SNMP_ROW_NOTREADY;
                }
            }
        }
    }

    return SNMP_ERR_NOERROR;
}                               /* write_snmpTargetAddrTAddress */


int
write_snmpTargetAddrTimeout(int action,
                            u_char * var_val,
                            u_char var_val_type,
                            size_t var_val_len,
                            u_char * statP, oid * name, size_t name_len)
{
    /*
     * variables we may use later 
     */
    static long     long_ret;
    size_t          size;
    struct targetAddrTable_struct *temp_struct;

    if (action == RESERVE1) {
        if (var_val_type != ASN_INTEGER) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrTimeout not ASN_INTEGER\n"));
            return SNMP_ERR_WRONGTYPE;
        }
        if (var_val_len > (size = sizeof(long_ret))) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrTimeout: bad length\n"));
            return SNMP_ERR_WRONGLENGTH;
        }
        long_ret = *((long *) var_val);
    } else if (action == RESERVE2) {

        /*
         * spec check range, no spec check 
         */

        /*
         * Find row in linked list and check pertinent status... 
         */
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRTIMEOUTCOLUMN;
        if ((temp_struct =
             search_snmpTargetAddrTable(snmpTargetAddrOID,
                                        snmpTargetAddrOIDLen, name, &name_len,
                                        1)) == 0) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrTimeout : BAD OID\n"));
            return SNMP_ERR_NOSUCHNAME;
        }
        /*
         * row exists, check if it is changeable 
         */
        if (temp_struct->storageType == SNMP_STORAGE_READONLY) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrTimeout : row is read only\n"));
            return SNMP_ERR_NOTWRITABLE;
        }
    } else if  (action == COMMIT) {
        /*
         * Finally, we're golden, should we save value? 
         */
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRTIMEOUTCOLUMN;
        if ((temp_struct =
             search_snmpTargetAddrTable(snmpTargetAddrOID,
                                        snmpTargetAddrOIDLen, name, &name_len,
                                        1)) != 0) {
            temp_struct->timeout = long_ret;
        }
    }

    return SNMP_ERR_NOERROR;
}                               /* write_snmpTargetAddrTimeout */


int
write_snmpTargetAddrRetryCount(int action,
                               u_char * var_val,
                               u_char var_val_type,
                               size_t var_val_len,
                               u_char * statP, oid * name, size_t name_len)
{
    static long     long_ret;
    struct targetAddrTable_struct *target;

    if (action == RESERVE1) {
        if (var_val_type != ASN_INTEGER) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrRetryCount not ASN_INTEGER\n"));
            return SNMP_ERR_WRONGTYPE;
        }
        if (var_val_len != sizeof(long)) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrRetryCount: bad length\n"));
            return SNMP_ERR_WRONGLENGTH;
        }
        long_ret = *((long *) var_val);
        if (long_ret < 0 || long_ret > 255) {
            return SNMP_ERR_WRONGVALUE;
        }
    } else if (action == RESERVE2) {
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRRETRYCOUNTCOLUMN;
        if ((target = search_snmpTargetAddrTable(snmpTargetAddrOID,
                                                 snmpTargetAddrOIDLen,
                                                 name, &name_len,
                                                 1)) == NULL) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrTimeout: BAD OID\n"));
            return SNMP_ERR_INCONSISTENTNAME;
        } else {
            if (target->storageType == SNMP_STORAGE_READONLY) {
                DEBUGMSGTL(("snmpTargetAddrEntry",
                            "write to snmpTargetAddrRetryCount: row is read only\n"));
                return SNMP_ERR_NOTWRITABLE;
            }
        }
    } else if (action == COMMIT) {
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRRETRYCOUNTCOLUMN;
        if ((target = search_snmpTargetAddrTable(snmpTargetAddrOID,
                                                 snmpTargetAddrOIDLen,
                                                 name, &name_len,
                                                 1)) != NULL) {
            target->retryCount = long_ret;
        }
    }

    return SNMP_ERR_NOERROR;
}                               /* write_snmpTargetAddrRetryCount */

static int
is_delim(const char c)
{
    return (c == 0x020 || c == 0x09 || c == 0x0d || c == 0x0b);
}

int
snmpTagListValid(const char *tagList, const size_t tagListLen)
{
    size_t          i = 0;
    int             inTag = 0;


    for (i = 0; i < tagListLen; i++) {
        if (is_delim(tagList[i]) && !inTag) {
            /*
             * Either a leading delimiter or two consecutive delimiters.  
             */
            return 0;
        } else if (is_delim(tagList[i]) && inTag) {
            inTag = 0;
        } else if (!is_delim(tagList[i]) && !inTag) {
            inTag = 1;
        }
    }
    if (!inTag) {
        /*
         * Trailing delimiter.  
         */
        return 0;
    }
    return 1;
}

int
write_snmpTargetAddrTagList(int action,
                            u_char * var_val,
                            u_char var_val_type,
                            size_t var_val_len,
                            u_char * statP, oid * name, size_t name_len)
{
    struct targetAddrTable_struct *target = NULL;
    static char    *old_tlist;

    if (action == RESERVE1) {
        if (var_val_type != ASN_OCTET_STR) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrTagList not ASN_OCTET_STR\n"));
            return SNMP_ERR_WRONGTYPE;
        }
        if (var_val_len > 255) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrTagList: bad length\n"));
            return SNMP_ERR_WRONGLENGTH;
        }
        if (!snmpTagListValid(var_val, var_val_len)) {
            return SNMP_ERR_WRONGVALUE;
        }
    } else if (action == RESERVE2) {
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRTAGLISTCOLUMN;
        if ((target =
             search_snmpTargetAddrTable(snmpTargetAddrOID,
                                        snmpTargetAddrOIDLen, name,
                                        &name_len, 1)) == NULL) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrTagList: BAD OID!\n"));
            return SNMP_ERR_INCONSISTENTNAME;
        } else {
            if (target->storageType == SNMP_STORAGE_READONLY) {
                DEBUGMSGTL(("snmpTargetAddrEntry",
                            "write to snmpTargetAddrTagList: row is read only\n"));
                return SNMP_ERR_NOTWRITABLE;
            }
            old_tlist = target->tagList;
            target->tagList = (char *) malloc(var_val_len + 1);
            if (target->tagList == NULL) {
                return SNMP_ERR_RESOURCEUNAVAILABLE;
            }
            memcpy(target->tagList, var_val, var_val_len);
            target->tagList[var_val_len] = '\0';
        }
    } else if (action == COMMIT) {
        SNMP_FREE(old_tlist);
        old_tlist = NULL;
    } else if (action == FREE || action == UNDO) {
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRTAGLISTCOLUMN;
        if ((target =
             search_snmpTargetAddrTable(snmpTargetAddrOID,
                                        snmpTargetAddrOIDLen, name,
                                        &name_len, 1)) != NULL) {
            if (target->storageType != SNMP_STORAGE_READONLY) {
                SNMP_FREE(target->tagList);
                target->tagList = old_tlist;
            }
        }
    }

    return SNMP_ERR_NOERROR;
}                               /* write_snmpTargetAddrTagList */


int
write_snmpTargetAddrParams(int action,
                           u_char * var_val,
                           u_char var_val_type,
                           size_t var_val_len,
                           u_char * statP, oid * name, size_t name_len)
{
    struct targetAddrTable_struct *target = NULL;
    static char    *old_params = NULL;

    if (action == RESERVE1) {
        if (var_val_type != ASN_OCTET_STR) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrParams not ASN_OCTET_STR\n"));
            return SNMP_ERR_WRONGTYPE;
        } else if (var_val_len < 1 || var_val_len > 32) {
            return SNMP_ERR_WRONGLENGTH;
        }
    } else if (action == RESERVE2) {
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRPARAMSCOLUMN;
        if ((target =
             search_snmpTargetAddrTable(snmpTargetAddrOID,
                                        snmpTargetAddrOIDLen, name,
                                        &name_len, 1)) == 0) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrParams: BAD OID!\n"));
            return SNMP_ERR_INCONSISTENTNAME;
        } else {
            if (target->storageType == SNMP_STORAGE_READONLY) {
                DEBUGMSGTL(("snmpTargetAddrEntry",
                            "write to snmpTargetAddrParams: row is read only\n"));
                return SNMP_ERR_NOTWRITABLE;
            }
            if (target->rowStatus == SNMP_ROW_ACTIVE) {
                DEBUGMSGTL(("snmpTargetAddrEntry",
                            "write to snmpTargetAddrParams: not allowed in active row.\n"));
                return SNMP_ERR_INCONSISTENTVALUE;
            }

            old_params = target->params;
            target->params = (u_char *) malloc(var_val_len + 1);
            if (target->params == NULL) {
                return SNMP_ERR_RESOURCEUNAVAILABLE;
            }
            memcpy(target->params, var_val, var_val_len);
            target->params[var_val_len] = '\0';

            /*
             * If row is new, check if its status can be updated.  
             */
            if ((target->rowStatus == SNMP_ROW_NOTREADY) &&
                (snmpTargetAddr_rowStatusCheck(target) != 0)) {
                target->rowStatus = SNMP_ROW_NOTINSERVICE;
            }
        }
    } else if (action == COMMIT) {
        SNMP_FREE(old_params);
        old_params = NULL;
    } else if (action == FREE || action == UNDO) {
        /*
         * Try to undo the SET here (abnormal usage of FREE clause)  
         */
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRPARAMSCOLUMN;
        if ((target =
             search_snmpTargetAddrTable(snmpTargetAddrOID,
                                        snmpTargetAddrOIDLen, name,
                                        &name_len, 1)) != NULL) {
            if (target->storageType != SNMP_STORAGE_READONLY
                && target->rowStatus != SNMP_ROW_ACTIVE) {
                SNMP_FREE(target->params);
                target->params = old_params;
                if (target->rowStatus == SNMP_ROW_NOTINSERVICE &&
                    snmpTargetAddr_rowStatusCheck(target) == 0) {
                    target->rowStatus = SNMP_ROW_NOTREADY;
                }
            }
        }
    }

    return SNMP_ERR_NOERROR;
}                               /* write_snmpTargetAddrParams */

int
write_snmpTargetAddrStorageType(int action,
                                u_char * var_val,
                                u_char var_val_type,
                                size_t var_val_len,
                                u_char * statP,
                                oid * name, size_t name_len)
{
    long            long_ret = *((long *) var_val);
    struct targetAddrTable_struct *target;

    if (action == RESERVE1) {
        if (var_val_type != ASN_INTEGER) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrStorageType not ASN_INTEGER\n"));
            return SNMP_ERR_WRONGTYPE;
        }
        if (var_val_len != sizeof(long)) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrStorageType: bad length\n"));
            return SNMP_ERR_WRONGLENGTH;
        }
        if (long_ret != SNMP_STORAGE_OTHER &&
            long_ret != SNMP_STORAGE_VOLATILE &&
            long_ret != SNMP_STORAGE_NONVOLATILE) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrStorageType: attempted storage type not a valid"));
            DEBUGMSG(("snmpTargetAddrEntry",
                      " value of other(%d), volatile(%d), or nonvolatile(%d)\n",
                      SNMP_STORAGE_OTHER, SNMP_STORAGE_VOLATILE,
                      SNMP_STORAGE_NONVOLATILE));
            return SNMP_ERR_WRONGVALUE;
        }
    } else if (action == RESERVE2) {
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRSTORAGETYPECOLUMN;
        if ((target = search_snmpTargetAddrTable(snmpTargetAddrOID,
                                                 snmpTargetAddrOIDLen,
                                                 name, &name_len,
                                                 1)) == NULL) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrStorageType: BAD OID\n"));
            return SNMP_ERR_INCONSISTENTNAME;
        } else {
            if (target->storageType == SNMP_STORAGE_PERMANENT ||
                target->storageType == SNMP_STORAGE_READONLY) {
                DEBUGMSGTL(("snmpTargetAddrEntry",
                            "write to snmpTargetAddrStorageType: row has unchangeable storage status: %d\n",
                            target->storageType));
                return SNMP_ERR_WRONGVALUE;
            }
        }
    } else if (action == COMMIT) {
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRSTORAGETYPECOLUMN;
        if ((target = search_snmpTargetAddrTable(snmpTargetAddrOID,
                                                 snmpTargetAddrOIDLen,
                                                 name, &name_len,
                                                 1)) != NULL) {
            target->storageType = long_ret;
        }
    }

    return SNMP_ERR_NOERROR;
}                               /* write_snmpTargetAddrStorageType */


/*
 * snmpTargeAddr_createNewRow is called from write_snmpTargetAddrRowStatus
 * when a new row is required. It creates a new row with 
 * the index of the passed in 'name' (i.e. full index OID) and
 * adds it to the linked list. 'name' should be the full OID of the new index. 
 * It passes back 0 if unsuccessfull.
 */
int
snmpTargetAddr_createNewRow(oid * name, size_t name_len)
{
    size_t          newNameLen;
    int             i;
    struct targetAddrTable_struct *temp_struct;

    /*
     * setup a new snmpTargetAddrTable structure and add it to the list 
     */
    newNameLen = name_len - snmpTargetAddrOIDLen;
    if (newNameLen > 0) {
        temp_struct = snmpTargetAddrTable_create();
        if (!temp_struct)
            return SNMP_ERR_GENERR;
        temp_struct->name = (char *) malloc(newNameLen + 1);
        if (temp_struct->name == NULL) {
            SNMP_FREE(temp_struct->tagList);
            SNMP_FREE(temp_struct);
            return 0;
        }

        for (i = 0; i < (int) newNameLen; i++) {
            temp_struct->name[i] = (char) name[i + snmpTargetAddrOIDLen];
        }

        temp_struct->name[newNameLen] = '\0';
        temp_struct->rowStatus = SNMP_ROW_NOTREADY;

        snmpTargetAddrTable_addToList(temp_struct, &aAddrTable);

        return 1;
    }

    return 0;
}                               /* snmpTargetAddr_createNewRow */


/*
 * Assign a value to the Row Status variable 
 */
int
write_snmpTargetAddrRowStatus(int action,
                              u_char * var_val,
                              u_char var_val_type,
                              size_t var_val_len,
                              u_char * statP, oid * name, size_t name_len)
{
    static long     value;
    struct targetAddrTable_struct *target = NULL;

    if (action == RESERVE1) {
        value = *((long *) var_val);
        if (var_val_type != ASN_INTEGER) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrRowStatus not ASN_INTEGER\n"));
            return SNMP_ERR_WRONGTYPE;
        }
        if (var_val_len != sizeof(long)) {
            DEBUGMSGTL(("snmpTargetAddrEntry",
                        "write to snmpTargetAddrRowStatus: bad length\n"));
            return SNMP_ERR_WRONGLENGTH;
        }
        if (value == RS_NOTREADY || value < 1 || value > 6) {
            return SNMP_ERR_WRONGVALUE;
        }

        /*
         * Check index value is reasonable.  
         */

        if (name_len < snmpTargetAddrOIDLen + 1 ||
            name_len > snmpTargetAddrOIDLen + 32) {
            DEBUGMSGTL(("snmpTargetAddrEntry", "bad index length %d\n",
                        name_len - snmpTargetAddrOIDLen));
            return SNMP_ERR_NOCREATION;
        }

        /*
         * Search for struct in linked list.  
         */

        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRROWSTATUSCOLUMN;
        target =
            search_snmpTargetAddrTable(snmpTargetAddrOID,
                                       snmpTargetAddrOIDLen, name,
                                       &name_len, 1);

        if (target != NULL) {
            if (value == RS_CREATEANDGO || value == RS_CREATEANDWAIT) {
                value = RS_NOTREADY;
                return SNMP_ERR_INCONSISTENTVALUE;
            }
            if (target->storageType == SNMP_STORAGE_READONLY) {
                DEBUGMSGTL(("snmpTargetAddrEntry", "row is read only\n"));
                return SNMP_ERR_NOTWRITABLE;
            }
            if (target->storageType == SNMP_STORAGE_PERMANENT) {
                if (value == RS_DESTROY) {
                    DEBUGMSGTL(("snmpTargetAddrEntry",
                                "unable to destroy permanent row\n"));
                    return SNMP_ERR_INCONSISTENTVALUE;
                }
            }
        } else {
            if (value == RS_ACTIVE || value == RS_NOTINSERVICE) {
                return SNMP_ERR_INCONSISTENTVALUE;
            }
            if (value == RS_CREATEANDGO || value == RS_CREATEANDWAIT) {
                if (snmpTargetAddr_createNewRow(name, name_len) == 0) {
                    DEBUGMSGTL(("snmpTargetAddrEntry",
                                "couldn't malloc() new row\n"));
                    return SNMP_ERR_RESOURCEUNAVAILABLE;
                }
            }
        }
    } else if (action == ACTION) {
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRROWSTATUSCOLUMN;
        target =
            search_snmpTargetAddrTable(snmpTargetAddrOID,
                                       snmpTargetAddrOIDLen, name,
                                       &name_len, 1);

        if (target != NULL) {
            if (value == RS_CREATEANDGO) {
                /*
                 * Check whether all the required objects have been set.  
                 */
                if (snmpTargetAddr_rowStatusCheck(target)) {
                    target->rowStatus = RS_ACTIVE;
                } else {
                    target->rowStatus = RS_NOTREADY;
                }
            } else if (value == RS_CREATEANDWAIT) {
                /*
                 * Check whether all the required objects have been set.  
                 */
                if (snmpTargetAddr_rowStatusCheck(target)) {
                    target->rowStatus = RS_NOTINSERVICE;
                } else {
                    target->rowStatus = RS_NOTREADY;
                }
            } else if (value == RS_ACTIVE) {
                if (target->rowStatus == RS_NOTINSERVICE) {
                    target->rowStatus = RS_ACTIVE;
                } else if (target->rowStatus == RS_NOTREADY) {
                    return SNMP_ERR_INCONSISTENTVALUE;
                }
            } else if (value == RS_NOTINSERVICE) {
                if (target->rowStatus == RS_ACTIVE) {
                    target->rowStatus = RS_NOTINSERVICE;
                } else if (target->rowStatus == RS_NOTREADY) {
                    return SNMP_ERR_INCONSISTENTVALUE;
                }
            }
        }
    } else if (action == COMMIT) {
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRROWSTATUSCOLUMN;
        target =
            search_snmpTargetAddrTable(snmpTargetAddrOID,
                                       snmpTargetAddrOIDLen, name,
                                       &name_len, 1);
        if (target != NULL) {
            if (value == RS_DESTROY) {
                snmpTargetAddrTable_remFromList(target, &aAddrTable);
            }
            if (value == RS_NOTINSERVICE) {
                if (target->sess != NULL) {
                    snmp_close(target->sess);
                    target->sess = NULL;
                }
            }
        }
    } else if (action == UNDO || action == FREE) {
        snmpTargetAddrOID[snmpTargetAddrOIDLen - 1] =
            SNMPTARGETADDRROWSTATUSCOLUMN;
        target =
            search_snmpTargetAddrTable(snmpTargetAddrOID,
                                       snmpTargetAddrOIDLen, name,
                                       &name_len, 1);
        if (value == RS_CREATEANDGO || value == RS_CREATEANDWAIT) {
            if (target != NULL) {
                snmpTargetAddrTable_remFromList(target, &aAddrTable);
            }
        }
    }
    return SNMP_ERR_NOERROR;
}                               /* write_snmpTargetAddrRowStatus */



int
write_targetSpinLock(int action,
                     u_char * var_val,
                     u_char var_val_type,
                     size_t var_val_len,
                     u_char * statP, oid * name, size_t name_len)
{
    if (action == RESERVE1) {
        if (var_val_type != ASN_INTEGER) {
            return SNMP_ERR_WRONGTYPE;
        }
        if (var_val_len != sizeof(unsigned long)) {
            return SNMP_ERR_WRONGLENGTH;
        }
        if (*((unsigned long *) var_val) != snmpTargetSpinLock) {
            return SNMP_ERR_INCONSISTENTVALUE;
        }
    } else if (action == COMMIT) {
        if (snmpTargetSpinLock == 2147483647) {
            snmpTargetSpinLock = 0;
        } else {
            snmpTargetSpinLock++;
        }
    }
    return SNMP_ERR_NOERROR;
}



u_char         *
var_targetSpinLock(struct variable * vp,
                   oid * name,
                   size_t * length,
                   int exact,
                   size_t * var_len, WriteMethod ** write_method)
{
    if (header_generic(vp, name, length, exact, var_len, write_method) ==
        MATCH_FAILED) {
        *write_method = write_targetSpinLock;
        return NULL;
    }
    if (vp->magic == SNMPTARGETSPINLOCK) {
        *write_method = write_targetSpinLock;
        *var_len = sizeof(unsigned long);
        return (u_char *) & (snmpTargetSpinLock);
    }
    return NULL;
}
