Blame snmplib/dir_utils.c

Packit fcad23
/* Portions of this file are subject to the following copyright(s).  See
Packit fcad23
 * the Net-SNMP's COPYING file for more details and other copyrights
Packit fcad23
 * that may apply:
Packit fcad23
 */
Packit fcad23
/*
Packit fcad23
 * Portions of this file are copyrighted by:
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
Packit fcad23
#include <stdio.h>
Packit fcad23
#include <ctype.h>
Packit fcad23
#if HAVE_STDLIB_H
Packit fcad23
#   include <stdlib.h>
Packit fcad23
#endif
Packit fcad23
#if HAVE_UNISTD_H
Packit fcad23
#   include <unistd.h>
Packit fcad23
#endif
Packit fcad23
#if HAVE_STRING_H
Packit fcad23
#   include <string.h>
Packit fcad23
#else
Packit fcad23
#  include <strings.h>
Packit fcad23
#endif
Packit fcad23
Packit fcad23
#include <sys/types.h>
Packit fcad23
#if HAVE_LIMITS_H
Packit fcad23
#include <limits.h>
Packit fcad23
#endif
Packit fcad23
#if HAVE_SYS_STAT_H
Packit fcad23
#include <sys/stat.h>
Packit fcad23
#endif
Packit fcad23
#if HAVE_DIRENT_H
Packit fcad23
# include <dirent.h>
Packit fcad23
# define NAMLEN(dirent) strlen((dirent)->d_name)
Packit fcad23
#else
Packit fcad23
# define dirent direct
Packit fcad23
# define NAMLEN(dirent) (dirent)->d_namlen
Packit fcad23
#endif
Packit fcad23
Packit fcad23
#include <errno.h>
Packit fcad23
Packit fcad23
#if HAVE_DMALLOC_H
Packit fcad23
#  include <dmalloc.h>
Packit fcad23
#endif
Packit fcad23
Packit fcad23
#include <net-snmp/types.h>
Packit fcad23
#include <net-snmp/library/container.h>
Packit fcad23
#include <net-snmp/library/file_utils.h>
Packit fcad23
#include <net-snmp/library/dir_utils.h>
Packit fcad23
Packit fcad23
netsnmp_feature_child_of(container_directory, container_types)
Packit fcad23
#ifdef NETSNMP_FEATURE_REQUIRE_CONTAINER_DIRECTORY
Packit fcad23
netsnmp_feature_require(file_utils)
Packit fcad23
netsnmp_feature_require(container_free_all)
Packit fcad23
#endif /* NETSNMP_FEATURE_REQUIRE_CONTAINER_DIRECTORY */
Packit fcad23
Packit fcad23
#ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_DIRECTORY
Packit fcad23
static int
Packit fcad23
_insert_nsfile( netsnmp_container *c, const char *name, struct stat *stats,
Packit fcad23
                u_int flags);
Packit fcad23
Packit fcad23
/*
Packit fcad23
 * read file names in a directory, with an optional filter
Packit fcad23
 */
Packit fcad23
netsnmp_container *
Packit fcad23
netsnmp_directory_container_read_some(netsnmp_container *user_container,
Packit fcad23
                                      const char *dirname,
Packit fcad23
                                      netsnmp_directory_filter *filter,
Packit fcad23
                                      void *filter_ctx, u_int flags)
Packit fcad23
{
Packit fcad23
    DIR               *dir;
Packit fcad23
    netsnmp_container *container = user_container;
Packit fcad23
    struct dirent     *file;
Packit fcad23
    char               path[SNMP_MAXPATH];
Packit fcad23
    size_t             dirname_len;
Packit fcad23
    int                rc;
Packit fcad23
    struct stat        statbuf;
Packit fcad23
    netsnmp_file       ns_file_tmp;
Packit fcad23
Packit fcad23
    if ((flags & NETSNMP_DIR_RELATIVE_PATH) && (flags & NETSNMP_DIR_RECURSE)) {
Packit fcad23
        DEBUGMSGTL(("directory:container",
Packit fcad23
                    "no support for relative path with recursion\n"));
Packit fcad23
        return NULL;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    DEBUGMSGTL(("directory:container", "reading %s\n", dirname));
Packit fcad23
Packit fcad23
    /*
Packit fcad23
     * create the container, if needed
Packit fcad23
     */
Packit fcad23
    if (NULL == container) {
Packit fcad23
        if (flags & NETSNMP_DIR_NSFILE) {
Packit fcad23
            container = netsnmp_container_find("nsfile_directory_container:"
Packit fcad23
                                               "binary_array");
Packit fcad23
            if (container) {
Packit fcad23
                container->compare = (netsnmp_container_compare*)
Packit fcad23
                    netsnmp_file_compare_name;
Packit fcad23
                container->free_item = (netsnmp_container_obj_func *)
Packit fcad23
                    netsnmp_file_container_free;
Packit fcad23
            }
Packit fcad23
        }
Packit fcad23
        else
Packit fcad23
            container = netsnmp_container_find("directory_container:cstring");
Packit fcad23
        if (NULL == container)
Packit fcad23
            return NULL;
Packit fcad23
        container->container_name = strdup(dirname);
Packit fcad23
        /** default to unsorted */
Packit fcad23
        if (! (flags & NETSNMP_DIR_SORTED))
Packit fcad23
            CONTAINER_SET_OPTIONS(container, CONTAINER_KEY_UNSORTED, rc);
Packit fcad23
    }
Packit fcad23
Packit fcad23
    dir = opendir(dirname);
Packit fcad23
    if (NULL == dir) {
Packit fcad23
        DEBUGMSGTL(("directory:container", "  not a dir\n"));
Packit fcad23
        if (container != user_container)
Packit fcad23
            netsnmp_directory_container_free(container);
Packit fcad23
        return NULL;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    /** copy dirname into path */
Packit fcad23
    if (flags & NETSNMP_DIR_RELATIVE_PATH)
Packit fcad23
        dirname_len = 0;
Packit fcad23
    else {
Packit fcad23
        dirname_len = strlen(dirname);
Packit fcad23
        strlcpy(path, dirname, sizeof(path));
Packit fcad23
        if ((dirname_len + 2) > sizeof(path)) {
Packit fcad23
            /** not enough room for files */
Packit fcad23
            closedir(dir);
Packit fcad23
            if (container != user_container)
Packit fcad23
                netsnmp_directory_container_free(container);
Packit fcad23
            return NULL;
Packit fcad23
        }
Packit fcad23
        path[dirname_len] = '/';
Packit fcad23
        path[++dirname_len] = '\0';
Packit fcad23
    }
Packit fcad23
Packit fcad23
    /** iterate over dir */
Packit fcad23
    while ((file = readdir(dir))) {
Packit fcad23
Packit fcad23
        if ((file->d_name == NULL) || (file->d_name[0] == 0))
Packit fcad23
            continue;
Packit fcad23
Packit fcad23
        /** skip '.' and '..' */
Packit fcad23
        if ((file->d_name[0] == '.') &&
Packit fcad23
            ((file->d_name[1] == 0) ||
Packit fcad23
             ((file->d_name[1] == '.') && ((file->d_name[2] == 0)))))
Packit fcad23
            continue;
Packit fcad23
Packit fcad23
        strlcpy(&path[dirname_len], file->d_name, sizeof(path) - dirname_len);
Packit fcad23
        if (NULL != filter) {
Packit fcad23
            if (flags & NETSNMP_DIR_NSFILE_STATS) {
Packit fcad23
                /** use local vars for now */
Packit fcad23
                if (stat(path, &statbuf) != 0) {
Packit fcad23
                    snmp_log(LOG_ERR, "could not stat %s\n", file->d_name);
Packit fcad23
                    break;
Packit fcad23
                }
Packit fcad23
                ns_file_tmp.stats = &statbuf;
Packit fcad23
                ns_file_tmp.name = path;
Packit fcad23
                rc = (*filter)(&ns_file_tmp, filter_ctx);
Packit fcad23
            }
Packit fcad23
            else
Packit fcad23
                rc = (*filter)(path, filter_ctx);
Packit fcad23
            if (0 == rc) {
Packit fcad23
                DEBUGMSGTL(("directory:container:filtered", "%s\n",
Packit fcad23
                            file->d_name));
Packit fcad23
                continue;
Packit fcad23
            }
Packit fcad23
        }
Packit fcad23
Packit fcad23
        DEBUGMSGTL(("directory:container:found", "%s\n", path));
Packit fcad23
        if ((flags & NETSNMP_DIR_RECURSE) 
Packit fcad23
#if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(DT_DIR)
Packit fcad23
            && (file->d_type == DT_DIR)
Packit fcad23
#elif defined(S_ISDIR)
Packit fcad23
            && (stat(file->d_name, &statbuf) != 0) && (S_ISDIR(statbuf.st_mode))
Packit fcad23
#endif
Packit fcad23
            ) {
Packit fcad23
            /** xxx add the dir as well? not for now.. maybe another flag? */
Packit fcad23
            netsnmp_directory_container_read(container, path, flags);
Packit fcad23
        }
Packit fcad23
        else if (flags & NETSNMP_DIR_NSFILE) {
Packit fcad23
            if (_insert_nsfile( container, file->d_name,
Packit fcad23
                                filter ? &statbuf : NULL, flags ) < 0)
Packit fcad23
                break;
Packit fcad23
        }
Packit fcad23
        else {
Packit fcad23
            char *dup = strdup(path);
Packit fcad23
            if (NULL == dup) {
Packit fcad23
                snmp_log(LOG_ERR,
Packit fcad23
                         "strdup failed while building directory container\n");
Packit fcad23
                break;
Packit fcad23
            }
Packit fcad23
            rc = CONTAINER_INSERT(container, dup);
Packit fcad23
            if (-1 == rc ) {
Packit fcad23
                DEBUGMSGTL(("directory:container", "  err adding %s\n", path));
Packit fcad23
                free(dup);
Packit fcad23
            }
Packit fcad23
        }
Packit fcad23
    } /* while */
Packit fcad23
Packit fcad23
    closedir(dir);
Packit fcad23
Packit fcad23
    rc = CONTAINER_SIZE(container);
Packit fcad23
    DEBUGMSGTL(("directory:container", "  container now has %d items\n", rc));
Packit fcad23
    if ((0 == rc) && !(flags & NETSNMP_DIR_EMPTY_OK)) {
Packit fcad23
        netsnmp_directory_container_free(container);
Packit fcad23
        return NULL;
Packit fcad23
    }
Packit fcad23
    
Packit fcad23
    return container;
Packit fcad23
}
Packit fcad23
Packit fcad23
void
Packit fcad23
netsnmp_directory_container_free(netsnmp_container *container)
Packit fcad23
{
Packit fcad23
    CONTAINER_FREE_ALL(container, NULL);
Packit fcad23
    CONTAINER_FREE(container);
Packit fcad23
}
Packit fcad23
Packit fcad23
static int
Packit fcad23
_insert_nsfile( netsnmp_container *c, const char *name, struct stat *stats,
Packit fcad23
                u_int flags)
Packit fcad23
{
Packit fcad23
    int           rc;
Packit fcad23
    netsnmp_file *ns_file = netsnmp_file_new(name, 0, 0, 0);
Packit fcad23
    if (NULL == ns_file) {
Packit fcad23
        snmp_log(LOG_ERR, "error creating ns_file\n");
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    if (flags & NETSNMP_DIR_NSFILE_STATS) {
Packit fcad23
        ns_file->stats = (struct stat*)calloc(1,sizeof(*(ns_file->stats)));
Packit fcad23
        if (NULL == ns_file->stats) {
Packit fcad23
            snmp_log(LOG_ERR, "error creating stats for ns_file\n");
Packit fcad23
            netsnmp_file_release(ns_file);
Packit fcad23
            return -1;
Packit fcad23
        }
Packit fcad23
    
Packit fcad23
        /** use stats from earlier if we have them */
Packit fcad23
        if (stats) {
Packit fcad23
            memcpy(ns_file->stats, stats, sizeof(*stats));
Packit fcad23
        } else if (stat(ns_file->name, ns_file->stats) < 0) {
Packit fcad23
            snmp_log(LOG_ERR, "stat() failed for ns_file\n");
Packit fcad23
            netsnmp_file_release(ns_file);
Packit fcad23
            return -1;
Packit fcad23
        }
Packit fcad23
    }
Packit fcad23
Packit fcad23
    rc = CONTAINER_INSERT(c, ns_file);
Packit fcad23
    if (-1 == rc ) {
Packit fcad23
        DEBUGMSGTL(("directory:container", "  err adding %s\n", name));
Packit fcad23
        netsnmp_file_release(ns_file);
Packit fcad23
    }
Packit fcad23
Packit fcad23
    return 0;
Packit fcad23
}
Packit fcad23
#else  /* NETSNMP_FEATURE_REMOVE_CONTAINER_DIRECTORY */
Packit fcad23
netsnmp_feature_unused(container_directory);
Packit fcad23
#endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_DIRECTORY */