264 lines
8.1 KiB
C
264 lines
8.1 KiB
C
/* 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
|
|
#if HAVE_DIRENT_H
|
|
# include <dirent.h>
|
|
# define NAMLEN(dirent) strlen((dirent)->d_name)
|
|
#else
|
|
# define dirent direct
|
|
# define NAMLEN(dirent) (dirent)->d_namlen
|
|
#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 */
|