swinst.patch   [plain text]


diff -I '\$Id: ' -u -r -b -w -p -d --new-file --exclude-from=/Users/rstory/.rcfiles/diff-ignore SVN/agent/mibgroup/host/data_access/swinst.c APPLE/agent/mibgroup/host/data_access/swinst.c
--- SVN/agent/mibgroup/host/data_access/swinst.c
+++ APPLE/agent/mibgroup/host/data_access/swinst.c
@@ -0,0 +1,211 @@
+/*
+ * swinst.c : hrSWInstalledTable data access
+ */
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <net-snmp/data_access/swinst.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+/* ---------------------------------------------------------------------
+ */
+
+static void netsnmp_swinst_entry_free_cb(netsnmp_swinst_entry *, void *);
+
+extern void netsnmp_swinst_arch_init(void);
+extern void netsnmp_swinst_arch_shutdown(void);
+extern int netsnmp_swinst_arch_load(netsnmp_container *, u_int);
+
+void init_swinst( void )
+{
+    static int initialized = 0;
+
+    DEBUGMSGTL(("swinst", "init called\n"));
+
+    if (initialized)
+        return; /* already initialized */
+
+
+    /*
+     * call arch init code
+     */
+    netsnmp_swinst_arch_init();
+}
+
+void shutdown_swinst( void )
+{
+    DEBUGMSGTL(("swinst", "shutdown called\n"));
+
+    netsnmp_swinst_arch_shutdown();
+}
+
+/* ---------------------------------------------------------------------
+ */
+
+/*
+ * load a container with installed software. If user_container is NULL,
+ * a new container will be allocated and returned, and the caller
+ * is responsible for releasing the allocated memory when done.
+ *
+ * if flags contains NETSNMP_SWINST_ALL_OR_NONE and any error occurs,
+ * the container will be completely cleared.
+ */
+netsnmp_container *
+netsnmp_swinst_container_load( netsnmp_container *user_container, int flags )
+{
+    netsnmp_container *container = user_container;
+    int arch_rc;
+
+    DEBUGMSGTL(("swinst:container", "load\n"));
+
+    /*
+     * create the container, if needed
+     */
+    if (NULL == container) {
+        container = netsnmp_container_find("swinst:table_container");
+        if (NULL == container)
+            return NULL;
+    }
+    if (NULL == container->container_name)
+        container->container_name = strdup("swinst container");
+
+    /*
+     * call the arch specific code to load the container
+     */
+    arch_rc = netsnmp_swinst_arch_load( container, flags );
+    if (arch_rc && (flags & NETSNMP_SWINST_ALL_OR_NONE)) {
+        /*
+         * caller does not want a partial load, so empty the container.
+         * if we created the container, destroy it.
+         */
+        netsnmp_swinst_container_free_items(container);
+        if (container != user_container) {
+            netsnmp_swinst_container_free(container, flags);
+        }
+    }
+    
+    return container;
+}
+
+void
+netsnmp_swinst_container_free(netsnmp_container *container, u_int free_flags)
+{
+    DEBUGMSGTL(("swinst:container", "free\n"));
+
+    if (NULL == container) {
+        snmp_log(LOG_ERR,
+                 "invalid container for netsnmp_swinst_container_free\n");
+        return;
+    }
+
+    if(! (free_flags & NETSNMP_SWINST_DONT_FREE_ITEMS))
+        netsnmp_swinst_container_free_items(container);
+
+    CONTAINER_FREE(container);
+}
+
+/*
+ * free a swinst container
+ */
+void netsnmp_swinst_container_free_items(netsnmp_container *container)
+{
+    DEBUGMSGTL(("swinst:container", "free_items\n"));
+
+    if (NULL == container) {
+        snmp_log(LOG_ERR,
+                 "invalid container for netsnmp_swinst_container_free_items\n");
+        return;
+    }
+
+    /*
+     * free all items.
+     */
+    CONTAINER_CLEAR(container,
+                    (netsnmp_container_obj_func*)netsnmp_swinst_entry_free_cb,
+                    NULL);
+}
+
+
+/* ---------------------------------------------------------------------
+ */
+
+/*
+ * create a new row in the table 
+ */
+netsnmp_swinst_entry *
+netsnmp_swinst_entry_create(int32_t swIndex)
+{
+    netsnmp_swinst_entry *entry;
+
+    entry = SNMP_MALLOC_TYPEDEF(netsnmp_swinst_entry);
+    if (!entry)
+        return NULL;
+
+    entry->swIndex = swIndex;
+    entry->oid_index.len = 1;
+    entry->oid_index.oids = &entry->swIndex;
+
+    entry->swType = HRSWINSTALLEDTYPE_APPLICATION;
+
+    return entry;
+}
+
+/*
+ * free a row
+ */
+void
+netsnmp_swinst_entry_free(netsnmp_swinst_entry *entry)
+{
+    DEBUGMSGTL(("swinst:entry:free", "index %d\n",entry->swIndex));
+
+    free(entry);
+}
+
+/*
+ * free a row
+ */
+static void
+netsnmp_swinst_entry_free_cb(netsnmp_swinst_entry *entry, void *context)
+{
+    free(entry);
+}
+
+/*
+ * remove a row from the table 
+ */
+void
+netsnmp_swinst_entry_remove(netsnmp_container * container,
+                            netsnmp_swinst_entry *entry)
+{
+    DEBUGMSGTL(("swinst:container", "remove\n"));
+    if (!entry)
+        return;                 /* Nothing to remove */
+    CONTAINER_REMOVE(container, entry);
+}
+
+/* ---------------------------------------------------------------------
+ */
+
+#ifdef TEST
+int main(int argc, char *argv[])
+{
+    const char *tokens = getenv("SNMP_DEBUG");
+
+    netsnmp_container_init_list();
+
+    /** swinst,verbose:swinst */
+    if (tokens)
+        debug_register_tokens(tokens);
+    else
+        debug_register_tokens("swinst");
+    snmp_set_do_debugging(1);
+
+    init_swinst();
+    netsnmp_swinst_container_load(NULL, 0);
+    shutdown_swinst();
+
+    return 0;
+}
+#endif
diff -I '\$Id: ' -u -r -b -w -p -d --new-file --exclude-from=/Users/rstory/.rcfiles/diff-ignore SVN/agent/mibgroup/host/data_access/swinst.h APPLE/agent/mibgroup/host/data_access/swinst.h
--- SVN/agent/mibgroup/host/data_access/swinst.h
+++ APPLE/agent/mibgroup/host/data_access/swinst.h
@@ -0,0 +1,46 @@
+/*
+ * swinst data access header
+ *
+ * $Id: swinst.patch,v 1.3 2007/08/16 22:09:13 randall Exp $
+ */
+#ifndef NETSNMP_ACCESS_SWINST_CONFIG_H
+#define NETSNMP_ACCESS_SWINST_CONFIG_H
+
+/*
+ * all platforms use this generic code
+ */
+config_require(host/data_access/swinst)
+
+/**---------------------------------------------------------------------*/
+/*
+ * configure required files
+ *
+ * Notes:
+ *
+ * 1) prefer functionality over platform, where possible. If a method
+ *    is available for multiple platforms, test that first. That way
+ *    when a new platform is ported, it won't need a new test here.
+ *
+ * 2) don't do detail requirements here. If, for example,
+ *    HPUX11 had different reuirements than other HPUX, that should
+ *    be handled in the *_hpux.h header file.
+ */
+
+#ifdef NETSNMP_INCLUDE_HRSWINST_REWRITES
+
+config_exclude(host/hr_swinst)
+
+#   if defined( darwin )
+
+    config_require(host/data_access/swinst_darwin)
+
+#   else
+
+    config_error(This platform does not yet support hrSWInstalledTable rewrites)
+
+#   endif
+#else
+#   define NETSNMP_ACCESS_SWINST_NOARCH 1
+#endif
+
+#endif /* NETSNMP_ACCESS_SWINST_CONFIG_H */
diff -I '\$Id: ' -u -r -b -w -p -d --new-file --exclude-from=/Users/rstory/.rcfiles/diff-ignore SVN/agent/mibgroup/host/data_access/swinst_darwin.c APPLE/agent/mibgroup/host/data_access/swinst_darwin.c
--- SVN/agent/mibgroup/host/data_access/swinst_darwin.c
+++ APPLE/agent/mibgroup/host/data_access/swinst_darwin.c
@@ -0,0 +1,385 @@
+/*
+ * swinst.c : hrSWInstalledTable data access
+ */
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <net-snmp/library/container.h>
+#include <net-snmp/library/dir_utils.h>
+#include <net-snmp/library/snmp_debug.h>
+#include <net-snmp/data_access/swinst.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#define __APPLE_API_EVOLVING 1
+#include <sys/acl.h> /* or else CoreFoundation.h barfs */
+#undef __APPLE_API_EVOLVING 
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <ApplicationServices/ApplicationServices.h>
+
+/* ---------------------------------------------------------------------
+ */
+static int _add_applications_in_dir(netsnmp_container *, const char* path);
+static int32_t _index;
+static int _check_bundled_app(CFURLRef currentURL, CFStringRef *name,
+                              CFStringRef *info, const char* path);
+static int _check_classic_app(CFURLRef currentURL, CFStringRef *name,
+                              CFStringRef *info, const char* path);
+static netsnmp_container *dirs = NULL;
+
+/* ---------------------------------------------------------------------
+ */
+void
+netsnmp_swinst_arch_init( void )
+{
+    struct stat stat_buf;
+    const char *default_dirs[] = {
+        "/Applications",
+        "/Applications (Mac OS 9)",
+        "/System/Library/CoreServices",
+        "/System/Library/Extensions",
+        "/System/Library/Services"
+#ifdef TEST
+        , "/Developer/Applications"
+        , "/Volumes/audX/Applications (Mac OS 9)"
+#endif
+    };
+    int i, count = sizeof(default_dirs)/sizeof(default_dirs[0]);
+
+    /*
+     * create the container, if needed
+     */
+    if (NULL == dirs) {
+        dirs = netsnmp_container_find("directory_container:cstring");
+        if (NULL == dirs) {
+            snmp_log(LOG_ERR, "couldn't allocate container for dir list\n");
+            return;
+        }
+        dirs->container_name = strdup("directory search list");
+        netsnmp_binary_array_options_set(dirs, 1, CONTAINER_KEY_UNSORTED);
+    }
+
+    /*
+     * add dirs
+     */
+    for(i = 0; i < count; ++i) {
+        char *      tmp;
+        /** xxx: get/save the last mod date? */
+        if(-1 == stat(default_dirs[i], &stat_buf)) {
+            DEBUGMSGTL(("swinst:arch:darwin", "skipping dir %s\n",
+                        default_dirs[i]));
+            continue;
+        }
+        DEBUGMSGTL(("swinst:arch:darwin", "adding dir %s\n",
+                        default_dirs[i]));
+        tmp = strdup(default_dirs[i]);
+        if (NULL == tmp) {
+            snmp_log(LOG_ERR,"strdup failed\n");
+            break;
+        }
+        CONTAINER_INSERT(dirs, tmp);
+    }
+}
+
+void
+netsnmp_swinst_arch_shutdown( void )
+{
+    netsnmp_directory_container_free(dirs);
+}
+
+/* ---------------------------------------------------------------------
+ */
+
+int
+netsnmp_swinst_arch_load( netsnmp_container *container, u_int flags )
+{
+    netsnmp_iterator   *it;
+    const char         *dir;
+    int                 rc;
+
+    DEBUGMSGTL(("swinst:arch:darwin", "load\n"));
+
+    if (NULL == dirs) {
+        DEBUGMSGTL(("swinst:arch:darwin", "no dirs to scan!\n"));
+        return -1;
+    }
+
+    _index = 0;
+    
+    it = CONTAINER_ITERATOR(dirs);
+    for (dir = ITERATOR_FIRST(it); dir; dir = ITERATOR_NEXT(it)) {
+        rc = _add_applications_in_dir(container, dir);
+    }
+    ITERATOR_RELEASE(it);
+    DEBUGMSGTL(("swinst:arch:darwin", "loaded %d apps\n",_index));
+
+    return 0;
+}
+
+void  _dump_flags(u_long flags)
+{
+    static struct {
+        const char*name;
+        u_long bits;
+    } names[] = {
+        { "kLSItemInfoIsPlainFile", 0x00000001 },
+        { "kLSItemInfoIsPackage", 0x00000002 },
+        { "kLSItemInfoIsApplication", 0x00000004 },
+        { "kLSItemInfoIsContainer", 0x00000008 },
+        { "kLSItemInfoIsAliasFile", 0x00000010 },
+        { "kLSItemInfoIsSymlink", 0x00000020 },
+        { "kLSItemInfoIsInvisible", 0x00000040 },
+        { "kLSItemInfoIsNativeApp", 0x00000080 },
+        { "kLSItemInfoIsClassicApp", 0x00000100 },
+        { "kLSItemInfoAppPrefersNative", 0x00000200 },
+        { "kLSItemInfoAppPrefersClassic", 0x00000400 },
+        { "kLSItemInfoAppIsScriptable", 0x00000800 },
+        { "kLSItemInfoIsVolume", 0x00001000 },
+        { "kLSItemInfoExtensionIsHidden", 0x00100000 }
+    };
+    int i, count = sizeof(names)/sizeof(names[0]);
+
+    for(i = 0; i < count; ++i) {
+        if (flags & names[i].bits) {
+            DEBUGMSGTL(("swinst:arch:darwin:flags", "\t%s\n",
+                       names[i].name));
+        }
+    }
+}
+
+static int
+_add_applications_in_dir(netsnmp_container *container, const char* path)
+{
+    netsnmp_container  *files;
+    netsnmp_iterator   *it;
+    const char         *file;
+    netsnmp_swinst_entry *entry = NULL;
+    struct stat	        stat_buf;
+    size_t              date_len;
+    u_char             *date_buf;
+    int                 rc = 0;
+
+    CFStringRef         currentPath = NULL;
+    CFURLRef            currentURL = NULL;
+    LSItemInfoRecord    itemInfoRecord;
+    CFStringRef         prodName = NULL;
+    CFStringRef         version = NULL;
+    
+    DEBUGMSGTL(("swinst:arch:darwin", " adding files from %s\n", path));
+    files = netsnmp_directory_container_read(NULL, path, 0);
+    if (NULL == files) {
+        snmp_log(LOG_ERR, "swinst: could not read directory %s\n", path);
+        return -1;
+    }
+
+    it = CONTAINER_ITERATOR(files);
+    if (NULL == it) {
+        snmp_log(LOG_ERR, "could not get iterator\n");
+        netsnmp_directory_container_free(files);
+        return -1;
+    }
+    for (file = ITERATOR_FIRST(it);
+         file;
+         file = ITERATOR_NEXT(it),
+             CFRelease(currentPath),
+             CFRelease(currentURL)) {
+
+        int                 rc2 = 0;
+        
+        currentPath =
+            CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, file,
+                                            kCFStringEncodingUTF8,
+                                            kCFAllocatorNull);
+        currentURL =
+            CFURLCreateWithFileSystemPath(kCFAllocatorDefault, currentPath,
+                                          kCFURLPOSIXPathStyle, true); 
+        LSCopyItemInfoForURL(currentURL,
+                             kLSRequestBasicFlagsOnly|kLSRequestAppTypeFlags,
+                             &itemInfoRecord); 
+        if((0 == itemInfoRecord.flags) ||
+           (kLSItemInfoIsPlainFile == itemInfoRecord.flags) ||
+           (itemInfoRecord.flags & kLSItemInfoIsInvisible) ||
+           (itemInfoRecord.flags & kLSItemInfoIsAliasFile)) {
+            continue;
+        }
+        /** recurse on non-application containers (i.e. directory) */
+        if ((itemInfoRecord.flags & kLSItemInfoIsContainer) &&
+            (!(itemInfoRecord.flags & kLSItemInfoIsApplication))) {
+            netsnmp_directory_container_read(files, file, 0);
+            continue;
+       }
+
+        /** skip any other non-application files */
+        if (!(itemInfoRecord.flags & kLSItemInfoIsApplication)) {
+            continue;
+       }
+
+        if ((itemInfoRecord.flags & kLSItemInfoIsPackage) ||           
+            (itemInfoRecord.flags & kLSItemInfoIsContainer)) {
+            rc2 = _check_bundled_app(currentURL, &prodName, &version, file);
+        } 
+        else if ((itemInfoRecord.flags & kLSItemInfoIsClassicApp) ||
+                 (itemInfoRecord.flags & kLSItemInfoIsPlainFile)) {
+            rc2 = _check_classic_app(currentURL, &prodName, &version, file);
+        } else {
+            snmp_log(LOG_ERR,"swinst shouldn't get here: %s\n", file);
+            _dump_flags(itemInfoRecord.flags);
+            continue;
+        }
+        if (rc2) { /* not an app. if directory, recurse; else continue */
+            _dump_flags(itemInfoRecord.flags);
+            if (1 == rc2)
+                netsnmp_directory_container_read(files, file, 0);
+            continue;
+        }
+        
+        /*
+         * allocate entry
+         */
+        entry = netsnmp_swinst_entry_create(++_index);
+        if (NULL == entry) {
+            snmp_log(LOG_ERR, "error creating swinst entry\n");
+            rc = -1;
+            SNMP_CFRelease(prodName);
+            SNMP_CFRelease(version);
+            break;
+        }
+
+        entry->swName_len =
+            snprintf(entry->swName, sizeof(entry->swName),
+                     "%s %s", CFStringGetCStringPtr(prodName,0),
+                     CFStringGetCStringPtr(version,0));
+
+        DEBUGMSGTL(("swinst:arch:darwin", "\t%s %s\n", file, entry->swName));
+
+        /** get the last mod date */
+        if(stat(file, &stat_buf) != -1) {
+            date_buf = date_n_time(&stat_buf.st_mtime, &date_len);
+            entry->swDate_len = date_len;
+            memcpy(entry->swDate, date_buf, entry->swDate_len);
+        }
+        
+        CONTAINER_INSERT(container, entry);
+        entry = NULL;
+        SNMP_CFRelease(prodName);
+        SNMP_CFRelease(version);
+    }
+    ITERATOR_RELEASE(it);
+    netsnmp_directory_container_free(files);
+
+    return rc;
+}
+
+int
+_check_bundled_app(CFURLRef currentURL, CFStringRef *prodName,
+                   CFStringRef *version, const char* file)
+{
+    CFBundleRef         theBundle = NULL;
+    CFDictionaryRef     infoDict = NULL;
+            
+    if ((NULL == prodName) || (NULL == version))
+       return -1;
+
+    theBundle = CFBundleCreate (kCFAllocatorDefault, currentURL);
+    if(theBundle == NULL)
+        return -1; /* not a bundle */
+
+    infoDict = CFBundleGetInfoDictionary(theBundle);
+    if(0 == CFDictionaryGetCount(infoDict)) {
+        SNMP_CFRelease(theBundle);
+        return 1; /* directory */
+    }
+
+    *prodName = (CFStringRef)
+        CFDictionaryGetValue (infoDict, CFSTR("CFBundleName"));
+    if (NULL == *prodName) {
+        *prodName = (CFStringRef)
+            CFDictionaryGetValue (infoDict, CFSTR("CFBundleDisplayName"));
+        if (NULL == *prodName) {
+            *prodName = (CFStringRef) CFDictionaryGetValue (infoDict,
+                                      CFSTR("CFBundleExecutable"));
+        }
+    }
+    if(NULL == *prodName) {
+        DEBUGMSGTL(("swinst:arch:darwin", "\tmissing name: %s\n",file));
+        /*CFShow(infoDict);*/
+        SNMP_CFRelease(theBundle);
+        return -1;
+    }
+
+    *version = (CFStringRef)
+        CFDictionaryGetValue (infoDict, CFSTR("CFBundleShortVersionString"));
+    if(NULL == *version) {
+        *version = (CFStringRef)
+            CFDictionaryGetValue (infoDict, CFSTR("CFBundleVersion"));
+        if (*version == NULL) 
+            *version = (CFStringRef) CFDictionaryGetValue (infoDict,
+                                      CFSTR("CFBundleGetInfoString"));
+    }
+    if(NULL == *version) {
+        DEBUGMSGTL(("swinst:arch:darwin", "\tmissing version: %s\n",file));
+        /*CFShow(infoDict);*/
+        SNMP_CFRelease(theBundle);
+        return -1;
+    }
+    
+    if(theBundle != NULL) {
+        CFRetain(*prodName);
+        CFRetain(*version);
+        SNMP_CFRelease(theBundle);
+    }
+    return 0;
+}
+
+static int
+_check_classic_app(CFURLRef currentURL, CFStringRef *prodName,
+                   CFStringRef *version, const char* file)
+{
+    /*
+     * get info for classic or single-file apps
+     */
+    FSRef theFSRef;
+    int theResFile;
+
+    if ((NULL == prodName) || (NULL == version))
+       return -1;
+
+    *prodName = CFURLCopyLastPathComponent(currentURL);
+    if (! CFURLGetFSRef(currentURL, &theFSRef)) {
+        DEBUGMSGTL(("swinst:arch:darwin", "GetFSRef failed: %s\n", file));
+        SNMP_CFRelease(*prodName);
+        return -1;
+    }
+    theResFile = FSOpenResFile(&theFSRef, fsRdPerm);
+    if (ResError() != noErr) {
+        DEBUGMSGTL(("swinst:arch:darwin", "FSOpenResFile failed: %s\n", file));
+        SNMP_CFRelease(*prodName);
+        return -1;
+    }
+    VersRecHndl versHandle = (VersRecHndl)Get1IndResource('vers', 1);
+    if (versHandle != NULL) {
+        *version = CFStringCreateWithPascalString(kCFAllocatorDefault,
+                       (**versHandle).shortVersion, kCFStringEncodingMacRoman);
+        if (*version == NULL) {
+            StringPtr longVersionPtr = (**versHandle).shortVersion;
+            longVersionPtr = (StringPtr)(((Ptr) longVersionPtr) +
+                              1 + ((unsigned char) *longVersionPtr));
+            *version = CFStringCreateWithPascalString(kCFAllocatorDefault,
+                          longVersionPtr,  kCFStringEncodingMacRoman);
+        }
+        ReleaseResource((Handle)versHandle);
+    }
+    CloseResFile(theResFile);
+    if(*version == NULL) {
+        DEBUGMSGTL(("swinst:arch:darwin",
+                    "\tmissing classic/file version: %s\n", file));
+        SNMP_CFRelease(*prodName);
+        return -1;
+    }
+
+    return 0;
+}
diff -I '\$Id: ' -u -r -b -w -p -d --new-file --exclude-from=/Users/rstory/.rcfiles/diff-ignore SVN/agent/mibgroup/host/hrSWInstalledTable.c APPLE/agent/mibgroup/host/hrSWInstalledTable.c
--- SVN/agent/mibgroup/host/hrSWInstalledTable.c
+++ APPLE/agent/mibgroup/host/hrSWInstalledTable.c
@@ -0,0 +1,261 @@
+/*
+ * Note: this file originally auto-generated by mib2c using
+ *  : mib2c.container.conf,v 1.8 2006/07/26 15:58:26 dts12 Exp $
+ */
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <net-snmp/agent/table_container.h>
+#include <net-snmp/data_access/swinst.h>
+#include <net-snmp/agent/cache_handler.h>
+#include "hrSWInstalledTable.h"
+
+#define MYTABLE "hrSWInstalledTable"
+
+static void _cache_free(netsnmp_cache * cache, void *magic);
+static int _cache_load(netsnmp_cache * cache, void *magic);
+
+/** Initializes the hrSWInstalledTable module */
+void
+init_hrSWInstalledTable(void)
+{
+    /*
+     * here we initialize all the tables we're planning on supporting 
+     */
+    initialize_table_hrSWInstalledTable();
+}
+
+/** Initialize the hrSWInstalledTable table by defining its contents and how it's structured */
+void
+initialize_table_hrSWInstalledTable(void)
+{
+    static oid      hrSWInstalledTable_oid[] =
+        { 1, 3, 6, 1, 2, 1, 25, 6, 3 };
+    size_t          hrSWInstalledTable_oid_len =
+        OID_LENGTH(hrSWInstalledTable_oid);
+    netsnmp_handler_registration *reg;
+    netsnmp_mib_handler *handler;
+    netsnmp_container *container;
+    netsnmp_cache *cache;
+    netsnmp_table_registration_info *table_info;
+
+    DEBUGMSGTL(("hrSWInstalled", "initialize\n"));
+
+    reg =
+        netsnmp_create_handler_registration("hrSWInstalledTable",
+                                            hrSWInstalledTable_handler,
+                                            hrSWInstalledTable_oid,
+                                            hrSWInstalledTable_oid_len,
+                                            HANDLER_CAN_RONLY);
+    if (NULL == reg) {
+        snmp_log(LOG_ERR,"error creating handler registration for "
+                 MYTABLE "\n");
+        goto bail;
+    }
+
+    container = netsnmp_container_find("hrSWInstalledTable:table_container");
+    if (NULL == container) {
+        snmp_log(LOG_ERR,"error creating container for "
+                 MYTABLE "\n");
+        goto bail;
+    }
+
+    table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
+    if (NULL == table_info) {
+        snmp_log(LOG_ERR,"error allocating table registration for "
+                 MYTABLE "\n");
+        goto bail;
+    }
+
+    netsnmp_table_helper_add_indexes(table_info, ASN_INTEGER,   /* index: hrSWInstalledIndex */
+                                     0);
+    table_info->min_column = COLUMN_HRSWINSTALLEDINDEX;
+    table_info->max_column = COLUMN_HRSWINSTALLEDDATE;
+
+    /*************************************************
+     *
+     * inject container_table helper
+     */
+    handler = netsnmp_container_table_handler_get(table_info, container,
+                                                  TABLE_CONTAINER_KEY_NETSNMP_INDEX);
+    if (NULL == handler) {
+        snmp_log(LOG_ERR,"error allocating table registration for "
+                 MYTABLE "\n");
+        goto bail;
+    }
+    if (SNMPERR_SUCCESS != netsnmp_inject_handler(reg, handler)) {
+        snmp_log(LOG_ERR,"error injecting container_table handler for "
+                 MYTABLE "\n");
+        goto bail;
+    }
+    handler = NULL; /* reg has it, will reuse below */
+
+    /*************************************************
+     *
+     * inject cache helper
+     */
+    cache = netsnmp_cache_create(30,    /* timeout in seconds */
+                                 _cache_load, _cache_free,
+                                 hrSWInstalledTable_oid,
+                                 hrSWInstalledTable_oid_len);
+
+    if (NULL == cache) {
+        snmp_log(LOG_ERR, "error creating cache for "
+                 MYTABLE "\n");
+        goto bail;
+    }
+    cache->magic = container;
+
+    handler = netsnmp_cache_handler_get(cache);
+    if (NULL == handler) {
+        snmp_log(LOG_ERR, "error creating cache handler for "
+                 MYTABLE "\n");
+        goto bail;
+    }
+    if (SNMPERR_SUCCESS != netsnmp_inject_handler(reg, handler)) {
+        snmp_log(LOG_ERR,"error injecting cache handler for "
+                 MYTABLE "\n");
+        goto bail;
+    }
+    handler = NULL; /* reg has it*/
+
+    if (SNMPERR_SUCCESS != netsnmp_register_table(reg, table_info)) {
+        snmp_log(LOG_ERR,"error registering table handler for "
+                 MYTABLE "\n");
+        goto bail;
+    }
+
+    return; /* ok */
+
+
+  bail: /* not ok */
+    
+    if (handler)
+        netsnmp_handler_free(handler);
+
+    if (cache)
+        netsnmp_cache_free(cache);
+
+    if (table_info)
+        netsnmp_table_registration_info_free(table_info);
+
+    if (container)
+        CONTAINER_FREE(container);
+
+    if (reg) 
+        netsnmp_handler_registration_free(reg);
+
+}
+
+/** handles requests for the hrSWInstalledTable table */
+int
+hrSWInstalledTable_handler(netsnmp_mib_handler *handler,
+                           netsnmp_handler_registration *reginfo,
+                           netsnmp_agent_request_info *reqinfo,
+                           netsnmp_request_info *requests)
+{
+
+    netsnmp_request_info *request;
+    netsnmp_table_request_info *table_info;
+    netsnmp_swinst_entry *table_entry;
+
+    switch (reqinfo->mode) {
+        /*
+         * Read-support (also covers GetNext requests)
+         */
+    case MODE_GET:
+        for (request = requests; request; request = request->next) {
+            if (request->processed)
+               continue;
+            table_entry = (netsnmp_swinst_entry *)
+                netsnmp_container_table_extract_context(request);
+            table_info = netsnmp_extract_table_info(request);
+            if ((NULL == table_entry) || (NULL == table_info)) {
+                snmp_log(LOG_ERR, "could not extract table entry or info for "
+                 MYTABLE "\n");
+                snmp_set_var_typed_value(request->requestvb,
+                                         SNMP_ERR_GENERR, NULL, 0);
+                continue;
+            }
+
+            switch (table_info->colnum) {
+            case COLUMN_HRSWINSTALLEDINDEX:
+                snmp_set_var_typed_integer(request->requestvb, ASN_INTEGER,
+                                           table_entry->swIndex);
+                break;
+            case COLUMN_HRSWINSTALLEDNAME: {
+                snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
+                                         (u_char *) table_entry->swName,
+                                         table_entry->swName_len);
+                } break;
+            case COLUMN_HRSWINSTALLEDID:
+                snmp_set_var_typed_value(request->requestvb, ASN_OBJECT_ID,
+                                         (u_char *) &nullOid, nullOidLen);
+                break;
+            case COLUMN_HRSWINSTALLEDTYPE:
+                snmp_set_var_typed_integer(request->requestvb, ASN_INTEGER,
+                                           table_entry->swType);
+                break;
+            case COLUMN_HRSWINSTALLEDDATE:
+                snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR,
+                                         (u_char *) table_entry->swDate, 
+                                         table_entry->swDate_len);
+                break;
+            default:
+                /*
+                 * An unsupported/unreadable column (if applicable) 
+                 */
+                snmp_set_var_typed_value(request->requestvb,
+                                         SNMP_NOSUCHOBJECT, NULL, 0);
+            }
+        }
+        break;
+
+    }
+    return SNMP_ERR_NOERROR;
+}
+
+/***********************************************************************
+ *
+ * DATA ACCESS
+ *
+ * The data access mechanism here is rather simple: let newsnmp_swinst_*
+ * take care of it.
+ ***********************************************************************/
+/**
+ * @internal
+ */
+static int
+_cache_load(netsnmp_cache * cache, void *vmagic)
+{
+    DEBUGMSGTL(("hrSWInstalledTable:cache", "load\n"));
+
+    if ((NULL == cache) || (NULL == cache->magic)) {
+        snmp_log(LOG_ERR, "invalid cache for hrSWInstalledTable_cache_load\n");
+        return -1;
+    }
+
+    /** should only be called for an invalid or expired cache */
+    netsnmp_assert((0 == cache->valid) || (1 == cache->expired));
+
+    cache->magic =
+        netsnmp_swinst_container_load((netsnmp_container *) cache->magic, 0);
+
+    return 0;
+}                               /* _cache_load */
+
+/**
+ * @internal
+ */
+static void
+_cache_free(netsnmp_cache * cache, void *magic)
+{
+    if ((NULL == cache) || (NULL == cache->magic)) {
+        snmp_log(LOG_ERR, "invalid cache in hrSWInstalledTable_cache_free\n");
+        return;
+    }
+    DEBUGMSGTL(("hrSWInstalledTable:cache", "free\n"));
+
+    netsnmp_swinst_container_free_items((netsnmp_container *) cache->magic);
+}                               /* _cache_free */
diff -I '\$Id: ' -u -r -b -w -p -d --new-file --exclude-from=/Users/rstory/.rcfiles/diff-ignore SVN/agent/mibgroup/host/hrSWInstalledTable.h APPLE/agent/mibgroup/host/hrSWInstalledTable.h
--- SVN/agent/mibgroup/host/hrSWInstalledTable.h
+++ APPLE/agent/mibgroup/host/hrSWInstalledTable.h
@@ -0,0 +1,25 @@
+/*
+ * Note: this file originally auto-generated by mib2c using
+ *  : mib2c.container.conf,v 1.8 2006/07/26 15:58:26 dts12 Exp $
+ */
+#ifndef HRSWINSTALLEDTABLE_H
+#define HRSWINSTALLEDTABLE_H
+
+config_require(host/data_access/swinst)
+
+/*
+ * function declarations 
+ */
+void            init_hrSWInstalledTable(void);
+void            initialize_table_hrSWInstalledTable(void);
+Netsnmp_Node_Handler hrSWInstalledTable_handler;
+
+/*
+ * column number definitions for table hrSWInstalledTable 
+ */
+#define COLUMN_HRSWINSTALLEDINDEX		1
+#define COLUMN_HRSWINSTALLEDNAME		2
+#define COLUMN_HRSWINSTALLEDID		3
+#define COLUMN_HRSWINSTALLEDTYPE		4
+#define COLUMN_HRSWINSTALLEDDATE		5
+#endif                          /* HRSWINSTALLEDTABLE_H */
diff -I '\$Id: ' -u -r -b -w -p -d --new-file --exclude-from=/Users/rstory/.rcfiles/diff-ignore SVN/include/net-snmp/data_access/swinst.h APPLE/include/net-snmp/data_access/swinst.h
--- SVN/include/net-snmp/data_access/swinst.h
+++ APPLE/include/net-snmp/data_access/swinst.h
@@ -0,0 +1,67 @@
+#ifndef NETSNMP_SWINST_H
+#define NETSNMP_SWINST_H
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+    /*
+     * Data structure for a swinst entry 
+     */
+    typedef struct hrSWInstalledTable_entry {
+        netsnmp_index   oid_index;
+        
+        /*
+         * Index values; MIB type is int32, but we use oid so this
+         * structure can be used directly with a table_container.
+         */
+        oid             swIndex;
+        
+        /*
+         * Column values 
+         */
+        char            swName[64];
+        char            swDate[11];
+#ifdef NETSNMP_HAVE_SWID
+        oid            *swID;
+        u_char          swID_len;
+#endif
+        u_char          swType;
+        u_char          swName_len;
+        u_char          swDate_len;
+    } netsnmp_swinst_entry;
+    
+#define HRSWINSTALLEDTYPE_UNKNOWN  1
+#define HRSWINSTALLEDTYPE_OPERATINGSYSTEM  2
+#define HRSWINSTALLEDTYPE_DEVICEDRIVER  3
+#define HRSWINSTALLEDTYPE_APPLICATION  4
+
+
+#define NETSNMP_SWINST_NOFLAGS            0x00000000
+
+#define NETSNMP_SWINST_ALL_OR_NONE        0x00000001
+#define NETSNMP_SWINST_DONT_FREE_ITEMS    0x00000002
+
+    netsnmp_container *
+    netsnmp_swinst_container_load(netsnmp_container *container, int flags );
+
+    void netsnmp_swinst_container_free(netsnmp_container *container,
+                                       u_int flags);
+    void netsnmp_swinst_container_free_items(netsnmp_container *container);
+
+    void netsnmp_swinst_entry_remove(netsnmp_container * container,
+                                     netsnmp_swinst_entry *entry);
+
+    netsnmp_swinst_entry * netsnmp_swinst_entry_create(int32_t index);
+    void netsnmp_swinst_entry_free(netsnmp_swinst_entry *entry);
+
+    int32_t netsnmp_swinst_add_name(const char *name);
+    int32_t netsnmp_swinst_get_id(const char *name);
+    const char * netsnmp_swinst_get_name(int32_t id);
+
+#ifdef  __cplusplus
+}
+#endif
+
+
+#endif /* NETSNMP_SWINST_H */