X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Frrd_daemon.c;h=40785b00778cc19ffc40b3e97bc687f553f5b4a9;hb=59525f3de81d5f6a9c5e5fc3a6662fa7e074163f;hp=28912c5358f8d17214b34d7c681fe8bcf714b69b;hpb=3c3effad5f54f8c73260068e3ec32c56684243bb;p=rrdtool.git diff --git a/src/rrd_daemon.c b/src/rrd_daemon.c index 28912c5..40785b0 100644 --- a/src/rrd_daemon.c +++ b/src/rrd_daemon.c @@ -286,7 +286,7 @@ static void install_signal_handlers(void) /* {{{ */ } /* }}} void install_signal_handlers */ -static int open_pidfile(void) /* {{{ */ +static int open_pidfile(char *action, int oflag) /* {{{ */ { int fd; char *file; @@ -295,14 +295,52 @@ static int open_pidfile(void) /* {{{ */ ? config_pid_file : LOCALSTATEDIR "/run/rrdcached.pid"; - fd = open(file, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IRGRP|S_IROTH); + fd = open(file, oflag, S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH); if (fd < 0) - fprintf(stderr, "FATAL: cannot create '%s' (%s)\n", - file, rrd_strerror(errno)); + fprintf(stderr, "rrdcached: can't %s pid file '%s' (%s)\n", + action, file, rrd_strerror(errno)); return(fd); } /* }}} static int open_pidfile */ +/* check existing pid file to see whether a daemon is running */ +static int check_pidfile(void) +{ + int pid_fd; + pid_t pid; + char pid_str[16]; + + pid_fd = open_pidfile("open", O_RDWR); + if (pid_fd < 0) + return pid_fd; + + if (read(pid_fd, pid_str, sizeof(pid_str)) <= 0) + return -1; + + pid = atoi(pid_str); + if (pid <= 0) + return -1; + + /* another running process that we can signal COULD be + * a competing rrdcached */ + if (pid != getpid() && kill(pid, 0) == 0) + { + fprintf(stderr, + "FATAL: Another rrdcached daemon is running?? (pid %d)\n", pid); + close(pid_fd); + return -1; + } + + lseek(pid_fd, 0, SEEK_SET); + ftruncate(pid_fd, 0); + + fprintf(stderr, + "rrdcached: removed stale PID file (no rrdcached on pid %d)\n" + "rrdcached: starting normally.\n", pid); + + return pid_fd; +} /* }}} static int check_pidfile */ + static int write_pidfile (int fd) /* {{{ */ { pid_t pid; @@ -748,8 +786,8 @@ static void *queue_thread_main (void *args __attribute__((unused))) /* {{{ */ flush_old_values (config_write_interval); /* Determine the time of the next cache flush. */ - while (next_flush.tv_sec <= now.tv_sec) - next_flush.tv_sec += config_flush_interval; + next_flush.tv_sec = + now.tv_sec + next_flush.tv_sec % config_flush_interval; /* unlock the cache while we rotate so we don't block incoming * updates if the fsync() blocks on disk I/O */ @@ -956,6 +994,26 @@ err: return 0; } /* }}} static int check_file_access */ +/* when using a base dir, convert relative paths to absolute paths. + * if necessary, modifies the "filename" pointer to point + * to the new path created in "tmp". "tmp" is provided + * by the caller and sizeof(tmp) must be >= PATH_MAX. + * + * this allows us to optimize for the expected case (absolute path) + * with a no-op. + */ +static void get_abs_path(char **filename, char *tmp) +{ + assert(tmp != NULL); + assert(filename != NULL && *filename != NULL); + + if (config_base_dir == NULL || **filename == '/') + return; + + snprintf(tmp, PATH_MAX, "%s/%s", config_base_dir, *filename); + *filename = tmp; +} /* }}} static int get_abs_path */ + /* returns 1 if we have the required privilege level, * otherwise issue an error to the user on sock */ static int has_privilege (listen_socket_t *sock, /* {{{ */ @@ -1183,7 +1241,7 @@ static int handle_request_stats (listen_socket_t *sock) /* {{{ */ static int handle_request_flush (listen_socket_t *sock, /* {{{ */ char *buffer, size_t buffer_size) { - char *file; + char *file, file_tmp[PATH_MAX]; int status; status = buffer_get_field (&buffer, &buffer_size, &file); @@ -1197,6 +1255,7 @@ static int handle_request_flush (listen_socket_t *sock, /* {{{ */ stats_flush_received++; pthread_mutex_unlock(&stats_lock); + get_abs_path(&file, file_tmp); if (!check_file_access(file, sock)) return 0; status = flush_file (file); @@ -1244,7 +1303,7 @@ static int handle_request_pending(listen_socket_t *sock, /* {{{ */ char *buffer, size_t buffer_size) { int status; - char *file; + char *file, file_tmp[PATH_MAX]; cache_item_t *ci; status = buffer_get_field(&buffer, &buffer_size, &file); @@ -1256,6 +1315,8 @@ static int handle_request_pending(listen_socket_t *sock, /* {{{ */ if (status <= 0) return status; + get_abs_path(&file, file_tmp); + pthread_mutex_lock(&cache_lock); ci = g_tree_lookup(cache_tree, file); if (ci == NULL) @@ -1275,7 +1336,7 @@ static int handle_request_forget(listen_socket_t *sock, /* {{{ */ char *buffer, size_t buffer_size) { int status; - char *file; + char *file, file_tmp[PATH_MAX]; status = buffer_get_field(&buffer, &buffer_size, &file); if (status != 0) @@ -1286,6 +1347,7 @@ static int handle_request_forget(listen_socket_t *sock, /* {{{ */ if (status <= 0) return status; + get_abs_path(&file, file_tmp); if (!check_file_access(file, sock)) return 0; pthread_mutex_lock(&cache_lock); @@ -1311,7 +1373,7 @@ static int handle_request_update (listen_socket_t *sock, /* {{{ */ time_t now, char *buffer, size_t buffer_size) { - char *file; + char *file, file_tmp[PATH_MAX]; int values_num = 0; int bad_timestamps = 0; int status; @@ -1335,6 +1397,7 @@ static int handle_request_update (listen_socket_t *sock, /* {{{ */ stats_updates_received++; pthread_mutex_unlock(&stats_lock); + get_abs_path(&file, file_tmp); if (!check_file_access(file, sock)) return 0; pthread_mutex_lock (&cache_lock); @@ -1462,6 +1525,9 @@ static int handle_request_update (listen_socket_t *sock, /* {{{ */ if (values_num < 1) { + /* journal replay mode */ + if (sock == NULL) return RESP_ERR; + /* if we had only one update attempt, then return the full error message... try to get the most information out of the limited error space allowed by the protocol @@ -2293,13 +2359,16 @@ static void *listen_thread_main (void *args __attribute__((unused))) /* {{{ */ static int daemonize (void) /* {{{ */ { int status; - int fd; + int pid_fd; char *base_dir; daemon_uid = geteuid(); - fd = open_pidfile(); - if (fd < 0) return fd; + pid_fd = open_pidfile("create", O_CREAT|O_EXCL|O_WRONLY); + if (pid_fd < 0) + pid_fd = check_pidfile(); + if (pid_fd < 0) + return pid_fd; if (!stay_foreground) { @@ -2352,7 +2421,7 @@ static int daemonize (void) /* {{{ */ return (-1); } - status = write_pidfile (fd); + status = write_pidfile (pid_fd); return status; } /* }}} int daemonize */ @@ -2468,6 +2537,7 @@ static int read_options (int argc, char **argv) /* {{{ */ case 'b': { size_t len; + char base_realpath[PATH_MAX]; if (config_base_dir != NULL) free (config_base_dir); @@ -2478,6 +2548,27 @@ static int read_options (int argc, char **argv) /* {{{ */ return (3); } + /* make sure that the base directory is not resolved via + * symbolic links. this makes some performance-enhancing + * assumptions possible (we don't have to resolve paths + * that start with a "/") + */ + if (realpath(config_base_dir, base_realpath) == NULL) + { + fprintf (stderr, "Invalid base directory '%s'.\n", config_base_dir); + return 5; + } + else if (strncmp(config_base_dir, + base_realpath, sizeof(base_realpath)) != 0) + { + fprintf(stderr, + "Base directory (-b) resolved via file system links!\n" + "Please consult rrdcached '-b' documentation!\n" + "Consider specifying the real directory (%s)\n", + base_realpath); + return 5; + } + len = strlen (config_base_dir); while ((len > 0) && (config_base_dir[len - 1] == '/')) {