]> git.ipfire.org Git - thirdparty/rrdtool-1.x.git/commitdiff
src/rrd_daemon.c,src/rrd_list.c: moves LIST-specific code into rrd_list_r
authorMarek Schimara <Marek.Schimara@bull.net>
Tue, 30 Aug 2016 15:06:28 +0000 (17:06 +0200)
committerMarek Schimara <Marek.Schimara@bull.net>
Wed, 31 Aug 2016 13:42:39 +0000 (15:42 +0200)
src/rrd.h
src/rrd_daemon.c
src/rrd_list.c

index 1b54377006fced0ee78e03a1b973743182830ec7..92b2d1fc3a77174cb8877b430f1e0958b12c8a03 100644 (file)
--- a/src/rrd.h
+++ b/src/rrd.h
@@ -166,6 +166,7 @@ struct rrd_t;
     void      rrd_info_free(
     rrd_info_t *);
     char      *rrd_list(int, char **);
+    char      *rrd_list_r(char *dirname);
     int       rrd_update(
     int,
     char **);
index f1d98def2cd4939e12a9d5ea86f15957f7570a1b..db548e78f413f9cebd6ecbe30715ddfc3329ed47 100644 (file)
 #include <libgen.h>
 #include <grp.h>
 #include <pwd.h>
-#include <glob.h>
 
 #ifdef HAVE_LIBWRAP
 #include <tcpd.h>
@@ -2389,14 +2388,13 @@ done:
 static int handle_request_list (HANDLER_PROTO) /* {{{ */
 {
   char *filename;
-  char fullpath[PATH_MAX];
+  char *list, *start_ptr, *end_ptr, *ptr;
+  char fullpath[PATH_MAX], current[PATH_MAX], absolute[PATH_MAX];
   char bwc[PATH_MAX], bwd[PATH_MAX];
-  char *base = &config_base_dir[0], *current = &fullpath[0];
+  char *base = &config_base_dir[0];
   struct stat sc, sd;
   ssize_t len;
   int status;
-  DIR *dir;
-  struct dirent *entry;
 
   if (config_base_dir == NULL) {
     return send_response(sock, RESP_ERR, "No base directory defined\n");
@@ -2412,56 +2410,11 @@ static int handle_request_list (HANDLER_PROTO) /* {{{ */
   snprintf(fullpath, PATH_MAX, "%s%s%s",
           config_base_dir, (filename[0] == '/') ? "" : "/", filename);
 
-  /* Prevent moving up the directory tree */
-  if (strstr(fullpath, ".."))
-      send_response(sock, RESP_ERR, "%s\n", rrd_strerror(EACCES));
-
-  /* if filename contains wildcards, then use glob() */
-  if (strchr(fullpath, '*') || strchr(fullpath, '?')) {
-    glob_t buf;
-    unsigned int i;
-
-    if (glob(fullpath, 0, NULL, &buf)) {
-      globfree(&buf);
-      goto out_send_response;
-    }
-
-    for (i = 0; i < buf.gl_pathc; i++) {
-      char *ptr;
-
-      if (!check_file_access(buf.gl_pathv[i], sock))
-       return send_response(sock, RESP_ERR,
-                            "Cannot read: %s\n", buf.gl_pathv[i]);
-
-      ptr = strrchr(buf.gl_pathv[i], '/');
-
-      if (ptr != NULL)
-       add_response_info(sock, "%s\n", ptr + 1);
-    }
-
-    globfree(&buf);
-    goto out_send_response;
-  }
-
-  if (!check_file_access(fullpath, sock))
+  if (!check_file_access(fullpath, sock)) {
     return send_response(sock, RESP_ERR, "Cannot read: %s\n", fullpath);
-
-  /* make sure we aren't following a symlink pointing outside of base_dir */
-  if (lstat(fullpath, &sc) == -1) {
-    return send_response(sock, RESP_ERR, "stat %s: %s\n",
-                        fullpath, rrd_strerror(errno));
-  }
-
-  if ((sc.st_mode & S_IFMT) == S_IFLNK) {
-    len = readlink(fullpath, bwc, sizeof(bwc) - 1);
-    if (len == -1) {
-      return send_response(sock, RESP_ERR, "readlink %s: %s\n",
-                          fullpath, rrd_strerror(errno));
-    }
-    bwc[len] = '\0';
-    current = &bwc[0];
   }
 
+  /* get real path of config_base_dir in case it's a symlink */
   if (lstat(config_base_dir, &sd) == -1) {
     return send_response(sock, RESP_ERR, "stat %s: %s\n",
                         config_base_dir, rrd_strerror(errno));
@@ -2477,44 +2430,89 @@ static int handle_request_list (HANDLER_PROTO) /* {{{ */
     base = &bwd[0];
   }
 
-  /* current path MUST be starting with base_dir */
-  if (memcmp(current, base, strlen(base)) != 0) {
-    return send_response(sock, RESP_ERR, "Permission denied\n");
+  list = rrd_list_r(fullpath);
+
+  if (list == NULL) {
+    /* Empty directory listing */
+    if (errno == 0) {
+      goto out_send_response;
+    }
+
+    return send_response(sock, RESP_ERR,
+                         "List %s: %s\n", fullpath, rrd_strerror(errno));
   }
 
-  /* If filename matches an RRD file, then return it */
-  if (strstr(current, ".rrd")) {
-         struct stat st;
-         char *ptr;
+  /* Check list items returned by rrd_list_r;
+   * the returned string is newline-separated: '%s\n%s\n...%s\n'
+   */
+  start_ptr = list;
+  end_ptr = list;
+
+  do {
+    end_ptr = strchr(start_ptr, '\n');
 
-         if (!stat(current, &st) && S_ISREG(st.st_mode)) {
-                 ptr = strrchr(current, '/');
+    if (end_ptr == NULL) {
+       end_ptr = start_ptr + strlen(start_ptr);
 
-                 if (ptr)
-                         add_response_info(sock, "%s\n", ptr + 1);
+       if (end_ptr == start_ptr) {
+               break;
+       }
          }
 
-         goto out_send_response;
+    if ((end_ptr - start_ptr + strlen(fullpath) + 1) >= PATH_MAX) {
+      /* Name too long: skip entry */
+      goto loop_next;
   }
+    strncpy(&current[0], start_ptr, (end_ptr - start_ptr));
+    current[end_ptr - start_ptr] = '\0';
+
+    /* if a single .rrd was asked for, absolute == fullpath  */
+    ptr = strstr(fullpath, ".rrd");
 
-  dir = opendir(current);
+    if (ptr != NULL && strlen(ptr) == 4) {
+      snprintf(&absolute[0], PATH_MAX, "%s", fullpath);
 
-  if (dir == NULL) {
+    } else {
+    snprintf(&absolute[0], PATH_MAX, "%s/%s", fullpath, current);
+    }
+
+    if (!check_file_access(absolute, sock)) {
+      /* Cannot access: skip entry */
+      goto loop_next;
+    }
+
+    /* Make sure we aren't following a symlink pointing outside of base_dir */
+    if (lstat(absolute, &sc) == -1) {
+      free(list);
     return send_response(sock, RESP_ERR,
-                        "Failed to open directory %s : %s\n",
-                        current, strerror(errno));
+                          "stat %s: %s\n", absolute, rrd_strerror(errno));
   }
 
-  while ((entry = readdir(dir)) != NULL) {
+    if ((sc.st_mode & S_IFMT) == S_IFLNK) {
+      len = readlink(absolute, bwc, sizeof(bwc) - 1);
 
-    if ((strcmp(entry->d_name, ".") == 0) ||
-       (strcmp(entry->d_name, "..") == 0)) {
-      continue;
+      if (len == -1) {
+       free(list);
+        return send_response(sock, RESP_ERR, "readlink %s: %s\n",
+                            absolute, rrd_strerror(errno));
+      }
+      bwc[len] = '\0';
+      strncpy(&absolute[0], bwc, PATH_MAX - 1);
+      absolute[PATH_MAX - 1] = '\0';
     }
 
-    add_response_info(sock, "%s\n", entry->d_name);
+    /* Absolute path MUST be starting with base_dir; if not skip the entry. */
+    if (memcmp(absolute, base, strlen(base)) != 0) {
+      goto loop_next;
   }
-  closedir(dir);
+    add_response_info(sock, "%s\n", current);
+
+loop_next:
+    start_ptr = end_ptr + 1;
+
+  } while (start_ptr != '\0');
+
+  free(list);
 
 out_send_response:
   send_response(sock, RESP_OK, "RRDs\n");
index 402157ed5ea1d02bacf43226591c5595620c99bc..75325d76204277dff5e271fd6fe7b3e4cde3ecb4 100644 (file)
@@ -1,5 +1,9 @@
 
 #include <stdio.h>
+#include <string.h>
+#include <glob.h>
+#include <dirent.h>
+#include <sys/types.h>
 
 #include "rrd_tool.h"
 #include "rrd_client.h"
@@ -9,8 +13,145 @@ char *rrd_list(int argc, char **argv);
 
 char *rrd_list_r(char *dirname)
 {
-       printf("rrd_list_r not implemented yet\n");
-       return NULL;
+#define SANE_ASPRINTF(_dest_str, _format, _params...)                  \
+       if (asprintf(&_dest_str, _format, _params) == -1) {             \
+               if (out != NULL) {                                      \
+                       free(out);                                      \
+               }                                                       \
+               errno = ENOMEM;                                         \
+               return NULL;                                            \
+       }
+#define SANE_ASPRINTF2(_dest_str, _format, _params...)                 \
+       if (asprintf(&_dest_str, _format, _params) == -1) {             \
+               if (out != NULL) {                                      \
+                       free(out);                                      \
+               }                                                       \
+               closedir(dir);                                          \
+               errno = ENOMEM;                                         \
+               return NULL;                                            \
+       }
+
+       char *out = NULL, *tmp;
+       char current[PATH_MAX];
+       glob_t buf;
+       char *ptr;
+       unsigned int i;
+       struct stat st;
+       DIR *dir;
+       struct dirent *entry;
+
+       /* Prevent moving up the directory tree */
+       if (strstr(dirname, "..")) {
+               errno = EACCES;
+               return NULL;
+       }
+
+       /* if filename contains wildcards, then use glob() */
+       if (strchr(dirname, '*') || strchr(dirname, '?')) {
+
+               if (glob(dirname, 0, NULL, &buf)) {
+                       globfree(&buf);
+                       errno = ENOENT;
+                       return NULL;
+               }
+
+               for (i = 0; i < buf.gl_pathc; i++) {
+                       ptr = strrchr(buf.gl_pathv[i], '/');
+
+                       if (ptr == NULL) {
+                               continue;
+                       }
+
+                       if (out == NULL) {
+                               SANE_ASPRINTF(out, "%s\n", ptr + 1);
+
+                       } else {
+                               tmp = out;
+                               SANE_ASPRINTF(out, "%s%s\n", out, ptr + 1);
+                               free(tmp);
+                       }
+               }
+               globfree(&buf);
+
+               if (out == NULL) {
+                       errno = ENOENT;
+               }
+               return out;
+       }
+
+       /* If 'dirname' matches an RRD file, then return it.
+        * strlen() used to make sure it's matching the end of string.
+        */
+       ptr = strstr(dirname, ".rrd");
+
+       if (ptr != NULL && strlen(ptr) == 4) {
+
+               if (!stat(dirname, &st) && S_ISREG(st.st_mode)) {
+                       ptr = strrchr(dirname, '/');
+
+                       if (ptr) {
+                               SANE_ASPRINTF(out, "%s\n", ptr + 1);
+                       }
+               }
+               return out;
+       }
+
+       /* Process directory */
+       dir = opendir(dirname);
+
+       if (dir == NULL) {
+               /* opendir sets errno */
+               return NULL;
+       }
+
+       while ((entry = readdir(dir)) != NULL) {
+
+               if ((strcmp(entry->d_name, ".") == 0) ||
+                   (strcmp(entry->d_name, "..") == 0)) {
+                       continue;
+               }
+
+               if (strlen(dirname) + strlen(entry->d_name) + 1 >= PATH_MAX) {
+                       continue;
+               }
+               snprintf(&current[0], PATH_MAX, "%s/%s", dirname, entry->d_name);
+
+               /* Only return directories and rrd files.
+                * NOTE: stat(2) follows symlinks and gives info on target. */
+               if (stat(current, &st) != 0) {
+                       continue;
+               }
+
+               if (!S_ISDIR(st.st_mode)) {
+
+                       if (S_ISREG(st.st_mode)) {
+
+                               ptr = strstr(entry->d_name, ".rrd");
+
+                               /* strlen() used to make sure it's matching
+                                * the end of string.
+                                */
+                               if (ptr == NULL || strlen(ptr) != 4) {
+                                       continue;
+                               }
+
+                       } else {
+                               continue;
+                       }
+               }
+
+               if (out == NULL) {
+                       SANE_ASPRINTF2(out, "%s\n", entry->d_name);
+
+               } else {
+                       tmp = out;
+                       SANE_ASPRINTF2(out, "%s%s\n", out, entry->d_name);
+                       free(tmp);
+               }
+       }
+       closedir(dir);
+
+       return out;
 }
 
 char *rrd_list(int argc, char **argv)
@@ -110,6 +251,10 @@ char *rrd_list(int argc, char **argv)
                        fprintf(stderr, "\n");
                }
                list = rrd_list_r(argv[options.optind]);
+
+               if (list == NULL) {
+                       fprintf(stderr, "%s", strerror(errno));
+               }
        }
 
        if (opt_daemon != NULL) {