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