#include #include #include "rrd_config.h" #ifdef _MSC_VER #include "win32-glob.h" /* from https://sourceforge.net/projects/sox/ */ #include "dirent.h" /* from https://github.com/tronkko/dirent */ #include "asprintf.h" /* from http://asprintf.insanecoding.org */ #else #if defined(__MINGW32__) && !defined(HAVE_GLOB_H) /* MinGW has glob.h, MinGW-w64 not (yet?) */ #include "win32/win32-glob.h" /* from https://sourceforge.net/projects/sox/ */ #else #include #endif #include #endif #include #include "rrd_tool.h" #include "rrd_client.h" static char *move_past_prefix(const char *prefix, const char *string) { int index = 0; if (strlen(prefix) > strlen(string)) { return (char *)string; } while (prefix[index] != '\0') { if (prefix[index] != string[index]) { break; } index++; } return (char *)&(string[index]); } static char *rrd_list_rec(int recursive, char *root, char *dirname) { #define SANE_ASPRINTF2(_dest_str, _format, ...) \ if (asprintf(&_dest_str, _format, __VA_ARGS__) == -1) { \ if (out != NULL) { \ free(out); \ } \ closedir(dir); \ errno = ENOMEM; \ return NULL; \ } struct stat st; struct dirent *entry; DIR *dir; char *out = NULL, *out_rec, *out_short, *tmp, *ptr; char current[PATH_MAX], fullpath[PATH_MAX]; 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); /* NOTE: stat(2) follows symlinks and gives info on target. */ if (stat(current, &st) != 0) { continue; } if (S_ISDIR(st.st_mode) && recursive) { snprintf(&fullpath[0], PATH_MAX, "%s/%s", dirname, entry->d_name); out_rec = rrd_list_rec(recursive, root, fullpath); if (out_rec == NULL) { continue; } if (out == NULL) { SANE_ASPRINTF2(out, "%s", out_rec); } else { tmp = out; SANE_ASPRINTF2(out, "%s%s", out, out_rec); free(tmp); } free(out_rec); } else { if (S_ISREG(st.st_mode)) { ptr = strstr(entry->d_name, ".rrd"); /* strlen() used to make sure it's matching * the end of string. Non-rrd-suffixed regular * files are skipped. */ if (ptr == NULL || strlen(ptr) != 4) { continue; } } snprintf(&fullpath[0], PATH_MAX, "%s/%s", dirname, entry->d_name); out_short = move_past_prefix(root, fullpath); /* don't start output with a '/' */ if (out_short[0] == '/') { out_short++; } if (out == NULL) { SANE_ASPRINTF2(out, "%s\n", out_short); } else { tmp = out; SANE_ASPRINTF2(out, "%s%s\n", out, out_short); free(tmp); } } } closedir(dir); errno = 0; return out; } char *rrd_list_r(int recursive, char *dirname) { #define SANE_ASPRINTF(_dest_str, _format, ...) \ if (asprintf(&_dest_str, _format, __VA_ARGS__) == -1) { \ if (out != NULL) { \ free(out); \ } \ errno = ENOMEM; \ return NULL; \ } char *out = NULL, *tmp; glob_t buf; char *ptr; unsigned int i; struct stat st; /* 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, '?')) { /* recursive list + globbing forbidden */ if (recursive) { errno = EINVAL; return NULL; } 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)) { return NULL; } if (!S_ISREG(st.st_mode)) { /* Same errno as if you try to open() a named socket. * This is less misleading than e.g. EISDIR */ errno = ENXIO; return NULL; } ptr = strrchr(dirname, '/'); if (ptr) { SANE_ASPRINTF(out, "%s\n", ptr + 1); } else { errno = EINVAL; } return out; } /* Process directory */ if (stat(dirname, &st) != 0) { return NULL; } if (!S_ISDIR(st.st_mode)) { errno = ENOTDIR; return NULL; } return rrd_list_rec(recursive, dirname, dirname); } char *rrd_list(int argc, char **argv) { char *opt_daemon = NULL; int status; int flushfirst = 1; int recursive = 0; char *list; static struct optparse_long long_options[] = { {"daemon", 'd', OPTPARSE_REQUIRED}, {"noflush", 'F', OPTPARSE_NONE}, {"recursive", 'r', OPTPARSE_NONE}, {0}, }; struct optparse options; int opt; optparse_init(&options, argc, argv); while ((opt = optparse_long(&options, long_options, NULL)) != -1) { switch (opt) { case 'd': if (opt_daemon != NULL) { free (opt_daemon); } opt_daemon = strdup (options.optarg); if (opt_daemon == NULL) { rrd_set_error ("strdup failed."); return NULL; } break; case 'F': flushfirst = 0; break; case 'r': recursive=1; break; case '?': if (opt_daemon) free(opt_daemon); rrd_set_error("%s", options.errmsg); return NULL; break; default: rrd_set_error ("Usage: rrdtool %s [--daemon [--noflush]] ", argv[0]); if (opt_daemon != NULL) { free (opt_daemon); } return NULL; break; } } if ((argc - options.optind) != 1) { rrd_set_error ("Usage: rrdtool %s [--daemon [--noflush]] [--recursive] ", argv[0]); if (opt_daemon != NULL) { free (opt_daemon); } return NULL; } if( flushfirst ) { status = rrdc_flushall_if_daemon(opt_daemon); if (status) { if (opt_daemon != NULL) { free (opt_daemon); } return NULL; } } rrdc_connect (opt_daemon); if (rrdc_is_connected (opt_daemon)) { list = rrdc_list(recursive, argv[options.optind]); rrdc_disconnect(); } else { if (opt_daemon) { fprintf(stderr, "Error connecting to rrdcached"); if (rrd_test_error()) fprintf(stderr, ": %s", rrd_get_error()); fprintf(stderr, "\n"); free(opt_daemon); return NULL; } list = rrd_list_r(recursive, argv[options.optind]); if (list == NULL) { fprintf(stderr, "%s", strerror(errno)); } } if (opt_daemon != NULL) { free(opt_daemon); } return list; }