X-Git-Url: https://git.octo.it/?p=rrdtool.git;a=blobdiff_plain;f=src%2Frrd_daemon.c;h=fa7dc41bac4cf62837d2517f3a66f707ad5ff44b;hp=199ebdb13c1b0071cec8a55bc6db8f438442c703;hb=faf5ffaa9e488e6cf185a64a51fd6e9fd35a3267;hpb=9d1a64da41154f4d2f6c803c572f6f7d2684d732 diff --git a/src/rrd_daemon.c b/src/rrd_daemon.c index 199ebdb..fa7dc41 100644 --- a/src/rrd_daemon.c +++ b/src/rrd_daemon.c @@ -1,7 +1,7 @@ /** * RRDTool - src/rrd_daemon.c - * Copyright (C) 2008 Florian octo Forster - * Copyright (C) 2008 Kevin Brintnall + * Copyright (C) 2008,2009 Florian octo Forster + * Copyright (C) 2008,2009 Kevin Brintnall * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -77,11 +77,13 @@ #include #ifndef WIN32 -#include +#ifdef HAVE_STDINT_H +# include +#endif #include #include #include -# include +#include #else @@ -103,6 +105,7 @@ #include #include #include +#include #include /* }}} */ @@ -116,12 +119,6 @@ /* * Types */ -typedef enum -{ - PRIV_LOW, - PRIV_HIGH -} socket_privilege; - typedef enum { RESP_ERR = -1, RESP_OK = 0 } response_code; struct listen_socket_s @@ -129,7 +126,6 @@ struct listen_socket_s int fd; char addr[PATH_MAX + 1]; int family; - socket_privilege privilege; /* state for BATCH processing */ time_t batch_start; @@ -142,23 +138,25 @@ struct listen_socket_s char *wbuf; ssize_t wbuf_len; + + uint32_t permissions; }; typedef struct listen_socket_s listen_socket_t; -struct command; +struct command_s; +typedef struct command_s command_t; /* note: guard against "unused" warnings in the handlers */ #define DISPATCH_PROTO listen_socket_t *sock __attribute__((unused)),\ time_t now __attribute__((unused)),\ char *buffer __attribute__((unused)),\ size_t buffer_size __attribute__((unused)) -#define HANDLER_PROTO struct command *cmd __attribute__((unused)),\ +#define HANDLER_PROTO command_t *cmd __attribute__((unused)),\ DISPATCH_PROTO -struct command { +struct command_s { char *cmd; int (*handler)(HANDLER_PROTO); - socket_privilege min_priv; char context; /* where we expect to see it */ #define CMD_CONTEXT_CLIENT (1<<0) @@ -351,12 +349,32 @@ static void install_signal_handlers(void) /* {{{ */ static int open_pidfile(char *action, int oflag) /* {{{ */ { int fd; - char *file; + const char *file; + char *file_copy, *dir; file = (config_pid_file != NULL) ? config_pid_file : LOCALSTATEDIR "/run/rrdcached.pid"; + /* dirname may modify its argument */ + file_copy = strdup(file); + if (file_copy == NULL) + { + fprintf(stderr, "rrdcached: strdup(): %s\n", + rrd_strerror(errno)); + return -1; + } + + dir = dirname(file_copy); + if (rrd_mkdir_p(dir, 0777) != 0) + { + fprintf(stderr, "Failed to create pidfile directory '%s': %s\n", + dir, rrd_strerror(errno)); + return -1; + } + + free(file_copy); + fd = open(file, oflag, S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH); if (fd < 0) fprintf(stderr, "rrdcached: can't %s pid file '%s' (%s)\n", @@ -479,7 +497,7 @@ static char *next_cmd (listen_socket_t *sock, ssize_t *len) /* {{{ */ /* NOTREACHED */ assert(1==0); -} +} /* }}} char *next_cmd */ /* add the characters directly to the write buffer */ static int add_to_wbuf(listen_socket_t *sock, char *str, size_t len) /* {{{ */ @@ -1068,20 +1086,6 @@ static void get_abs_path(char **filename, char *tmp) *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, /* {{{ */ - socket_privilege priv) -{ - if (sock == NULL) /* journal replay */ - return 1; - - if (sock->privilege >= priv) - return 1; - - return send_response(sock, RESP_ERR, "%s\n", rrd_strerror(EACCES)); -} /* }}} static int has_privilege */ - static int flush_file (const char *filename) /* {{{ */ { cache_item_t *ci; @@ -1110,7 +1114,7 @@ static int flush_file (const char *filename) /* {{{ */ return (0); } /* }}} int flush_file */ -static int syntax_error(listen_socket_t *sock, struct command *cmd) /* {{{ */ +static int syntax_error(listen_socket_t *sock, command_t *cmd) /* {{{ */ { char *err = "Syntax error.\n"; @@ -1513,11 +1517,10 @@ static int handle_request_quit (HANDLER_PROTO) /* {{{ */ return -1; } /* }}} static int handle_request_quit */ -struct command COMMANDS[] = { +static command_t list_of_commands[] = { /* {{{ */ { "UPDATE", handle_request_update, - PRIV_HIGH, CMD_CONTEXT_ANY, "UPDATE [ ...]\n" , @@ -1532,7 +1535,6 @@ struct command COMMANDS[] = { { "WROTE", handle_request_wrote, - PRIV_HIGH, CMD_CONTEXT_JOURNAL, NULL, NULL @@ -1540,7 +1542,6 @@ struct command COMMANDS[] = { { "FLUSH", handle_request_flush, - PRIV_LOW, CMD_CONTEXT_CLIENT | CMD_CONTEXT_BATCH, "FLUSH \n" , @@ -1550,7 +1551,6 @@ struct command COMMANDS[] = { { "FLUSHALL", handle_request_flushall, - PRIV_HIGH, CMD_CONTEXT_CLIENT, "FLUSHALL\n" , @@ -1559,7 +1559,6 @@ struct command COMMANDS[] = { { "PENDING", handle_request_pending, - PRIV_HIGH, CMD_CONTEXT_CLIENT, "PENDING \n" , @@ -1569,7 +1568,6 @@ struct command COMMANDS[] = { { "FORGET", handle_request_forget, - PRIV_HIGH, CMD_CONTEXT_ANY, "FORGET \n" , @@ -1579,7 +1577,6 @@ struct command COMMANDS[] = { { "QUEUE", handle_request_queue, - PRIV_LOW, CMD_CONTEXT_CLIENT, "QUEUE\n" , @@ -1592,7 +1589,6 @@ struct command COMMANDS[] = { { "STATS", handle_request_stats, - PRIV_LOW, CMD_CONTEXT_CLIENT, "STATS\n" , @@ -1602,7 +1598,6 @@ struct command COMMANDS[] = { { "HELP", handle_request_help, - PRIV_LOW, CMD_CONTEXT_CLIENT, "HELP []\n", NULL, /* special! */ @@ -1610,7 +1605,6 @@ struct command COMMANDS[] = { { "BATCH", batch_start, - PRIV_LOW, CMD_CONTEXT_CLIENT, "BATCH\n" , @@ -1634,7 +1628,6 @@ struct command COMMANDS[] = { { ".", /* BATCH terminator */ batch_done, - PRIV_LOW, CMD_CONTEXT_BATCH, NULL, NULL @@ -1642,34 +1635,79 @@ struct command COMMANDS[] = { { "QUIT", handle_request_quit, - PRIV_LOW, CMD_CONTEXT_CLIENT | CMD_CONTEXT_BATCH, "QUIT\n" , "Disconnect from rrdcached.\n" - }, - {NULL,NULL,0,0,NULL,NULL} /* LAST ENTRY */ -}; + } +}; /* }}} command_t list_of_commands[] */ +static size_t list_of_commands_len = sizeof (list_of_commands) + / sizeof (list_of_commands[0]); -static struct command *find_command(char *cmd) +static command_t *find_command(char *cmd) { - struct command *c = COMMANDS; - - while (c->cmd != NULL) - { - if (strcasecmp(cmd, c->cmd) == 0) - break; - c++; - } + size_t i; - if (c->cmd == NULL) - return NULL; - else - return c; + for (i = 0; i < list_of_commands_len; i++) + if (strcasecmp(cmd, list_of_commands[i].cmd) == 0) + return (&list_of_commands[i]); + return NULL; } +/* We currently use the index in the `list_of_commands' array as a bit position + * in `listen_socket_t.permissions'. This member schould NEVER be accessed from + * outside these functions so that switching to a more elegant storage method + * is easily possible. */ +static ssize_t find_command_index (const char *cmd) /* {{{ */ +{ + size_t i; + + for (i = 0; i < list_of_commands_len; i++) + if (strcasecmp(cmd, list_of_commands[i].cmd) == 0) + return ((ssize_t) i); + return (-1); +} /* }}} ssize_t find_command_index */ + +static int socket_permission_check (listen_socket_t *sock, /* {{{ */ + const char *cmd) +{ + ssize_t i; + + if (cmd == NULL) + return (-1); + + if ((strcasecmp ("QUIT", cmd) == 0) + || (strcasecmp ("HELP", cmd) == 0)) + return (1); + else if (strcmp (".", cmd) == 0) + cmd = "BATCH"; + + i = find_command_index (cmd); + if (i < 0) + return (-1); + assert (i < 32); + + if ((sock->permissions & (1 << i)) != 0) + return (1); + return (0); +} /* }}} int socket_permission_check */ + +static int socket_permission_add (listen_socket_t *sock, /* {{{ */ + const char *cmd) +{ + ssize_t i; + + i = find_command_index (cmd); + if (i < 0) + return (-1); + assert (i < 32); + + sock->permissions |= (1 << i); + return (0); +} /* }}} int socket_permission_add */ + /* check whether commands are received in the expected context */ -static int command_check_context(listen_socket_t *sock, struct command *cmd) +static int command_check_context(listen_socket_t *sock, command_t *cmd) { if (sock == NULL) return (cmd->context & CMD_CONTEXT_JOURNAL); @@ -1687,7 +1725,7 @@ static int handle_request_help (HANDLER_PROTO) /* {{{ */ int status; char *cmd_str; char *resp_txt; - struct command *help = NULL; + command_t *help = NULL; status = buffer_get_field (&buffer, &buffer_size, &cmd_str); if (status == 0) @@ -1708,14 +1746,15 @@ static int handle_request_help (HANDLER_PROTO) /* {{{ */ } else { - help = COMMANDS; + size_t i; + resp_txt = "Command overview\n"; - while (help->cmd) + for (i = 0; i < list_of_commands_len; i++) { - if (help->syntax) - add_response_info(sock, "%s", help->syntax); - help++; + if (list_of_commands[i].syntax == NULL) + continue; + add_response_info (sock, "%s", list_of_commands[i].syntax); } } @@ -1727,7 +1766,7 @@ static int handle_request (DISPATCH_PROTO) /* {{{ */ { char *buffer_ptr = buffer; char *cmd_str = NULL; - struct command *cmd = NULL; + command_t *cmd = NULL; int status; assert (buffer[buffer_size - 1] == '\0'); @@ -1746,9 +1785,8 @@ static int handle_request (DISPATCH_PROTO) /* {{{ */ if (!cmd) return send_response(sock, RESP_ERR, "Unknown command: %s\n", cmd_str); - status = has_privilege(sock, cmd->min_priv); - if (status <= 0) - return status; + if (!socket_permission_check (sock, cmd->cmd)) + return send_response(sock, RESP_ERR, "Permission denied.\n"); if (!command_check_context(sock, cmd)) return send_response(sock, RESP_ERR, "Can't use '%s' here.\n", cmd_str); @@ -2222,11 +2260,31 @@ static int open_listen_socket_unix (const listen_socket_t *sock) /* {{{ */ listen_socket_t *temp; int status; const char *path; + char *path_copy, *dir; path = sock->addr; if (strncmp(path, "unix:", strlen("unix:")) == 0) path += strlen("unix:"); + /* dirname may modify its argument */ + path_copy = strdup(path); + if (path_copy == NULL) + { + fprintf(stderr, "rrdcached: strdup(): %s\n", + rrd_strerror(errno)); + return (-1); + } + + dir = dirname(path_copy); + if (rrd_mkdir_p(dir, 0777) != 0) + { + fprintf(stderr, "Failed to create socket directory '%s': %s\n", + dir, rrd_strerror(errno)); + return (-1); + } + + free(path_copy); + temp = (listen_socket_t *) rrd_realloc (listen_fds, sizeof (listen_fds[0]) * (listen_fds_num + 1)); if (temp == NULL) @@ -2660,7 +2718,6 @@ static int cleanup (void) /* {{{ */ free(queue_threads); free(config_base_dir); - free(config_pid_file); pthread_mutex_lock(&cache_lock); g_tree_destroy(cache_tree); @@ -2672,6 +2729,7 @@ static int cleanup (void) /* {{{ */ closelog (); remove_pidfile (); + free(config_pid_file); return (0); } /* }}} int cleanup */ @@ -2681,7 +2739,10 @@ static int read_options (int argc, char **argv) /* {{{ */ int option; int status = 0; - while ((option = getopt(argc, argv, "gl:L:f:w:z:t:Bb:p:Fj:h?")) != -1) + char **permissions = NULL; + size_t permissions_len = 0; + + while ((option = getopt(argc, argv, "gl:P:f:w:z:t:Bb:p:Fj:h?")) != -1) { switch (option) { @@ -2689,7 +2750,6 @@ static int read_options (int argc, char **argv) /* {{{ */ stay_foreground=1; break; - case 'L': case 'l': { listen_socket_t *new; @@ -2703,7 +2763,40 @@ static int read_options (int argc, char **argv) /* {{{ */ memset(new, 0, sizeof(listen_socket_t)); strncpy(new->addr, optarg, sizeof(new->addr)-1); - new->privilege = (option == 'l') ? PRIV_HIGH : PRIV_LOW; + + /* Add permissions to the socket {{{ */ + if (permissions_len != 0) + { + size_t i; + for (i = 0; i < permissions_len; i++) + { + status = socket_permission_add (new, permissions[i]); + if (status != 0) + { + fprintf (stderr, "read_options: Adding permission \"%s\" to " + "socket failed. Most likely, this permission doesn't " + "exist. Check your command line.\n", permissions[i]); + status = 4; + } + } + } + else /* if (permissions_len == 0) */ + { + /* Add permission for ALL commands to the socket. */ + size_t i; + for (i = 0; i < list_of_commands_len; i++) + { + status = socket_permission_add (new, list_of_commands[i].cmd); + if (status != 0) + { + fprintf (stderr, "read_options: Adding permission \"%s\" to " + "socket failed. This should never happen, ever! Sorry.\n", + permissions[i]); + status = 4; + } + } + } + /* }}} Done adding permissions. */ if (!rrd_add_ptr((void ***)&config_listen_address_list, &config_listen_address_list_len, new)) @@ -2714,6 +2807,28 @@ static int read_options (int argc, char **argv) /* {{{ */ } break; + case 'P': + { + char *optcopy; + char *saveptr; + char *dummy; + char *ptr; + + rrd_free_ptrs ((void *) &permissions, &permissions_len); + + optcopy = strdup (optarg); + dummy = optcopy; + saveptr = NULL; + while ((ptr = strtok_r (dummy, ", ", &saveptr)) != NULL) + { + dummy = NULL; + rrd_add_strdup ((void *) &permissions, &permissions_len, ptr); + } + + free (optcopy); + } + break; + case 'f': { int temp; @@ -2799,7 +2914,8 @@ static int read_options (int argc, char **argv) /* {{{ */ */ if (realpath(config_base_dir, base_realpath) == NULL) { - fprintf (stderr, "Invalid base directory '%s'.\n", config_base_dir); + fprintf (stderr, "Failed to canonicalize the base directory '%s': " + "%s\n", config_base_dir, rrd_strerror(errno)); return 5; } else if (strncmp(config_base_dir, @@ -2849,18 +2965,17 @@ static int read_options (int argc, char **argv) /* {{{ */ case 'j': { - struct stat statbuf; const char *dir = journal_dir = strdup(optarg); - status = stat(dir, &statbuf); + status = rrd_mkdir_p(dir, 0777); if (status != 0) { - fprintf(stderr, "Cannot stat '%s' : %s\n", dir, rrd_strerror(errno)); + fprintf(stderr, "Failed to create journal directory '%s': %s\n", + dir, rrd_strerror(errno)); return 6; } - if (!S_ISDIR(statbuf.st_mode) - || access(dir, R_OK|W_OK|X_OK) != 0) + if (access(dir, R_OK|W_OK|X_OK) != 0) { fprintf(stderr, "Must specify a writable directory with -j! (%s)\n", errno ? rrd_strerror(errno) : ""); @@ -2871,13 +2986,15 @@ static int read_options (int argc, char **argv) /* {{{ */ case 'h': case '?': - printf ("RRDCacheD %s Copyright (C) 2008 Florian octo Forster\n" + printf ("RRDCacheD %s\n" + "Copyright (C) 2008,2009 Florian octo Forster and Kevin Brintnall\n" "\n" "Usage: rrdcached [options]\n" "\n" "Valid options are:\n" " -l
Socket address to listen to.\n" - " -L
Socket address to listen to ('FLUSH' only).\n" + " -P Sets the permissions to assign to all following " + "sockets\n" " -w Interval in which to write data.\n" " -z Delay writes up to seconds to spread load\n" " -t Number of write threads.\n" @@ -2913,6 +3030,8 @@ static int read_options (int argc, char **argv) /* {{{ */ if (journal_dir == NULL) config_flush_at_shutdown = 1; + rrd_free_ptrs ((void *) &permissions, &permissions_len); + return (status); } /* }}} int read_options */