#include <libgen.h>
#include <grp.h>
#include <pwd.h>
-#include <glob.h>
#ifdef HAVE_LIBWRAP
#include <tcpd.h>
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");
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));
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(¤t[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");
#include <stdio.h>
+#include <string.h>
+#include <glob.h>
+#include <dirent.h>
+#include <sys/types.h>
#include "rrd_tool.h"
#include "rrd_client.h"
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(¤t[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)
fprintf(stderr, "\n");
}
list = rrd_list_r(argv[options.optind]);
+
+ if (list == NULL) {
+ fprintf(stderr, "%s", strerror(errno));
+ }
}
if (opt_daemon != NULL) {