/**
 * add new entry
 */
static void
_add_new_entry(netsnmp_defaultrouter_entry *defaultrouter_entry,
               netsnmp_container *container)
{
    ipDefaultRouterTable_rowreq_ctx *rowreq_ctx;

    DEBUGMSGTL(("ipDefaultRouterTable:access", "creating new entry\n"));

    netsnmp_assert(NULL != defaultrouter_entry);
    netsnmp_assert(NULL != container);

    /*
     * allocate an row context and set the index(es)
     */
    rowreq_ctx = ipDefaultRouterTable_allocate_rowreq_ctx(defaultrouter_entry,
                                                          NULL);
    if ((NULL != rowreq_ctx) &&
            (MFD_SUCCESS ==
             ipDefaultRouterTable_indexes_set(rowreq_ctx,
                 defaultrouter_entry->dr_addresstype,
                 defaultrouter_entry->dr_address,
                 defaultrouter_entry->dr_address_len,
                 defaultrouter_entry->dr_if_index))) {
        if (CONTAINER_INSERT(container, rowreq_ctx) < 0) {
            DEBUGMSGTL(("ipAddressTable:access",
                         "container insert failed for new entry\n"));
            ipDefaultRouterTable_release_rowreq_ctx(rowreq_ctx);
            return;
        }
    } else {
        if (NULL != rowreq_ctx) {
            snmp_log(LOG_ERR, "error setting index while loading "
                    "ipDefaultRouterTable cache.\n");
            ipDefaultRouterTable_release_rowreq_ctx(rowreq_ctx);
        } else {
            snmp_log(LOG_ERR, "memory allocation failed while loading "
                     "ipDefaultRouterTable cache.\n");
            netsnmp_access_defaultrouter_entry_free(defaultrouter_entry);
        }

        return;
    }
}
/**
 * @internal
 */
static void
_container_item_free(ipDefaultRouterTable_rowreq_ctx * rowreq_ctx,
                     void *context)
{
    DEBUGMSGTL(("internal:ipDefaultRouterTable:_container_item_free",
                "called\n"));

    if (NULL == rowreq_ctx)
        return;

    ipDefaultRouterTable_release_rowreq_ctx(rowreq_ctx);
}                               /* _container_item_free */
/*
 *********************************************************************
 * @internal
 * allocate resources for a ipDefaultRouterTable_rowreq_ctx
 */
ipDefaultRouterTable_rowreq_ctx *
ipDefaultRouterTable_allocate_rowreq_ctx(ipDefaultRouterTable_data * data,
                                         void *user_init_ctx)
{
    ipDefaultRouterTable_rowreq_ctx *rowreq_ctx =
        SNMP_MALLOC_TYPEDEF(ipDefaultRouterTable_rowreq_ctx);

    DEBUGMSGTL(("internal:ipDefaultRouterTable:ipDefaultRouterTable_allocate_rowreq_ctx", "called\n"));

    if (NULL == rowreq_ctx) {
        snmp_log(LOG_ERR, "Couldn't allocate memory for a "
                 "ipDefaultRouterTable_rowreq_ctx.\n");
        return NULL;
    } else {
        if (NULL != data) {
            /*
             * track if we got data from user
             */
            rowreq_ctx->rowreq_flags |= MFD_ROW_DATA_FROM_USER;
            rowreq_ctx->data = data;
        } else if (NULL ==
                   (rowreq_ctx->data =
                    ipDefaultRouterTable_allocate_data())) {
            SNMP_FREE(rowreq_ctx);
            return NULL;
        }
    }

    /*
     * undo context will be allocated when needed (in *_undo_setup)
     */

    rowreq_ctx->oid_idx.oids = rowreq_ctx->oid_tmp;

    rowreq_ctx->ipDefaultRouterTable_data_list = NULL;

    /*
     * if we allocated data, call init routine
     */
    if (!(rowreq_ctx->rowreq_flags & MFD_ROW_DATA_FROM_USER)) {
        if (SNMPERR_SUCCESS !=
            ipDefaultRouterTable_rowreq_ctx_init(rowreq_ctx,
                                                 user_init_ctx)) {
            ipDefaultRouterTable_release_rowreq_ctx(rowreq_ctx);
            rowreq_ctx = NULL;
        }
    }

    return rowreq_ctx;
}                               /* ipDefaultRouterTable_allocate_rowreq_ctx */
/**
 * @internal
 * wrapper
 */
static int
_mfd_ipDefaultRouterTable_post_request(netsnmp_mib_handler *handler,
                                       netsnmp_handler_registration
                                       *reginfo,
                                       netsnmp_agent_request_info
                                       *agtreq_info,
                                       netsnmp_request_info *requests)
{
    ipDefaultRouterTable_rowreq_ctx *rowreq_ctx =
        netsnmp_container_table_row_extract(requests);
    int             rc, packet_rc;

    DEBUGMSGTL(("internal:ipDefaultRouterTable:_mfd_ipDefaultRouterTable_post_request", "called\n"));

    /*
     * release row context, if deleted
     */
    if (rowreq_ctx && (rowreq_ctx->rowreq_flags & MFD_ROW_DELETED))
        ipDefaultRouterTable_release_rowreq_ctx(rowreq_ctx);

    /*
     * wait for last call before calling user
     */
    if (1 != netsnmp_row_merge_status_last(reginfo, agtreq_info)) {
        DEBUGMSGTL(("internal:ipDefaultRouterTable",
                    "waiting for last post_request\n"));
        return SNMP_ERR_NOERROR;
    }

    packet_rc = netsnmp_check_all_requests_error(agtreq_info->asp, 0);
    rc = ipDefaultRouterTable_post_request(ipDefaultRouterTable_if_ctx.
                                           user_ctx, packet_rc);
    if (MFD_SUCCESS != rc) {
        /*
         * nothing we can do about it but log it
         */
        DEBUGMSGTL(("ipDefaultRouterTable", "error %d from "
                    "ipDefaultRouterTable_post_request\n", rc));
    }

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

    DEBUGMSGTL(("verbose:ipDefaultRouterTable:ipDefaultRouterTable_container_load", "called\n"));

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

    if (NULL == defaultrouter_container)
        return MFD_RESOURCE_UNAVAILABLE;        /* msg already logged */

    /*
     * we just got a fresh copy of interface data. compare it to
     * what we've already got, and make any adjustments, saving
     * missing addresses to be deleted.
     */
    tmp_ptr[0] = defaultrouter_container->next;
    tmp_ptr[1] = NULL;
    CONTAINER_FOR_EACH(container,
                       (netsnmp_container_obj_func *) _check_entry_for_updates,
                       tmp_ptr);

    /*
     * now add any new interfaces
     */
    CONTAINER_FOR_EACH(defaultrouter_container,
                       (netsnmp_container_obj_func *) _add_new_entry,
                       container);

    /*
     * free the container. we've either claimed each entry, or released it,
     * so the access function doesn't need to clear the container.
     */
    netsnmp_access_defaultrouter_container_free(defaultrouter_container,
                NETSNMP_ACCESS_DEFAULTROUTER_FREE_DONT_CLEAR);

    /*
     * remove deleted addresses from table container
     */
    if (NULL != tmp_ptr[1]) {
        netsnmp_container *tmp_container =
            (netsnmp_container *) tmp_ptr[1];
        ipDefaultRouterTable_rowreq_ctx *tmp_ctx;

        /*
         * this works because the tmp_container is a linked list,
         * which can be used like a stack...
         */
        while (CONTAINER_SIZE(tmp_container)) {
            /*
             * get from delete list
             */
            tmp_ctx = (ipDefaultRouterTable_rowreq_ctx *) CONTAINER_FIRST(tmp_container);

            /*
             * release context, delete from table container
             */
            CONTAINER_REMOVE(container, tmp_ctx);
            ipDefaultRouterTable_release_rowreq_ctx(tmp_ctx);

            /*
             * pop off delete list
             */
            CONTAINER_REMOVE(tmp_container, NULL);
        }
    }

    DEBUGMSGT(("verbose:ipDefaultRouterTable:ipDefaultRouterTable_container_load",
               "%" NETSNMP_PRIz "d records\n", CONTAINER_SIZE(container)));

    return MFD_SUCCESS;
}