#endif
#endif
-#include "rrd.h"
+#include "rrd_tool.h"
#include "rrd_client.h"
#include "unused.h"
#define RRDD_LOG(severity, ...) \
do { \
- if (stay_foreground) \
+ if (stay_foreground) { \
fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, "\n"); } \
syslog ((severity), __VA_ARGS__); \
} while (0)
static listen_socket_t *listen_fds = NULL;
static size_t listen_fds_num = 0;
+static listen_socket_t default_socket;
+
enum {
RUNNING, /* normal operation */
FLUSHING, /* flushing remaining values */
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"
static int handle_request_fetch (HANDLER_PROTO) /* {{{ */
{
- char *file;
+ char *file, file_tmp[PATH_MAX];
char *cf;
char *start_str;
if (status != 0)
return (syntax_error(sock,cmd));
+ get_abs_path(&file, file_tmp);
+ if (!check_file_access(file, sock)) return 0;
+
status = flush_file (file);
if ((status != 0) && (status != ENOENT))
return (send_response (sock, RESP_ERR,
return (0);
} /* }}} int handle_request_wrote */
+static int handle_request_info (HANDLER_PROTO) /* {{{ */
+{
+ char *file, file_tmp[PATH_MAX];
+ int status;
+ rrd_info_t *data;
+
+ /* 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 ();
+ data = rrd_info_r(file);
+ if(!data) {
+ return send_response(sock, RESP_ERR, "RRD Error: %s\n", rrd_get_error());
+ }
+ while (data) {
+ 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;
+ }
+ data = data->next;
+ }
+ 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) /* {{{ */
{
"The 'FETCH' can be used by the client to retrieve values from an RRD file.\n"
},
{
+ "INFO",
+ handle_request_info,
+ CMD_CONTEXT_CLIENT,
+ "INFO <filename>\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 <keyname> = <value>\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 <filename> <rra index>\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 <filename>\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 <filename> [-b start] [-s step] [-O] <DS definitions> <RRA definitions>\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,
return (0);
} /* }}} int socket_permission_add */
+static void socket_permission_clear (listen_socket_t *sock) /* {{{ */
+{
+ sock->permissions = 0;
+} /* }}} socket_permission_clear */
+
+static void socket_permission_copy (listen_socket_t *dest, /* {{{ */
+ listen_socket_t *src)
+{
+ dest->permissions = src->permissions;
+} /* }}} socket_permission_copy */
+
/* check whether commands are received in the expected context */
static int command_check_context(listen_socket_t *sock, command_t *cmd)
{
}
else
{
- listen_socket_t sock;
- memset(&sock, 0, sizeof(sock));
- strncpy(sock.addr, RRDCACHED_DEFAULT_ADDRESS, sizeof(sock.addr)-1);
- open_listen_socket (&sock);
+ strncpy(default_socket.addr, RRDCACHED_DEFAULT_ADDRESS,
+ sizeof(default_socket.addr) - 1);
+ default_socket.addr[sizeof(default_socket.addr) - 1] = '\0';
+ open_listen_socket (&default_socket);
}
if (listen_fds_num < 1)
int option;
int status = 0;
- char **permissions = NULL;
- size_t permissions_len = 0;
+ socket_permission_clear (&default_socket);
- gid_t socket_group = (gid_t)-1;
- mode_t socket_permissions = (mode_t)-1;
+ 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;
strncpy(new->addr, optarg, sizeof(new->addr)-1);
/* Add permissions to the socket {{{ */
- if (permissions_len != 0)
+ if (default_socket.permissions != 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;
- }
- }
+ socket_permission_copy (new, &default_socket);
}
- else /* if (permissions_len == 0) */
+ else /* if (default_socket.permissions == 0) */
{
/* Add permission for ALL commands to the socket. */
size_t i;
{
fprintf (stderr, "read_options: Adding permission \"%s\" to "
"socket failed. This should never happen, ever! Sorry.\n",
- permissions[i]);
+ list_of_commands[i].cmd);
status = 4;
}
}
}
/* }}} Done adding permissions. */
- new->socket_group = socket_group;
- new->socket_permissions = socket_permissions;
+ new->socket_group = default_socket.socket_group;
+ new->socket_permissions = default_socket.socket_permissions;
if (!rrd_add_ptr((void ***)&config_listen_address_list,
&config_listen_address_list_len, new))
if (grp)
{
- socket_group = grp->gr_gid;
+ default_socket.socket_group = grp->gr_gid;
}
else
{
return (5);
}
- socket_permissions = (mode_t)tmp;
+ default_socket.socket_permissions = (mode_t)tmp;
}
break;
char *dummy;
char *ptr;
- rrd_free_ptrs ((void *) &permissions, &permissions_len);
+ socket_permission_clear (&default_socket);
optcopy = strdup (optarg);
dummy = optcopy;
while ((ptr = strtok_r (dummy, ", ", &saveptr)) != NULL)
{
dummy = NULL;
- rrd_add_strdup ((void *) &permissions, &permissions_len, ptr);
+ status = socket_permission_add (&default_socket, ptr);
+ 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", ptr);
+ status = 4;
+ }
}
free (optcopy);
"for that group)\n"
" -m <mode> File permissions (octal) of all following UNIX "
"sockets\n"
- " -a <size> Memory allocation chunk size. Default is 1."
+ " -a <size> 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"
if (journal_dir == NULL)
config_flush_at_shutdown = 1;
- rrd_free_ptrs ((void *) &permissions, &permissions_len);
-
return (status);
} /* }}} int read_options */