Blame agent/mibgroup/host/data_access/swinst_darwin.c

Packit fcad23
/*
Packit fcad23
 * swinst.c : hrSWInstalledTable data access
Packit fcad23
 */
Packit fcad23
/*
Packit fcad23
 * Copyright (C) 2007 Apple, Inc. All rights reserved.
Packit fcad23
 * Use is subject to license terms specified in the COPYING file
Packit fcad23
 * distributed with the Net-SNMP package.
Packit fcad23
 */
Packit fcad23
#include <net-snmp/net-snmp-config.h>
Packit fcad23
#include <net-snmp/net-snmp-features.h>
Packit fcad23
#include <net-snmp/net-snmp-includes.h>
Packit fcad23
#include <net-snmp/agent/net-snmp-agent-includes.h>
Packit fcad23
#include <net-snmp/library/container.h>
Packit fcad23
#include <net-snmp/library/dir_utils.h>
Packit fcad23
#include <net-snmp/library/snmp_debug.h>
Packit fcad23
#include <net-snmp/data_access/swinst.h>
Packit fcad23
#include "swinst_private.h"
Packit fcad23
Packit fcad23
#include <stdlib.h>
Packit fcad23
#include <unistd.h>
Packit fcad23
#include <dirent.h>
Packit fcad23
#include <sys/stat.h>
Packit fcad23
Packit fcad23
#define __APPLE_API_EVOLVING 1
Packit fcad23
#include <sys/acl.h> /* or else CoreFoundation.h barfs */
Packit fcad23
#undef __APPLE_API_EVOLVING 
Packit fcad23
Packit fcad23
#include <CoreFoundation/CoreFoundation.h>
Packit fcad23
#include <ApplicationServices/ApplicationServices.h>
Packit fcad23
Packit fcad23
netsnmp_feature_require(container_directory)
Packit fcad23
netsnmp_feature_require(date_n_time)
Packit fcad23
Packit fcad23
/* ---------------------------------------------------------------------
Packit fcad23
 */
Packit fcad23
static int _add_applications_in_dir(netsnmp_container *, const char* path);
Packit fcad23
static int32_t _index;
Packit fcad23
static int _check_bundled_app(CFURLRef currentURL, CFStringRef *name,
Packit fcad23
                              CFStringRef *info, const char* path);
Packit fcad23
static int _check_classic_app(CFURLRef currentURL, CFStringRef *name,
Packit fcad23
                              CFStringRef *info, const char* path);
Packit fcad23
static netsnmp_container *dirs = NULL;
Packit fcad23
Packit fcad23
/* ---------------------------------------------------------------------
Packit fcad23
 */
Packit fcad23
void
Packit fcad23
netsnmp_swinst_arch_init( void )
Packit fcad23
{
Packit fcad23
    struct stat stat_buf;
Packit fcad23
    const char *default_dirs[] = {
Packit fcad23
        "/Applications",
Packit fcad23
        "/Applications (Mac OS 9)",
Packit fcad23
        "/System/Library/CoreServices",
Packit fcad23
        "/System/Library/Extensions",
Packit fcad23
        "/System/Library/Services"
Packit fcad23
#ifdef TEST
Packit fcad23
        , "/Developer/Applications"
Packit fcad23
        , "/Volumes/audX/Applications (Mac OS 9)"
Packit fcad23
#endif
Packit fcad23
    };
Packit fcad23
    int i, count = sizeof(default_dirs)/sizeof(default_dirs[0]);
Packit fcad23
Packit fcad23
    /*
Packit fcad23
     * create the container, if needed
Packit fcad23
     */
Packit fcad23
    if (NULL == dirs) {
Packit fcad23
        dirs = netsnmp_container_find("directory_container:cstring");
Packit fcad23
        if (NULL == dirs) {
Packit fcad23
            snmp_log(LOG_ERR, "couldn't allocate container for dir list\n");
Packit fcad23
            return;
Packit fcad23
        }
Packit fcad23
        dirs->container_name = strdup("directory search list");
Packit fcad23
        netsnmp_binary_array_options_set(dirs, 1, CONTAINER_KEY_UNSORTED);
Packit fcad23
    }
Packit fcad23
Packit fcad23
    /*
Packit fcad23
     * add dirs
Packit fcad23
     */
Packit fcad23
    for(i = 0; i < count; ++i) {
Packit fcad23
        char *      tmp;
Packit fcad23
        /** xxx: get/save the last mod date? */
Packit fcad23
        if(-1 == stat(default_dirs[i], &stat_buf)) {
Packit fcad23
            DEBUGMSGTL(("swinst:arch:darwin", "skipping dir %s\n",
Packit fcad23
                        default_dirs[i]));
Packit fcad23
            continue;
Packit fcad23
        }
Packit fcad23
        DEBUGMSGTL(("swinst:arch:darwin", "adding dir %s\n",
Packit fcad23
                        default_dirs[i]));
Packit fcad23
        tmp = strdup(default_dirs[i]);
Packit fcad23
        if (NULL == tmp) {
Packit fcad23
            snmp_log(LOG_ERR,"strdup failed\n");
Packit fcad23
            break;
Packit fcad23
        }
Packit fcad23
        CONTAINER_INSERT(dirs, tmp);
Packit fcad23
    }
Packit fcad23
}
Packit fcad23
Packit fcad23
void
Packit fcad23
netsnmp_swinst_arch_shutdown( void )
Packit fcad23
{
Packit fcad23
    netsnmp_directory_container_free(dirs);
Packit fcad23
}
Packit fcad23
Packit fcad23
/* ---------------------------------------------------------------------
Packit fcad23
 */
Packit fcad23
Packit fcad23
int
Packit fcad23
netsnmp_swinst_arch_load( netsnmp_container *container, u_int flags )
Packit fcad23
{
Packit fcad23
    netsnmp_iterator   *it;
Packit fcad23
    const char         *dir;
Packit fcad23
    int                 rc;
Packit fcad23
Packit fcad23
    DEBUGMSGTL(("swinst:arch:darwin", "load\n"));
Packit fcad23
Packit fcad23
    if (NULL == dirs) {
Packit fcad23
        DEBUGMSGTL(("swinst:arch:darwin", "no dirs to scan!\n"));
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    _index = 0;
Packit fcad23
    
Packit fcad23
    it = CONTAINER_ITERATOR(dirs);
Packit fcad23
    for (dir = ITERATOR_FIRST(it); dir; dir = ITERATOR_NEXT(it)) {
Packit fcad23
        rc = _add_applications_in_dir(container, dir);
Packit fcad23
    }
Packit fcad23
    ITERATOR_RELEASE(it);
Packit fcad23
    DEBUGMSGTL(("swinst:arch:darwin", "loaded %d apps\n",_index));
Packit fcad23
Packit fcad23
    return 0;
Packit fcad23
}
Packit fcad23
Packit fcad23
static void
Packit fcad23
_dump_flags(u_long flags)
Packit fcad23
{
Packit fcad23
    static struct {
Packit fcad23
        const char*name;
Packit fcad23
        u_long bits;
Packit fcad23
    } names[] = {
Packit fcad23
        { "kLSItemInfoIsPlainFile", 0x00000001 },
Packit fcad23
        { "kLSItemInfoIsPackage", 0x00000002 },
Packit fcad23
        { "kLSItemInfoIsApplication", 0x00000004 },
Packit fcad23
        { "kLSItemInfoIsContainer", 0x00000008 },
Packit fcad23
        { "kLSItemInfoIsAliasFile", 0x00000010 },
Packit fcad23
        { "kLSItemInfoIsSymlink", 0x00000020 },
Packit fcad23
        { "kLSItemInfoIsInvisible", 0x00000040 },
Packit fcad23
        { "kLSItemInfoIsNativeApp", 0x00000080 },
Packit fcad23
        { "kLSItemInfoIsClassicApp", 0x00000100 },
Packit fcad23
        { "kLSItemInfoAppPrefersNative", 0x00000200 },
Packit fcad23
        { "kLSItemInfoAppPrefersClassic", 0x00000400 },
Packit fcad23
        { "kLSItemInfoAppIsScriptable", 0x00000800 },
Packit fcad23
        { "kLSItemInfoIsVolume", 0x00001000 },
Packit fcad23
        { "kLSItemInfoExtensionIsHidden", 0x00100000 }
Packit fcad23
    };
Packit fcad23
    int i, count = sizeof(names)/sizeof(names[0]);
Packit fcad23
Packit fcad23
    for(i = 0; i < count; ++i) {
Packit fcad23
        if (flags & names[i].bits) {
Packit fcad23
            DEBUGMSGTL(("swinst:arch:darwin:flags", "\t%s\n",
Packit fcad23
                       names[i].name));
Packit fcad23
        }
Packit fcad23
    }
Packit fcad23
}
Packit fcad23
Packit fcad23
static int
Packit fcad23
_add_applications_in_dir(netsnmp_container *container, const char* path)
Packit fcad23
{
Packit fcad23
    netsnmp_container  *files;
Packit fcad23
    netsnmp_iterator   *it;
Packit fcad23
    const char         *file;
Packit fcad23
    netsnmp_swinst_entry *entry = NULL;
Packit fcad23
    struct stat	        stat_buf;
Packit fcad23
    size_t              date_len;
Packit fcad23
    u_char             *date_buf;
Packit fcad23
    int                 rc = 0;
Packit fcad23
Packit fcad23
    CFStringRef         currentPath = NULL;
Packit fcad23
    CFURLRef            currentURL = NULL;
Packit fcad23
    LSItemInfoRecord    itemInfoRecord;
Packit fcad23
    CFStringRef         prodName = NULL;
Packit fcad23
    CFStringRef         version = NULL;
Packit fcad23
    
Packit fcad23
    DEBUGMSGTL(("swinst:arch:darwin", " adding files from %s\n", path));
Packit fcad23
    files = netsnmp_directory_container_read(NULL, path, 0);
Packit fcad23
    if (NULL == files) {
Packit fcad23
        snmp_log(LOG_ERR, "swinst: could not read directory %s\n", path);
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    it = CONTAINER_ITERATOR(files);
Packit fcad23
    if (NULL == it) {
Packit fcad23
        snmp_log(LOG_ERR, "could not get iterator\n");
Packit fcad23
        netsnmp_directory_container_free(files);
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
    for (file = ITERATOR_FIRST(it);
Packit fcad23
         file;
Packit fcad23
         file = ITERATOR_NEXT(it),
Packit fcad23
             CFRelease(currentPath),
Packit fcad23
             CFRelease(currentURL)) {
Packit fcad23
Packit fcad23
        int                 rc2 = 0;
Packit fcad23
        
Packit fcad23
        currentPath =
Packit fcad23
            CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, file,
Packit fcad23
                                            kCFStringEncodingUTF8,
Packit fcad23
                                            kCFAllocatorNull);
Packit fcad23
        currentURL =
Packit fcad23
            CFURLCreateWithFileSystemPath(kCFAllocatorDefault, currentPath,
Packit fcad23
                                          kCFURLPOSIXPathStyle, true); 
Packit fcad23
        LSCopyItemInfoForURL(currentURL,
Packit fcad23
                             kLSRequestBasicFlagsOnly|kLSRequestAppTypeFlags,
Packit fcad23
                             &itemInfoRecord); 
Packit fcad23
        if((0 == itemInfoRecord.flags) ||
Packit fcad23
           (kLSItemInfoIsPlainFile == itemInfoRecord.flags) ||
Packit fcad23
           (itemInfoRecord.flags & kLSItemInfoIsInvisible) ||
Packit fcad23
           (itemInfoRecord.flags & kLSItemInfoIsAliasFile)) {
Packit fcad23
            continue;
Packit fcad23
        }
Packit fcad23
        /** recurse on non-application containers (i.e. directory) */
Packit fcad23
        if ((itemInfoRecord.flags & kLSItemInfoIsContainer) &&
Packit fcad23
            (!(itemInfoRecord.flags & kLSItemInfoIsApplication))) {
Packit fcad23
            netsnmp_directory_container_read(files, file, 0);
Packit fcad23
            continue;
Packit fcad23
       }
Packit fcad23
Packit fcad23
        /** skip any other non-application files */
Packit fcad23
        if (!(itemInfoRecord.flags & kLSItemInfoIsApplication)) {
Packit fcad23
            continue;
Packit fcad23
       }
Packit fcad23
Packit fcad23
        if ((itemInfoRecord.flags & kLSItemInfoIsPackage) ||           
Packit fcad23
            (itemInfoRecord.flags & kLSItemInfoIsContainer)) {
Packit fcad23
            rc2 = _check_bundled_app(currentURL, &prodName, &version, file);
Packit fcad23
        } 
Packit fcad23
        else if ((itemInfoRecord.flags & kLSItemInfoIsClassicApp) ||
Packit fcad23
                 (itemInfoRecord.flags & kLSItemInfoIsPlainFile)) {
Packit fcad23
            rc2 = _check_classic_app(currentURL, &prodName, &version, file);
Packit fcad23
        } else {
Packit fcad23
            snmp_log(LOG_ERR,"swinst shouldn't get here: %s\n", file);
Packit fcad23
            _dump_flags(itemInfoRecord.flags);
Packit fcad23
            continue;
Packit fcad23
        }
Packit fcad23
        if (rc2) { /* not an app. if directory, recurse; else continue */
Packit fcad23
            _dump_flags(itemInfoRecord.flags);
Packit fcad23
            if (1 == rc2)
Packit fcad23
                netsnmp_directory_container_read(files, file, 0);
Packit fcad23
            continue;
Packit fcad23
        }
Packit fcad23
        
Packit fcad23
        /*
Packit fcad23
         * allocate entry
Packit fcad23
         */
Packit fcad23
        entry = netsnmp_swinst_entry_create(++_index);
Packit fcad23
        if (NULL == entry) {
Packit fcad23
            snmp_log(LOG_ERR, "error creating swinst entry\n");
Packit fcad23
            rc = -1;
Packit fcad23
            SNMP_CFRelease(prodName);
Packit fcad23
            SNMP_CFRelease(version);
Packit fcad23
            CFRelease(currentPath);
Packit fcad23
            CFRelease(currentURL);
Packit fcad23
            break;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        entry->swName_len =
Packit fcad23
            snprintf(entry->swName, sizeof(entry->swName),
Packit fcad23
                     "%s %s", CFStringGetCStringPtr(prodName,0),
Packit fcad23
                     CFStringGetCStringPtr(version,0));
Packit fcad23
	if (entry->swName_len >= sizeof(entry->swName))
Packit fcad23
	    entry->swName_len = sizeof(entry->swName)-1;
Packit fcad23
Packit fcad23
        DEBUGMSGTL(("swinst:arch:darwin", "\t%s %s\n", file, entry->swName));
Packit fcad23
Packit fcad23
        /** get the last mod date */
Packit fcad23
        if(stat(file, &stat_buf) != -1) {
Packit fcad23
            date_buf = date_n_time(&stat_buf.st_mtime, &date_len);
Packit fcad23
            entry->swDate_len = date_len;
Packit fcad23
            memcpy(entry->swDate, date_buf, entry->swDate_len);
Packit fcad23
        }
Packit fcad23
        
Packit fcad23
        CONTAINER_INSERT(container, entry);
Packit fcad23
        entry = NULL;
Packit fcad23
        SNMP_CFRelease(prodName);
Packit fcad23
        SNMP_CFRelease(version);
Packit fcad23
    }
Packit fcad23
    ITERATOR_RELEASE(it);
Packit fcad23
    netsnmp_directory_container_free(files);
Packit fcad23
Packit fcad23
    return rc;
Packit fcad23
}
Packit fcad23
Packit fcad23
static int
Packit fcad23
_check_bundled_app(CFURLRef currentURL, CFStringRef *prodName,
Packit fcad23
                   CFStringRef *version, const char* file)
Packit fcad23
{
Packit fcad23
    CFBundleRef         theBundle = NULL;
Packit fcad23
    CFDictionaryRef     infoDict = NULL;
Packit fcad23
            
Packit fcad23
    if ((NULL == prodName) || (NULL == version))
Packit fcad23
       return -1;
Packit fcad23
Packit fcad23
    theBundle = CFBundleCreate (kCFAllocatorDefault, currentURL);
Packit fcad23
    if(theBundle == NULL)
Packit fcad23
        return -1; /* not a bundle */
Packit fcad23
Packit fcad23
    infoDict = CFBundleGetInfoDictionary(theBundle);
Packit fcad23
    if(0 == CFDictionaryGetCount(infoDict)) {
Packit fcad23
        SNMP_CFRelease(theBundle);
Packit fcad23
        return 1; /* directory */
Packit fcad23
    }
Packit fcad23
Packit fcad23
    *prodName = (CFStringRef)
Packit fcad23
        CFDictionaryGetValue (infoDict, CFSTR("CFBundleName"));
Packit fcad23
    if (NULL == *prodName) {
Packit fcad23
        *prodName = (CFStringRef)
Packit fcad23
            CFDictionaryGetValue (infoDict, CFSTR("CFBundleDisplayName"));
Packit fcad23
        if (NULL == *prodName) {
Packit fcad23
            *prodName = (CFStringRef) CFDictionaryGetValue (infoDict,
Packit fcad23
                                      CFSTR("CFBundleExecutable"));
Packit fcad23
        }
Packit fcad23
    }
Packit fcad23
    if(NULL == *prodName) {
Packit fcad23
        DEBUGMSGTL(("swinst:arch:darwin", "\tmissing name: %s\n",file));
Packit fcad23
        /*CFShow(infoDict);*/
Packit fcad23
        SNMP_CFRelease(theBundle);
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    *version = (CFStringRef)
Packit fcad23
        CFDictionaryGetValue (infoDict, CFSTR("CFBundleShortVersionString"));
Packit fcad23
    if(NULL == *version) {
Packit fcad23
        *version = (CFStringRef)
Packit fcad23
            CFDictionaryGetValue (infoDict, CFSTR("CFBundleVersion"));
Packit fcad23
        if (*version == NULL) 
Packit fcad23
            *version = (CFStringRef) CFDictionaryGetValue (infoDict,
Packit fcad23
                                      CFSTR("CFBundleGetInfoString"));
Packit fcad23
    }
Packit fcad23
    if(NULL == *version) {
Packit fcad23
        DEBUGMSGTL(("swinst:arch:darwin", "\tmissing version: %s\n",file));
Packit fcad23
        /*CFShow(infoDict);*/
Packit fcad23
        SNMP_CFRelease(theBundle);
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
    
Packit fcad23
    if(theBundle != NULL) {
Packit fcad23
        CFRetain(*prodName);
Packit fcad23
        CFRetain(*version);
Packit fcad23
        SNMP_CFRelease(theBundle);
Packit fcad23
    }
Packit fcad23
    return 0;
Packit fcad23
}
Packit fcad23
Packit fcad23
static int
Packit fcad23
_check_classic_app(CFURLRef currentURL, CFStringRef *prodName,
Packit fcad23
                   CFStringRef *version, const char* file)
Packit fcad23
{
Packit fcad23
    /*
Packit fcad23
     * get info for classic or single-file apps
Packit fcad23
     */
Packit fcad23
    FSRef theFSRef;
Packit fcad23
    int theResFile;
Packit fcad23
Packit fcad23
    if ((NULL == prodName) || (NULL == version))
Packit fcad23
       return -1;
Packit fcad23
Packit fcad23
    *prodName = CFURLCopyLastPathComponent(currentURL);
Packit fcad23
    if (! CFURLGetFSRef(currentURL, &theFSRef)) {
Packit fcad23
        DEBUGMSGTL(("swinst:arch:darwin", "GetFSRef failed: %s\n", file));
Packit fcad23
        SNMP_CFRelease(*prodName);
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
    theResFile = FSOpenResFile(&theFSRef, fsRdPerm);
Packit fcad23
    if (ResError() != noErr) {
Packit fcad23
        DEBUGMSGTL(("swinst:arch:darwin", "FSOpenResFile failed: %s\n", file));
Packit fcad23
        SNMP_CFRelease(*prodName);
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
    VersRecHndl versHandle = (VersRecHndl)Get1IndResource('vers', 1);
Packit fcad23
    if (versHandle != NULL) {
Packit fcad23
        *version = CFStringCreateWithPascalString(kCFAllocatorDefault,
Packit fcad23
                       (**versHandle).shortVersion, kCFStringEncodingMacRoman);
Packit fcad23
        if (*version == NULL) {
Packit fcad23
            StringPtr longVersionPtr = (**versHandle).shortVersion;
Packit fcad23
            longVersionPtr = (StringPtr)(((Ptr) longVersionPtr) +
Packit fcad23
                              1 + ((unsigned char) *longVersionPtr));
Packit fcad23
            *version = CFStringCreateWithPascalString(kCFAllocatorDefault,
Packit fcad23
                          longVersionPtr,  kCFStringEncodingMacRoman);
Packit fcad23
        }
Packit fcad23
        ReleaseResource((Handle)versHandle);
Packit fcad23
    }
Packit fcad23
    CloseResFile(theResFile);
Packit fcad23
    if(*version == NULL) {
Packit fcad23
        DEBUGMSGTL(("swinst:arch:darwin",
Packit fcad23
                    "\tmissing classic/file version: %s\n", file));
Packit fcad23
        SNMP_CFRelease(*prodName);
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    return 0;
Packit fcad23
}