net-snmp 5.7
dir_utils.c
00001 /* Portions of this file are subject to the following copyright(s).  See
00002  * the Net-SNMP's COPYING file for more details and other copyrights
00003  * that may apply:
00004  */
00005 /*
00006  * Portions of this file are copyrighted by:
00007  * Copyright (C) 2007 Apple, Inc. All rights reserved.
00008  * Use is subject to license terms specified in the COPYING file
00009  * distributed with the Net-SNMP package.
00010  */
00011 #include <net-snmp/net-snmp-config.h>
00012 #include <net-snmp/net-snmp-features.h>
00013 #include <net-snmp/net-snmp-includes.h>
00014 
00015 #include <stdio.h>
00016 #include <ctype.h>
00017 #if HAVE_STDLIB_H
00018 #   include <stdlib.h>
00019 #endif
00020 #if HAVE_UNISTD_H
00021 #   include <unistd.h>
00022 #endif
00023 #if HAVE_STRING_H
00024 #   include <string.h>
00025 #else
00026 #  include <strings.h>
00027 #endif
00028 
00029 #include <sys/types.h>
00030 #if HAVE_LIMITS_H
00031 #include <limits.h>
00032 #endif
00033 #if HAVE_SYS_STAT_H
00034 #include <sys/stat.h>
00035 #endif
00036 #ifdef HAVE_DIRENT_H
00037 #include <dirent.h>
00038 #endif
00039 
00040 #include <errno.h>
00041 
00042 #if HAVE_DMALLOC_H
00043 #  include <dmalloc.h>
00044 #endif
00045 
00046 #include <net-snmp/types.h>
00047 #include <net-snmp/library/container.h>
00048 #include <net-snmp/library/file_utils.h>
00049 #include <net-snmp/library/dir_utils.h>
00050 
00051 netsnmp_feature_child_of(container_directory, container_types)
00052 #ifdef NETSNMP_FEATURE_REQUIRE_CONTAINER_DIRECTORY
00053 netsnmp_feature_require(file_utils)
00054 netsnmp_feature_require(container_free_all)
00055 #endif /* NETSNMP_FEATURE_REQUIRE_CONTAINER_DIRECTORY */
00056 
00057 #ifndef NETSNMP_FEATURE_REMOVE_CONTAINER_DIRECTORY
00058 static int
00059 _insert_nsfile( netsnmp_container *c, const char *name, struct stat *stats,
00060                 u_int flags);
00061 
00062 /*
00063  * read file names in a directory, with an optional filter
00064  */
00065 netsnmp_container *
00066 netsnmp_directory_container_read_some(netsnmp_container *user_container,
00067                                       const char *dirname,
00068                                       netsnmp_directory_filter *filter,
00069                                       void *filter_ctx, u_int flags)
00070 {
00071     DIR               *dir;
00072     netsnmp_container *container = user_container, *tmp_c;
00073     struct dirent     *file;
00074     char               path[SNMP_MAXPATH];
00075     size_t             dirname_len;
00076     int                rc;
00077     struct stat        statbuf;
00078     netsnmp_file       ns_file_tmp;
00079 
00080     if ((flags & NETSNMP_DIR_RELATIVE_PATH) && (flags & NETSNMP_DIR_RECURSE)) {
00081         DEBUGMSGTL(("directory:container",
00082                     "no support for relative path with recursion\n"));
00083         return NULL;
00084     }
00085 
00086     DEBUGMSGTL(("directory:container", "reading %s\n", dirname));
00087 
00088     /*
00089      * create the container, if needed
00090      */
00091     if (NULL == container) {
00092         if (flags & NETSNMP_DIR_NSFILE) {
00093             container = netsnmp_container_find("nsfile_directory_container:"
00094                                                "binary_array");
00095             if (container) {
00096                 container->compare = (netsnmp_container_compare*)
00097                     netsnmp_file_compare_name;
00098                 container->free_item = (netsnmp_container_obj_func *)
00099                     netsnmp_file_container_free;
00100             }
00101         }
00102         else
00103             container = netsnmp_container_find("directory_container:cstring");
00104         if (NULL == container)
00105             return NULL;
00106         container->container_name = strdup(dirname);
00108         if (! (flags & NETSNMP_DIR_SORTED))
00109             CONTAINER_SET_OPTIONS(container, CONTAINER_KEY_UNSORTED, rc);
00110     }
00111 
00112     dir = opendir(dirname);
00113     if (NULL == dir) {
00114         DEBUGMSGTL(("directory:container", "  not a dir\n"));
00115         if (container != user_container)
00116             netsnmp_directory_container_free(container);
00117         return NULL;
00118     }
00119 
00121     if (flags & NETSNMP_DIR_RELATIVE_PATH)
00122         dirname_len = 0;
00123     else {
00124         dirname_len = strlen(dirname);
00125         strncpy(path, dirname, sizeof(path));
00126         if ((dirname_len + 2) > sizeof(path)) {
00128             closedir(dir);
00129             if (container != user_container)
00130                 netsnmp_directory_container_free(container);
00131             return NULL;
00132         }
00133         path[dirname_len] = '/';
00134         path[++dirname_len] = 0;
00135     }
00136 
00138     while ((file = readdir(dir))) {
00139 
00140         if ((file->d_name == NULL) || (file->d_name[0] == 0))
00141             continue;
00142 
00144         if ((file->d_name[0] == '.') &&
00145             ((file->d_name[1] == 0) ||
00146              ((file->d_name[1] == '.') && ((file->d_name[2] == 0)))))
00147             continue;
00148 
00149         strncpy(&path[dirname_len], file->d_name, sizeof(path) - dirname_len);
00150         if (NULL != filter) {
00151             if (flags & NETSNMP_DIR_NSFILE_STATS) {
00153                 if (stat(path, &statbuf) != 0) {
00154                     snmp_log(LOG_ERR, "could not stat %s\n", file->d_name);
00155                     break;
00156                 }
00157                 ns_file_tmp.stats = &statbuf;
00158                 ns_file_tmp.name = path;
00159                 rc = (*filter)(&ns_file_tmp, filter_ctx);
00160             }
00161             else
00162                 rc = (*filter)(path, filter_ctx);
00163             if (0 == rc) {
00164                 DEBUGMSGTL(("directory:container:filtered", "%s\n",
00165                             file->d_name));
00166                 continue;
00167             }
00168         }
00169 
00170         DEBUGMSGTL(("directory:container:found", "%s\n", path));
00171         if ((flags & NETSNMP_DIR_RECURSE) 
00172 #if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(DT_DIR)
00173             && (file->d_type == DT_DIR)
00174 #elif defined(S_ISDIR)
00175             && (stat(file->d_name, &statbuf) != 0) && (S_ISDIR(statbuf.st_mode))
00176 #endif
00177             ) {
00179             tmp_c = netsnmp_directory_container_read(container, path, flags);
00180         }
00181         else if (flags & NETSNMP_DIR_NSFILE) {
00182             if (_insert_nsfile( container, file->d_name,
00183                                 filter ? &statbuf : NULL, flags ) < 0)
00184                 break;
00185         }
00186         else {
00187             char *dup = strdup(path);
00188             if (NULL == dup) {
00189                 snmp_log(LOG_ERR,
00190                          "strdup failed while building directory container\n");
00191                 break;
00192             }
00193             rc = CONTAINER_INSERT(container, dup);
00194             if (-1 == rc ) {
00195                 DEBUGMSGTL(("directory:container", "  err adding %s\n", path));
00196                 free(dup);
00197             }
00198         }
00199     } /* while */
00200 
00201     closedir(dir);
00202 
00203     rc = CONTAINER_SIZE(container);
00204     DEBUGMSGTL(("directory:container", "  container now has %d items\n", rc));
00205     if ((0 == rc) && !(flags & NETSNMP_DIR_EMPTY_OK)) {
00206         netsnmp_directory_container_free(container);
00207         return NULL;
00208     }
00209     
00210     return container;
00211 }
00212 
00213 void
00214 netsnmp_directory_container_free(netsnmp_container *container)
00215 {
00216     CONTAINER_FREE_ALL(container, NULL);
00217     CONTAINER_FREE(container);
00218 }
00219 
00220 static int
00221 _insert_nsfile( netsnmp_container *c, const char *name, struct stat *stats,
00222                 u_int flags)
00223 {
00224     int           rc;
00225     netsnmp_file *ns_file = netsnmp_file_new(name, 0, 0, 0);
00226     if (NULL == ns_file) {
00227         snmp_log(LOG_ERR, "error creating ns_file\n");
00228         return -1;
00229     }
00230 
00231     if (flags & NETSNMP_DIR_NSFILE_STATS) {
00232         ns_file->stats = (struct stat*)calloc(1,sizeof(*(ns_file->stats)));
00233         if (NULL == ns_file->stats) {
00234             snmp_log(LOG_ERR, "error creating stats for ns_file\n");
00235             netsnmp_file_release(ns_file);
00236             return -1;
00237         }
00238     
00240         if (stats)
00241             memcpy(ns_file->stats, stats, sizeof(*stats));
00242         else
00243             stat(ns_file->name, ns_file->stats);
00244     }
00245 
00246     rc = CONTAINER_INSERT(c, ns_file);
00247     if (-1 == rc ) {
00248         DEBUGMSGTL(("directory:container", "  err adding %s\n", name));
00249         netsnmp_file_release(ns_file);
00250     }
00251 
00252     return 0;
00253 }
00254 #else  /* NETSNMP_FEATURE_REMOVE_CONTAINER_DIRECTORY */
00255 netsnmp_feature_unused(container_directory);
00256 #endif /* NETSNMP_FEATURE_REMOVE_CONTAINER_DIRECTORY */