X-Git-Url: https://git.octo.it/?p=rrdtool.git;a=blobdiff_plain;f=src%2Frrd_daemon.c;h=2c81424a34d0426d8680b751f13221b9837d268c;hp=22093205ba099b49bb1d98172c2fb84baffc2355;hb=b63a6268ac7c3668f6731c0a0972e4575c3f6dcf;hpb=896c97caca5fdc260fe5defd7952ad2741419432 diff --git a/src/rrd_daemon.c b/src/rrd_daemon.c index 2209320..2c81424 100644 --- a/src/rrd_daemon.c +++ b/src/rrd_daemon.c @@ -63,15 +63,8 @@ * Now for some includes.. */ /* {{{ */ -#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) && !defined(HAVE_CONFIG_H) -#include "../win32/config.h" -#else -#ifdef HAVE_CONFIG_H -#include "../rrd_config.h" -#endif -#endif -#include "rrd.h" +#include "rrd_tool.h" #include "rrd_client.h" #include "unused.h" @@ -109,6 +102,10 @@ #include #include +#ifdef HAVE_LIBWRAP +#include +#endif /* HAVE_LIBWRAP */ + #include /* }}} */ @@ -184,7 +181,7 @@ struct cache_item_s size_t values_num; /* number of valid pointers */ size_t values_alloc; /* number of allocated pointers */ time_t last_flush_time; - time_t last_update_stamp; + double last_update_stamp; #define CI_FLAGS_IN_TREE (1<<0) #define CI_FLAGS_IN_QUEUE (1<<1) int flags; @@ -215,9 +212,7 @@ typedef struct { size_t files_num; } journal_set; -/* max length of socket command or response */ -#define CMD_MAX 4096 -#define RBUF_SIZE (CMD_MAX*2) +#define RBUF_SIZE (RRD_CMD_MAX*2) /* * Variables @@ -275,6 +270,8 @@ static uint64_t stats_journal_bytes = 0; static uint64_t stats_journal_rotate = 0; static pthread_mutex_t stats_lock = PTHREAD_MUTEX_INITIALIZER; +static int opt_no_overwrite = 0; /* default for the daemon */ + /* Journaled updates */ #define JOURNAL_REPLAY(s) ((s) == NULL) #define JOURNAL_BASE "rrd.journal" @@ -298,7 +295,9 @@ static int handle_request_help (HANDLER_PROTO); static void sig_common (const char *sig) /* {{{ */ { RRDD_LOG(LOG_NOTICE, "caught SIG%s", sig); - state = FLUSHING; + if (state == RUNNING) { + state = FLUSHING; + } pthread_cond_broadcast(&flush_cond); pthread_cond_broadcast(&queue_cond); } /* }}} void sig_common */ @@ -537,7 +536,7 @@ static int add_to_wbuf(listen_socket_t *sock, char *str, size_t len) /* {{{ */ static int add_response_info(listen_socket_t *sock, char *fmt, ...) /* {{{ */ { va_list argp; - char buffer[CMD_MAX]; + char buffer[RRD_CMD_MAX]; int len; if (JOURNAL_REPLAY(sock)) return 0; @@ -582,7 +581,7 @@ static int send_response (listen_socket_t *sock, response_code rc, char *fmt, ...) /* {{{ */ { va_list argp; - char buffer[CMD_MAX]; + char buffer[RRD_CMD_MAX]; int lines; ssize_t wrote; int rclen, len; @@ -1321,13 +1320,13 @@ static int handle_request_update (HANDLER_PROTO) /* {{{ */ char *file, file_tmp[PATH_MAX]; int values_num = 0; int status; - char orig_buf[CMD_MAX]; + char orig_buf[RRD_CMD_MAX]; cache_item_t *ci; /* save it for the journal later */ if (!JOURNAL_REPLAY(sock)) - strncpy(orig_buf, buffer, buffer_size); + strncpy(orig_buf, buffer, min(RRD_CMD_MAX,buffer_size)); status = buffer_get_field (&buffer, &buffer_size, &file); if (status != 0) @@ -1418,7 +1417,7 @@ static int handle_request_update (HANDLER_PROTO) /* {{{ */ while (buffer_size > 0) { char *value; - time_t stamp; + double stamp; char *eostamp; status = buffer_get_field (&buffer, &buffer_size, &value); @@ -1428,8 +1427,9 @@ static int handle_request_update (HANDLER_PROTO) /* {{{ */ break; } - /* make sure update time is always moving forward */ - stamp = strtol(value, &eostamp, 10); + /* make sure update time is always moving forward. We use double here since + update does support subsecond precision for timestamps ... */ + stamp = strtod(value, &eostamp); if (eostamp == value || eostamp == NULL || *eostamp != ':') { pthread_mutex_unlock(&cache_lock); @@ -1440,8 +1440,8 @@ static int handle_request_update (HANDLER_PROTO) /* {{{ */ { pthread_mutex_unlock(&cache_lock); return send_response(sock, RESP_ERR, - "illegal attempt to update using time %ld when last" - " update time is %ld (minimum one second step)\n", + "illegal attempt to update using time %lf when last" + " update time is %lf (minimum one second step)\n", stamp, ci->last_update_stamp); } else @@ -1695,6 +1695,192 @@ static int handle_request_wrote (HANDLER_PROTO) /* {{{ */ return (0); } /* }}} int handle_request_wrote */ +static int handle_request_info (HANDLER_PROTO) /* {{{ */ +{ + char *file, file_tmp[PATH_MAX]; + int status; + rrd_info_t *info; + + /* obtain filename */ + status = buffer_get_field(&buffer, &buffer_size, &file); + if (status != 0) + return syntax_error(sock,cmd); + /* get full pathname */ + get_abs_path(&file, file_tmp); + if (!check_file_access(file, sock)) { + return send_response(sock, RESP_ERR, "Cannot read: %s\n", file); + } + /* get data */ + rrd_clear_error (); + info = rrd_info_r(file); + if(!info) { + return send_response(sock, RESP_ERR, "RRD Error: %s\n", rrd_get_error()); + } + for (rrd_info_t *data = info; data != NULL; data = data->next) { + switch (data->type) { + case RD_I_VAL: + if (isnan(data->value.u_val)) + add_response_info(sock,"%s %d NaN\n",data->key, data->type); + else + add_response_info(sock,"%s %d %0.10e\n", data->key, data->type, data->value.u_val); + break; + case RD_I_CNT: + add_response_info(sock,"%s %d %lu\n", data->key, data->type, data->value.u_cnt); + break; + case RD_I_INT: + add_response_info(sock,"%s %d %d\n", data->key, data->type, data->value.u_int); + break; + case RD_I_STR: + add_response_info(sock,"%s %d %s\n", data->key, data->type, data->value.u_str); + break; + case RD_I_BLO: + add_response_info(sock,"%s %d %lu\n", data->key, data->type, data->value.u_blo.size); + break; + } + } + + rrd_info_free(info); + + return send_response(sock, RESP_OK, "Info for %s follows\n",file); +} /* }}} static int handle_request_info */ + +static int handle_request_first (HANDLER_PROTO) /* {{{ */ +{ + char *i, *file, file_tmp[PATH_MAX]; + int status; + int idx; + time_t t; + + /* obtain filename */ + status = buffer_get_field(&buffer, &buffer_size, &file); + if (status != 0) + return syntax_error(sock,cmd); + /* get full pathname */ + get_abs_path(&file, file_tmp); + if (!check_file_access(file, sock)) { + return send_response(sock, RESP_ERR, "Cannot read: %s\n", file); + } + + status = buffer_get_field(&buffer, &buffer_size, &i); + if (status != 0) + return syntax_error(sock,cmd); + idx = atoi(i); + if(idx<0) { + return send_response(sock, RESP_ERR, "Invalid index specified (%d)\n", idx); + } + + /* get data */ + rrd_clear_error (); + t = rrd_first_r(file,idx); + if(t<1) { + return send_response(sock, RESP_ERR, "RRD Error: %s\n", rrd_get_error()); + } + return send_response(sock, RESP_OK, "%lu\n",(unsigned)t); +} /* }}} static int handle_request_first */ + + +static int handle_request_last (HANDLER_PROTO) /* {{{ */ +{ + char *file, file_tmp[PATH_MAX]; + int status; + time_t t, from_file, step; + rrd_file_t * rrd_file; + cache_item_t * ci; + rrd_t rrd; + + /* obtain filename */ + status = buffer_get_field(&buffer, &buffer_size, &file); + if (status != 0) + return syntax_error(sock,cmd); + /* get full pathname */ + get_abs_path(&file, file_tmp); + if (!check_file_access(file, sock)) { + return send_response(sock, RESP_ERR, "Cannot read: %s\n", file); + } + rrd_clear_error(); + rrd_init(&rrd); + rrd_file = rrd_open(file,&rrd,RRD_READONLY); + if(!rrd_file) { + return send_response(sock, RESP_ERR, "RRD Error: %s\n", rrd_get_error()); + } + from_file = rrd.live_head->last_up; + step = rrd.stat_head->pdp_step; + rrd_close(rrd_file); + pthread_mutex_lock(&cache_lock); + ci = g_tree_lookup(cache_tree, file); + if (ci) + t = ci->last_update_stamp; + else + t = from_file; + pthread_mutex_unlock(&cache_lock); + t -= t % step; + rrd_free(&rrd); + if(t<1) { + return send_response(sock, RESP_ERR, "Error: rrdcached: Invalid timestamp returned\n"); + } + return send_response(sock, RESP_OK, "%lu\n",(unsigned)t); +} /* }}} static int handle_request_last */ + +static int handle_request_create (HANDLER_PROTO) /* {{{ */ +{ + char *file, file_tmp[PATH_MAX]; + char *tok; + int ac = 0; + char *av[128]; + int status; + unsigned long step = 300; + time_t last_up = time(NULL)-10; + int no_overwrite = opt_no_overwrite; + + + /* obtain filename */ + status = buffer_get_field(&buffer, &buffer_size, &file); + if (status != 0) + return syntax_error(sock,cmd); + /* get full pathname */ + get_abs_path(&file, file_tmp); + if (!check_file_access(file, sock)) { + return send_response(sock, RESP_ERR, "Cannot read: %s\n", file); + } + RRDD_LOG(LOG_INFO, "rrdcreate request for %s",file); + + while ((status = buffer_get_field(&buffer, &buffer_size, &tok)) == 0 && tok) { + if( ! strncmp(tok,"-b",2) ) { + status = buffer_get_field(&buffer, &buffer_size, &tok ); + if (status != 0) return syntax_error(sock,cmd); + last_up = (time_t) atol(tok); + continue; + } + if( ! strncmp(tok,"-s",2) ) { + status = buffer_get_field(&buffer, &buffer_size, &tok ); + if (status != 0) return syntax_error(sock,cmd); + step = atol(tok); + continue; + } + if( ! strncmp(tok,"-O",2) ) { + no_overwrite = 1; + continue; + } + if( ! strncmp(tok,"DS:",3) ) { av[ac++]=tok; continue; } + if( ! strncmp(tok,"RRA:",4) ) { av[ac++]=tok; continue; } + return syntax_error(sock,cmd); + } + if(step<1) { + return send_response(sock, RESP_ERR, "The step size cannot be less than 1 second.\n"); + } + if (last_up < 3600 * 24 * 365 * 10) { + return send_response(sock, RESP_ERR, "The first entry must be after 1980.\n"); + } + + rrd_clear_error (); + status = rrd_create_r2(file,step,last_up,no_overwrite,ac,(const char **)av); + + if(!status) { + return send_response(sock, RESP_OK, "RRD created OK\n"); + } + return send_response(sock, RESP_ERR, "RRD Error: %s\n", rrd_get_error()); +} /* }}} static int handle_request_create */ + /* start "BATCH" processing */ static int batch_start (HANDLER_PROTO) /* {{{ */ { @@ -1848,6 +2034,47 @@ static command_t list_of_commands[] = { /* {{{ */ "The 'FETCH' can be used by the client to retrieve values from an RRD file.\n" }, { + "INFO", + handle_request_info, + CMD_CONTEXT_CLIENT, + "INFO \n", + "The INFO command retrieves information about a specified RRD file.\n" + "This is returned in standard rrdinfo format, a sequence of lines\n" + "with the format = \n" + "Note that this is the data as of the last update of the RRD file itself,\n" + "not the last time data was received via rrdcached, so there may be pending\n" + "updates in the queue. If this bothers you, then first run a FLUSH.\n" + }, + { + "FIRST", + handle_request_first, + CMD_CONTEXT_CLIENT, + "FIRST \n", + "The FIRST command retrieves the first data time for a specified RRA in\n" + "an RRD file.\n" + }, + { + "LAST", + handle_request_last, + CMD_CONTEXT_CLIENT, + "LAST \n", + "The LAST command retrieves the last update time for a specified RRD file.\n" + "Note that this is the time of the last update of the RRD file itself, not\n" + "the last time data was received via rrdcached, so there may be pending\n" + "updates in the queue. If this bothers you, then first run a FLUSH.\n" + }, + { + "CREATE", + handle_request_create, + CMD_CONTEXT_CLIENT | CMD_CONTEXT_BATCH, + "CREATE [-b start] [-s step] [-O] \n", + "The CREATE command will create an RRD file, overwriting any existing file\n" + "unless the -O option is given or rrdcached was started with the -O option.\n" + "The start parameter needs to be in seconds since 1/1/70 (AT-style syntax is\n" + "not acceptable) and the step is in seconds (default is 300).\n" + "The DS and RRA definitions are as for the 'rrdtool create' command.\n" + }, + { "QUIT", handle_request_quit, CMD_CONTEXT_CLIENT | CMD_CONTEXT_BATCH, @@ -1935,6 +2162,15 @@ static void socket_permission_copy (listen_socket_t *dest, /* {{{ */ dest->permissions = src->permissions; } /* }}} socket_permission_copy */ +static void socket_permission_set_all (listen_socket_t *sock) /* {{{ */ +{ + size_t i; + + sock->permissions = 0; + for (i = 0; i < list_of_commands_len; i++) + sock->permissions |= (1 << i); +} /* }}} void socket_permission_set_all */ + /* check whether commands are received in the expected context */ static int command_check_context(listen_socket_t *sock, command_t *cmd) { @@ -1962,7 +2198,7 @@ static int handle_request_help (HANDLER_PROTO) /* {{{ */ if (help && (help->syntax || help->help)) { - char tmp[CMD_MAX]; + char tmp[RRD_CMD_MAX]; snprintf(tmp, sizeof(tmp)-1, "Help for %s\n", help->cmd); resp_txt = tmp; @@ -2197,7 +2433,7 @@ static int journal_replay (const char *file) /* {{{ */ int entry_cnt = 0; int fail_cnt = 0; uint64_t line = 0; - char entry[CMD_MAX]; + char entry[RRD_CMD_MAX]; time_t now; if (file == NULL) return 0; @@ -2407,6 +2643,21 @@ static void *connection_thread_main (void *args) /* {{{ */ } pthread_mutex_lock (&connection_threads_lock); +#ifdef HAVE_LIBWRAP + /* LIBWRAP does not support multiple threads! By putting this code + inside pthread_mutex_lock we do not have to worry about request_info + getting overwritten by another thread. + */ + struct request_info req; + request_init(&req, RQ_DAEMON, "rrdcached\0", RQ_FILE, fd, NULL ); + fromhost(&req); + if(!hosts_access(&req)) { + RRDD_LOG(LOG_INFO, "refused connection from %s", eval_client(&req)); + pthread_mutex_unlock (&connection_threads_lock); + close_connection(sock); + return NULL; + } +#endif /* HAVE_LIBWRAP */ connection_threads_num++; pthread_mutex_unlock (&connection_threads_lock); @@ -2883,6 +3134,10 @@ static int daemonize (void) /* {{{ */ strncpy(default_socket.addr, RRDCACHED_DEFAULT_ADDRESS, sizeof(default_socket.addr) - 1); default_socket.addr[sizeof(default_socket.addr) - 1] = '\0'; + + if (default_socket.permissions == 0) + socket_permission_set_all (&default_socket); + open_listen_socket (&default_socket); } @@ -2993,10 +3248,14 @@ static int read_options (int argc, char **argv) /* {{{ */ default_socket.socket_group = (gid_t)-1; default_socket.socket_permissions = (mode_t)-1; - while ((option = getopt(argc, argv, "gl:s:m:P:f:w:z:t:Bb:p:Fj:a:h?")) != -1) + while ((option = getopt(argc, argv, "Ogl:s:m:P:f:w:z:t:Bb:p:Fj:a:h?")) != -1) { switch (option) { + case 'O': + opt_no_overwrite = 1; + break; + case 'g': stay_foreground=1; break; @@ -3023,18 +3282,7 @@ static int read_options (int argc, char **argv) /* {{{ */ else /* if (default_socket.permissions == 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", - list_of_commands[i].cmd); - status = 4; - } - } + socket_permission_set_all (new); } /* }}} Done adding permissions. */ @@ -3279,23 +3527,30 @@ static int read_options (int argc, char **argv) /* {{{ */ case 'j': { char journal_dir_actual[PATH_MAX]; - const char *dir; - dir = journal_dir = strdup(realpath((const char *)optarg, journal_dir_actual)); - - status = rrd_mkdir_p(dir, 0777); - if (status != 0) - { - fprintf(stderr, "Failed to create journal directory '%s': %s\n", - dir, rrd_strerror(errno)); - return 6; - } - - 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) : ""); - return 6; - } + journal_dir = realpath((const char *)optarg, journal_dir_actual); + if (journal_dir) + { + // if we were able to properly resolve the path, lets have a copy + // for use outside this block. + journal_dir = strdup(journal_dir); + status = rrd_mkdir_p(journal_dir, 0777); + if (status != 0) + { + fprintf(stderr, "Failed to create journal directory '%s': %s\n", + journal_dir, rrd_strerror(errno)); + return 6; + } + if (access(journal_dir, R_OK|W_OK|X_OK) != 0) + { + fprintf(stderr, "Must specify a writable directory with -j! (%s)\n", + errno ? rrd_strerror(errno) : ""); + return 6; + } + } else { + fprintf(stderr, "Unable to resolve journal path (%s,%s)\n", optarg, + errno ? rrd_strerror(errno) : ""); + return 6; + } } break; @@ -3321,6 +3576,7 @@ static int read_options (int argc, char **argv) /* {{{ */ "\n" "Valid options are:\n" " -l
Socket address to listen to.\n" + " Default: "RRDCACHED_DEFAULT_ADDRESS"\n" " -P Sets the permissions to assign to all following " "sockets\n" " -w Interval in which to write data.\n" @@ -3338,7 +3594,9 @@ static int read_options (int argc, char **argv) /* {{{ */ "for that group)\n" " -m File permissions (octal) of all following UNIX " "sockets\n" - " -a Memory allocation chunk size. Default is 1." + " -a Memory allocation chunk size. Default is 1.\n" + " -O Do not allow CREATE commands to overwrite existing\n" + " files, even if asked to.\n" "\n" "For more information and a detailed description of all options " "please refer\n"