} /* }}} void install_signal_handlers */
-static int open_pidfile(void) /* {{{ */
+static int open_pidfile(char *action, int oflag) /* {{{ */
{
int fd;
char *file;
? 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;
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, /* {{{ */
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);
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);
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);
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)
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)
if (status <= 0)
return status;
+ get_abs_path(&file, file_tmp);
if (!check_file_access(file, sock)) return 0;
pthread_mutex_lock(&cache_lock);
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;
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);
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
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)
{
return (-1);
}
- status = write_pidfile (fd);
+ status = write_pidfile (pid_fd);
return status;
} /* }}} int daemonize */
case 'b':
{
size_t len;
+ char base_realpath[PATH_MAX];
if (config_base_dir != NULL)
free (config_base_dir);
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] == '/'))
{