/* * * * Facility: * * SNMP Extension Agent * * Abstract: * * This module contains support functions for the creation and * maintenance of the in-memory cache for the HostMIB Subagent. * * * Author: * * D. D. Burns @ WebEnable, Inc. * * * Revision History: * * V1.00 - 04/17/97 D. D. Burns Original Creation * * */ /* Host-MIB Cache Overview ----------------------- This module contains functions that create, maintain and allow for searching data structures that are used to implement a cache of HOST-MIB information. Typically, a cache is created on a "per-table" basis and is formed as a linked-list of CACHEROW structures (all cache structures are defined in "HMCACHE.H"), one CACHEROW structure for each logical "row" in the table. The list-head of each cache is a CACHEHEAD structure and is instantiated in the source module for the functions that service the attributes in that table (so the CACHEHEAD structure for the cache for the "hrStorage" table is in module "HRSTOENT.C"). Caches are created at start-up time by special cache-creation functions (coded to the specs for each table) in each of the "table" source modules. Those cache-creation functions (plus the associated "get" and "set" functions) use the general cache manipulation functions in this module. For example, a typical cache looks like this: HrStorage Table Cache "hrStorage_cache" (statically allocated in "HRSTOENT.C")... *============* | CACHEHEAD | | "list"....|--* ..(Malloced as a single instance in function *============* | . "CreateTableRow") V . *================* ..(Malloced as an | CACHEROW | . array in function *--|...."next" | . "CreateTableRow()") | | "index".....|--> "1" . | | "attrib_list"..|--> *===============* | *================* | ATTRIB | | | "attrib_type".|-->CA_NUMBER | | "u.unumber"...|-->"4" | +---------------+ | | ATTRIB | | | "attrib_type".|-->CA_STRING | | "u.string"....|-->"" | +---------------+ | | ATTRIB | | | "attrib_type".|-->CA_CACHE | | "u.cache"...|------* | +---------------+ | | . | | *=================* . | *->| CACHEROW | . | *--|...."next" | *============* | | "index"......|-->"2" | CACHEHEAD | | | "attrib_list"..|--> | "list"....|--* | *=================* *============* | V (For doubly | indexed V tables) The general cache manipulation functions in this module include: Name Purpose ---- ------- CreateTableRow Creates an instance of a CACHEROW with a given attribute count. (This function does not link the instance into any list, it merely mallocs storage). AddTableRow Given an index value, a CACHEROW instance (created by "CreateTableRow()" above) and a CACHEHEAD, this function links the CACHEROW instance into the list described by CACHEHEAD in the proper place given the index value. These two functions above are used to populate the cache (typically at startup time). Name Purpose ---- ------- FindTableRow Given an index value and a CACHEHEAD, this function returns a pointer to the CACHEROW instance in the CACHEHEAD cache that has the given index. This function is used to find a given cache entry (ie table "row") in service for a "get" or "set" routine. FindNextTableRow Given an index value and a CACHEHEAD, this function returns a pointer to the CACHEROW instance in the CACHEHEAD cache that IMMEDIATELY FOLLOWS the given index. This function is used to find a given cache entry (ie table "row") in service for "get-next" situations. GetNextTableRow Given a CACHEROW row (obtained using either of the routines above), this gets the next entry regardless of index (or NULL if the given row is the last row). (Implemented as a macro in "HMCACHE.H"). RemoveTableRow Given an index value and a CACHEHEAD, this function unlinks the CACHEROW instance from the cache list described by CACHEHEAD. (TBD in support of PNP) DestroyTable Given a pointer to a CACHEHEAD, this function releases every row-instance in the cache (through calls to "DestroyTableRow()" below). This function presumes that the CACHEHEAD itself is statically allocated. DestroyTableRow Given an instance of an (unlinked) CACHEROW, this function releases the storage associated with it. For Debugging: Name Purpose ---- ------- PrintCache Prints a dump on an output file in ASCII of the contents of a specified cache. Only works for caches for which a "print-row" function is defined and referenced in the CACHEHEAD structure for the cache. */ /* | INCLUDES: */ #include #include #include /* for debug printf */ #include /* for debug support */ #include #include "hmcache.h" /* |============================================================================== | Debug File Channel | */ #if defined(CACHE_DUMP) || defined(PROC_CACHE) FILE *Ofile; #endif /* CreateTableRow - Create a CACHEROW structure for attributes in a table */ /* CreateTableRow - Create a CACHEROW structure for attributes in a table */ /* CreateTableRow - Create a CACHEROW structure for attributes in a table */ CACHEROW * CreateTableRow( ULONG attribute_count ) /* | EXPLICIT INPUTS: | | "attribute_count" indicates how much storage to allocate for the | array of attributes for this row. | | IMPLICIT INPUTS: | | None. | | OUTPUTS: | | On Success: | Function returns a pointer to allocated storage containing an | image of a CACHEROW structure. Enough storage for each attribute | in the row has been allocated within array "attrib_list[]" and the | count of these elements has been stored in the CACHEROW structure. | | On any Failure: | Function returns NULL (indicating "not enough storage"). | | THE BIG PICTURE: | | At subagent startup time, the caches for each table in the MIB is | populated with rows for each row in the table. This function is | invoked by the start-up code for each table to create one CACHEROW | structure for each row needed in the table. | | With the advent of PNP, this function can be called after startup | time to add rows to an existing table. | | (Note: The actual insertion of an instance returned by this function | into a cache table list is done by function "AddTableRow()" ). | | OTHER THINGS TO KNOW: | | "DestroyTableRow()" deallocates storage associated with any instance | of a CACHEROW structure created by this function. | */ { CACHEROW *new=NULL; /* New CACHEROW instance to be created */ ULONG i; /* handy-dandy Index */ /* Create the main CACHEROW structure to be returned . . . */ if ( (new = (CACHEROW *) malloc(sizeof(CACHEROW))) == NULL) { /* "Not Enough Storage" */ return (NULL); } /* | Now try to allocate enough storage for the array of attributes in this row */ if ( (new->attrib_list = (ATTRIB *) malloc(sizeof(ATTRIB) * attribute_count)) == NULL) { /* "Not Enough Storage" */ free( new ) ; /* Blow off the CACHEROW we won't be returning */ return (NULL); } /* Indicate how big this array is so DestroyTableRow() can do the right thing*/ new->attrib_count = attribute_count; /* Zap each array entry so things are clean */ for (i = 0; i < attribute_count; i += 1) { new->attrib_list[i].attrib_type = CA_UNKNOWN; new->attrib_list[i].u.string_value = NULL; } new->index = 0; /* No legal index yet */ new->next = NULL; /* Not in the cache list yet */ /* Return the newly allocated CACHEROW structure for further population */ return ( new ) ; } /* DestroyTable - Destroy all rows in CACHEHEAD structure (Release Storage) */ /* DestroyTable - Destroy all rows in CACHEHEAD structure (Release Storage) */ /* DestroyTable - Destroy all rows in CACHEHEAD structure (Release Storage) */ void DestroyTable( CACHEHEAD *cache /* Cache whose rows are to be Released */ ) /* | EXPLICIT INPUTS: | | "cache" is the CACHEHEAD instance of a table for which all rows are | to be released. | | IMPLICIT INPUTS: | | None. | | OUTPUTS: | | On Success/Failure: | Function returns, the CACHEHEAD is set to reflect an empty cache. | | THE BIG PICTURE: | | At subagent startup time, the caches for each table in the MIB is | populated with rows for each row in the table. "CreateTableRow" is | invoked by the start-up code for each table to create one CACHEROW | structure for each row needed in the table. | | With the advent of PNP, this function can be called after startup | time to delete all storage associated with such a cache. | | | OTHER THINGS TO KNOW: | | This function may be recursively invoked through the call to | "DestroyTable()" inside "DestroyTableRow()". | | This function may be safely invoked on an "empty" cache-head. | | This function doesn't attempt to release storage associated with | the CACHEHEAD structure itself. | | DOUBLE NOTE: | This function simply releases storage. You can't go calling this | function willy-nilly on just any cache without taking into | consideration the semantics of what maybe being blown away. For | instance, some tables in Host MIB contain attributes whose values | are indices into other tables. If a table is destroyed and rebuilt, | clearly the references to the rebuilt table must be refreshed in | some manner. */ { /* | If an old copy of the cache exists, blow it away now */ while (cache->list != NULL) { CACHEROW *row_to_go; /* Pick up the row to blow away */ row_to_go = cache->list; /* Change the cache-head to point to the next row (if any) */ cache->list = GetNextTableRow(row_to_go); DestroyTableRow(row_to_go); } /* Show no entries in the cache */ cache->list_count = 0; } /* DestroyTableRow - Destroy a CACHEROW structure (Release Storage) */ /* DestroyTableRow - Destroy a CACHEROW structure (Release Storage) */ /* DestroyTableRow - Destroy a CACHEROW structure (Release Storage) */ void DestroyTableRow( CACHEROW *row /* Row to be Released */ ) /* | EXPLICIT INPUTS: | | "row" is the instance of a row to be released. | | IMPLICIT INPUTS: | | None. | | OUTPUTS: | | On Success/Failure: | Function returns. | | THE BIG PICTURE: | | At subagent startup time, the caches for each table in the MIB is | populated with rows for each row in the table. "CreateTableRow" is | invoked by the start-up code for each table to create one CACHEROW | structure for each row needed in the table. | | With the advent of PNP, this function can be called after startup | time to delete storage associated with rows being replaced in an | existing table. | | (Note: The actual deletion of an row instance from a cache table list | must be done before this function is called). | | OTHER THINGS TO KNOW: | | "CreateTableRow()" creates an instance of what this function | "destroys". | | This function may be recursively invoked through the call to | "DestroyTable()" in the event we are release a row that contains | an attribute "value" that is really another table (in the case | of a multiply-indexed attribute). | | DOUBLE NOTE: | This function simply releases storage. You can't go calling this | function willy-nilly on just any cache without taking into | consideration the semantics of what maybe being blown away. For | instance, some tables in Host MIB contain attributes whose values | are indices into other tables. If a row is destroyed, clearly the | references to the row must be refreshed in some manner. */ { CACHEROW *new=NULL; /* New CACHEROW instance to be created */ ULONG i; /* handy-dandy Index */ /* Zap storage associated each attribute entry (if any) */ for (i = 0; i < row->attrib_count; i += 1) { /* Blow off storage for attribute values that have malloc-ed storage */ switch (row->attrib_list[i].attrib_type) { case CA_STRING: free( row->attrib_list[i].u.string_value ); break; case CA_CACHE: /* Release the contents of the entire cache */ DestroyTable( row->attrib_list[i].u.cache ); /* Free the storage containing the cache free ( row->attrib_list[i].u.cache ); break; case CA_NUMBER: case CA_COMPUTED: case CA_UNKNOWN: /* No malloced storage associated with these types */ default: break; } } /* Free the storage associated with the array of attributes */ free( row->attrib_list); /* Free the storage for the row itself */ free( row ); } /* AddTableRow - Adds a specific "row" into a cached "table" */ /* AddTableRow - Adds a specific "row" into a cached "table" */ /* AddTableRow - Adds a specific "row" into a cached "table" */ BOOL AddTableRow( ULONG index, /* Index for row desired */ CACHEROW *row, /* Row to be added to .. */ CACHEHEAD *cache /* this cache */ ) /* | EXPLICIT INPUTS: | | "index" is index inserted into "row" before the | "row" is added to "cache". | | IMPLICIT INPUTS: | | None. | | OUTPUTS: | | On Success: | Function returns TRUE indicating that the row was successfully | added to the cache for the table. | | On Failure: | Function returns FALSE, indicating that the row already | existed. | | THE BIG PICTURE: | | At startup time the subagent is busy populating the cache for | each table. The rows in any cached table are inserted into | the cache by this function. | | OTHER THINGS TO KNOW: | | Code in this function presumes that the list (cache) is in sorted index | order. | | Any change of organization of the linked list that constitutes the | cache will impact this function and "Find(Next)TableRow()". | */ { CACHEROW **index_row; /* Used for searching cache */ /* NOTE: It always points at a cell that */ /* points to the next list element */ /* (if any is on the list). */ /* Whip down the list until there is no "next" or "next" is "bigger" . . . */ for ( index_row = &cache->list; *index_row != NULL; index_row = &((*index_row)->next) ) { /* If this row MATCHES the to-be-inserted row: Error! */ if ((*index_row)->index == index) { return ( FALSE ); } /* | If next cache entry is "Greater Than" new index, then | "index_row" points to the cell that should be changed to insert | the new entry. */ if ((*index_row)->index > index) { break; } /* Otherwise we should try for a "next" entry in the list */ } /* | When we fall thru here "index_row" contains the address of the cell to | change to add the new row into the cache (might be in the list-head, | might be in a list-entry) */ row->next = *index_row; /* Put cache-list "next" into new row element */ *index_row = row; /* Insert new row into the list */ row->index = index; /* Stick the index into the row entry itself */ cache->list_count += 1; /* Count another entry on the cache list */ /* Successful insertion */ return (TRUE); } /* FindTableRow - Finds a specific "row" in a cached "table" */ /* FindTableRow - Finds a specific "row" in a cached "table" */ /* FindTableRow - Finds a specific "row" in a cached "table" */ CACHEROW * FindTableRow( ULONG index, /* Index for row desired */ CACHEHEAD *cache /* Table cache to search */ ) /* | EXPLICIT INPUTS: | | "index" indicates which table row entry is desired | "cache" indicates the cache list to search for the desired row. | | IMPLICIT INPUTS: | | None. | | OUTPUTS: | | On Success: | Function returns a pointer to the instance of a CACHEROW structure | for the desired row. | | On any Failure: | Function returns NULL (indicating "no such entry" or "cache empty"). | | THE BIG PICTURE: | | As the subagent runs, the "get" functions for the attributes that do | not "compute" their values dynamically must lookup cached values. | | This function can be used by any "get" function that knows the | CACHEHEAD for it's table to find a specific row containing an | attribute value to be returned. | | OTHER THINGS TO KNOW: | | Code in this function presumes that the list is in sorted index | order (hence it gives up once encountering an entry whose index | is "too big"). | | Any change of organization of the linked list that constitutes the | cache will impact this function and "AddTableRow()". | */ { CACHEROW *row=NULL; /* Row instance to be returned, initially none */ /* Whip down the list until there is no next . . */ for ( row = cache->list; row != NULL; row = row->next ) { /* If this is "It": Return it */ if (row->index == index) { return ( row ); } /* If this is "Greater Than IT", it's not in the list: Return NULL */ if (row->index > index) { return ( NULL ); } /* Otherwise we should try for a "next" entry in the list */ } /* | If we fall thru here we didn't find the desired entry because the cache | list is either empty or devoid of the desired row. */ return (NULL); } /* FindNextTableRow - Finds Next row after a given "index" in a cache */ /* FindNextTableRow - Finds Next row after a given "index" in a cache */ /* FindNextTableRow - Finds Next row after a given "index" in a cache */ CACHEROW * FindNextTableRow( ULONG index, /* Index for row desired */ CACHEHEAD *cache /* Table cache to search */ ) /* | EXPLICIT INPUTS: | | "index" indicates which table row entry AFTER WHICH the NEXT is | desired. The "index" row need not exist (could be before | the first row or a missing row "in the middle", but | it may not specify a row that would be after the | last in the table. | | "cache" indicates the cache list to search for the desired row. | | IMPLICIT INPUTS: | | None. | | OUTPUTS: | | On Success: | Function returns a pointer to the instance of a CACHEROW structure | for the desired NEXT row. | | On any Failure: | Function returns NULL (indicating "no such entry", "cache empty" or | "end-of-cache" reached). | | THE BIG PICTURE: | | As the subagent runs, the "get-next" functions for the attributes | that do not "compute" their values dynamically must lookup cached | values. | | This function can be used by any "FindNextInstance" function that | knows the CACHEHEAD for it's table to find the NEXT row following a | specific row containing an attribute value to be returned. | | OTHER THINGS TO KNOW: | | To get the first entry in a table, supply an ("illegal") index of 0. | | Any change of organization of the linked list that constitutes the | cache will impact this function, "FindTableRow()" and "AddTableRow()". | */ { CACHEROW *row=NULL; /* Row instance to be returned, initially none */ /* | If there is a non-empty cache and the input "index" is less than | the first entry in the cache, simply return the first entry. */ if ( cache->list != NULL /* If there is a non-empty cache . . . */ && index < cache->list->index /* AND index is LESS THAN first entry */ ) { /* Return the first entry in the table */ return (cache->list); } /* Whip down the list until there is no next . . */ for ( row = cache->list; row != NULL; row = row->next ) { /* If "index" specifies THIS ROW . . . */ if (row->index == index) { return ( row->next ); /* Return NEXT (or NULL if no "next") */ } /* If this is "Greater Than IT", "index" is not in the list */ if (row->index > index) { return ( row ); /* Return CURRENT, it is Greater than "index"*/ } /* Otherwise we should try for a "next" entry in the list */ } /* | If we fall thru here then the cache is empty or the "index" specifies | a row after the last legal row. */ return (NULL); } #if defined(CACHE_DUMP) /* PrintCache - Dumps for debugging the contents of a cache */ /* PrintCache - Dumps for debugging the contents of a cache */ /* PrintCache - Dumps for debugging the contents of a cache */ void PrintCache( CACHEHEAD *cache /* Table cache to dump */ ) /* | EXPLICIT INPUTS: | | "cache" indicates the cache whose contents is to be dumped. | | IMPLICIT INPUTS: | | None. | | OUTPUTS: | | On Success: | Function returns. It may be called recursively by a "Print-Row" | function. | | | THE BIG PICTURE: | | For debugging only. | | OTHER THINGS TO KNOW: | | Define "CACHE_DUMP" at the top of "HMCACHE.H" to enable this debug | support. You can change the file into which output goes by modifying | "DUMP_FILE", also in "HMCACHE.H". | */ #define DO_CLOSE \ { if ((open_count -= 1) == 0) { fclose(OFILE); } } { CACHEROW *row; /* Row instance to dump. */ UINT i; /* Element counter */ time_t ltime; /* For debug message */ static UINT open_count=0; /* We can be called recursively */ /* Avoid a recursive open */ if (open_count == 0) { /* Open the debug log file */ if ((Ofile=fopen(DUMP_FILE, "a+")) == NULL) { return; } /* | Put a time stamp into the debug file because we're opening for append. */ time( <ime); fprintf(OFILE, "=============== Open for appending: %s\n", ctime( <ime )); } open_count += 1; if (cache == NULL) { fprintf(OFILE, "Call to PrintCache with NULL CACHEHEAD pointer.\n"); DO_CLOSE; return; } if (cache->print_row == NULL) { fprintf(OFILE, "Call to PrintCache with NULL CACHEHEAD Print-Routine pointer.\n"); DO_CLOSE; return; } /* Print a Title */ cache->print_row(NULL); fprintf(OFILE, "Element Count: %d\n", cache->list_count); /* For every row in the cache . . . */ for (row = cache->list, i = 0; row != NULL; row = row->next, i += 1) { fprintf(OFILE, "\nElement #%d, Internal Index %d, at 0x%x:\n", i, row->index, row); cache->print_row(row); } fprintf(OFILE, "======== End of Cache ========\n\n"); DO_CLOSE; } #endif // defined(CACHE_DUMP)