-RRDtool git - 2022-04-28
-==========================
+RRDtool - master ...
+======================
Bugfixes
--------
--------
* Remove autogenerated files from git repo (configure, Makefile.in, conftools, rrd_config.h.in) <c72578>
+* Reads $RRD_LOCKING environment variable and adds --locking option to some tools. The updatex api has been also
+ updated to support setting locking related bits in its extra_flags parameter. @ensc
+
+ This allows now to choose between three kinds of locking:
+
+ none: no locking is done at all; caller has to do it manually and can implement e.g. a timeout with alarm(2) or so
+ try: fails when lock is hold by another process; users will see "ERROR: could not lock RRD". This is the default and the only possible mode with the old code
+ block: waits until lock is available
+
+ It can be used like
+
+ env RRD_LOCKING=block rrdupdate ...
+
+ or
+
+ rrdupdate --locking none ...
+
+ or
+
+ rrd_updatex_r(filename, tmplt, RRD_FLAGS_LOCKING_MODE_BLOCK, ...);
+
RRDtool 1.8.0 - 2022-03-13
==========================
B<NOTE:> that there is no authentication with this feature! Do not setup
such a port unless you are sure what you are doing.
+=head1 ENVIRONMENT VARIABLES
+
+The following environment variables may be used to change the behavior
+of most of the utilities.
+
+=over
+
+=item B<RRD_LOCKING>
+
+If this environment variable is set, the B<RRD> file is locked in the
+given mode:
+
+=over
+
+=item -
+
+B<try> fails, when the file is locked by another process. This is the default.
+
+=item -
+
+B<block> waits until the lock is released.
+
+=item -
+
+B<none> skips locking at all. Caller has to ensure a proper locking
+which should be compatible with C<fcntl(fd, F_SETLK, ...)>.
+
+=back
+
+Some utilities have command line options (B<--locking>) which override
+this variable.
+
+=back
+
=head1 RRDCACHED, THE CACHING DAEMON
For very big setups, updating thousands of RRD files often becomes a serious IO
The name of the B<RRD> you want to update.
+=item B<--locking>|B<-L> B<try>|B<block>|B<none>
+
+Lock the B<RRD> file in the given mode: B<try> fails, when the file
+is locked by another process, B<block> waits until the lock is
+released and B<none> skips locking at all. The default is read
+from the B<$RRD_LOCKING> environment variable and falls back to
+B<try> when such a variable does not exist.
+
=item B<--template>|B<-t> I<ds-name>[B<:>I<ds-name>]...
By default, the B<update> function expects its data input in the order
This is mostly intended to allow rrdcached to work with xymon and cacti tools
without having to modify those tools.
+=item B<RRD_LOCKING>
+
+If this environment variable is set, the B<RRD> file is locked in the
+given mode: B<try> fails, when the file is locked by another process,
+B<block> waits until the lock is released and B<none> skips locking at
+all.
+
+This variable can be overridden by the B<--locking> command line option.
+
=back
=head1 EXAMPLES
/* extra flags */
#define RRD_SKIP_PAST_UPDATES 0x01
+/* Locking mode which can be set in 'extra_flags':
+ *
+ * DEFAULT ... read $RRD_LOCKING environment or fall back to TRY
+ * NONE ... no locking; caller is responsible to ensure that the file is not
+ * used else
+ * BLOCK ... wait until lock is available
+ * TRY ... try to lock but fail when file is used elsewhere (default)
+ */
+#define RRD_FLAGS_LOCKING_MODE_NONE (1 << 7)
+#define RRD_FLAGS_LOCKING_MODE_DEFAULT (0 << 7)
+#define RRD_FLAGS_LOCKING_MODE_BLOCK (2 << 7)
+#define RRD_FLAGS_LOCKING_MODE_TRY (3 << 7)
+#define RRD_FLAGS_LOCKING_MODE_MASK (3 << 7)
+
int rrd_updatex_r(
const char *filename,
const char *_template,
static int rrd_rwlock(
rrd_file_t *rrd_file,
- int writelock);
+ int writelock,
+ int lock_mode);
static int close_and_unlock(
int fd);
rrd_simple_file_t *rrd_simple_file = NULL;
size_t newfile_size = 0;
+ if ((rdwr & RRD_LOCK_MASK) == RRD_LOCK_DEFAULT) {
+ rdwr &= ~RRD_LOCK_MASK;
+ rdwr |= _rrd_lock_flags(_rrd_lock_default());
+ }
+
/* Are we creating a new file? */
if (rdwr & RRD_CREAT) {
size_t header_len, value_cnt, data_len;
#endif
#endif
- if (rdwr & RRD_LOCK) {
- if (rrd_rwlock(rrd_file, rdwr & RRD_READWRITE) != 0) {
- rrd_set_error("could not lock RRD");
- goto out_close;
- }
+ if (rrd_rwlock(rrd_file, rdwr & RRD_READWRITE, rdwr & RRD_LOCK_MASK) != 0) {
+ rrd_set_error("could not lock RRD");
+ goto out_close;
}
/* Better try to avoid seeks as much as possible. stat may be heavy but
int rrd_lock(
rrd_file_t *rrd_file)
{
- return rrd_rwlock(rrd_file, 1);
+ return rrd_rwlock(rrd_file, 1, RRD_LOCK_DEFAULT);
}
#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
static
int rrd_rwlock(
rrd_file_t *rrd_file,
- int writelock)
+ int writelock,
+ int lock_mode)
{
+ if (lock_mode == RRD_LOCK_NONE)
+ return 0;
+
#ifdef DISABLE_FLOCK
(void) rrd_file;
return 0;
#else
{
struct flock lock;
+ int op = lock_mode == RRD_LOCK_TRY ? F_SETLK : F_SETLKW;
lock.l_type = writelock ? F_WRLCK : /* exclusive write lock or */
F_RDLCK; /* shared read lock */
lock.l_start = 0; /* start of file */
lock.l_whence = SEEK_SET; /* end of file */
- rcstat = fcntl(rrd_simple_file->fd, F_SETLK, &lock);
+ rcstat = fcntl(rrd_simple_file->fd, op, &lock);
}
#endif
{
return rrd_random() % rra->row_cnt;
}
+
+/*
+ * Translates a string in a RRD_FLAGS_LOCKING_xxx constant.
+ *
+ * Empty or non-existing strings are valid and will be mapped to a default
+ * value.
+ *
+ * Functions returns -1 on unsupported values but does not emit diagnostics.
+ */
+static int _rrd_lock_parse(const char *opt)
+{
+ /* non-existing and empty values */
+ if (!opt || !opt[0])
+ /* the default locking mode */
+ return RRD_FLAGS_LOCKING_MODE_TRY;
+ else if (strcmp(opt, "try") == 0)
+ return RRD_FLAGS_LOCKING_MODE_TRY;
+ else if (strcmp(opt, "block") == 0)
+ return RRD_FLAGS_LOCKING_MODE_BLOCK;
+ else if (strcmp(opt, "none") == 0)
+ return RRD_FLAGS_LOCKING_MODE_NONE;
+ else
+ return -1;
+}
+
+/*
+ * Returns the default locking method.
+ *
+ * It reads the $RRD_LOCKING environment.
+ *
+ * Function always succeeds; unsupported values will emit a
+ * diagnostic and function returns a default value in this case.
+ */
+int _rrd_lock_default(void)
+{
+ const char *opt = getenv("RRD_LOCKING");
+ int flags = _rrd_lock_parse(opt);
+
+ if (flags < 0) {
+ fprintf(stderr,
+ "unsupported locking mode '%s' in $RRD_LOCKING; assuming 'try'\n",
+ opt);
+ return RRD_FLAGS_LOCKING_MODE_TRY;
+ }
+
+ return flags;
+}
+
+/*
+ * Translates a string to a RRD_FLAGS_LOCKING_xxx constant and updates flags.
+ *
+ * Function will fail on unsupported values and return -1. It sets rrd_set_error()
+ * in this case.
+ *
+ * Else, the RRD_FLAGS_LOCKING_xxx related bits in 'out_flags' will be cleared
+ * and updated. Function returns 0 then.
+ */
+int _rrd_lock_from_opt(int *out_flags, const char *opt)
+{
+ int flags = _rrd_lock_parse(opt);
+
+ if (flags < 0) {
+ rrd_set_error("unsupported locking mode '%s'\n", opt);
+ return flags;
+ }
+
+ *out_flags &= ~RRD_FLAGS_LOCKING_MODE_MASK;
+ *out_flags |= flags;
+
+ return 0;
+}
+
+/*
+ * Translates RRD_FLAGS_LOCKING_MODE_xxx to RRD_LOCK_xxx
+ *
+ * Function removes unrelated bits from 'extra_flags' and maps it to the
+ * RRD_LOCK_xxx constants.
+ */
+int _rrd_lock_flags(int extra_flags)
+{
+ /* Due to legacy reasons, we have to map this manually.
+ *
+ * E.g. RRD_LOCK_DEFAULT (which might be used by deprecated direct calls
+ * to rrd_open()) must be non-zero. But RRD_FLAGS_LOCKING_MODE_DEFAULT
+ * must be 0 because not all users of the updatex api might have been
+ * updated yet.
+ */
+ switch (extra_flags & RRD_FLAGS_LOCKING_MODE_MASK) {
+ case RRD_FLAGS_LOCKING_MODE_NONE:
+ return RRD_LOCK_NONE;
+ case RRD_FLAGS_LOCKING_MODE_TRY:
+ return RRD_LOCK_TRY;
+ case RRD_FLAGS_LOCKING_MODE_BLOCK:
+ return RRD_LOCK_BLOCK;
+ case RRD_FLAGS_LOCKING_MODE_DEFAULT:
+ return RRD_LOCK_DEFAULT;
+ default:
+ abort();
+ }
+}
#define RRD_COPY (1<<4)
#define RRD_EXCL (1<<5)
#define RRD_READVALUES (1<<6)
-#define RRD_LOCK (1<<7)
+#define RRD_LOCK RRD_LOCK_DEFAULT
+#define RRD_LOCK_NONE (0<<7)
+#define RRD_LOCK_DEFAULT (1<<7)
+#define RRD_LOCK_BLOCK (2<<7)
+#define RRD_LOCK_TRY (3<<7)
+#define RRD_LOCK_MASK (3<<7)
enum cf_en rrd_cf_conv(
const char *string);
const char *cf_to_string (enum cf_en cf);
+ int _rrd_lock_default(void);
+ int _rrd_lock_from_opt(int *out_flags, const char *opt);
+ int _rrd_lock_flags(int extra_flags);
+
#ifdef __cplusplus
}
#endif
struct optparse_long longopts[] = {
{"template", 't', OPTPARSE_REQUIRED},
{"skip-past-updates", 's', OPTPARSE_NONE},
+ {"locking", 'L', OPTPARSE_REQUIRED},
{0},
};
struct optparse options;
int opt;
const char *tmplt = NULL;
- int extra_flags = 0;
+ int extra_flags = _rrd_lock_default();
rrd_info_t *result = NULL;
rrd_infoval_t rc;
char *opt_daemon = NULL;
extra_flags |= RRD_SKIP_PAST_UPDATES;
break;
+ case 'L':
+ if (_rrd_lock_from_opt(&extra_flags, options.optarg) < 0)
+ goto end_tag;
+ break;
+
case '?':
rrd_set_error("%s", options.errmsg);
goto end_tag;
{"template", 't', OPTPARSE_REQUIRED},
{"daemon", 'd', OPTPARSE_REQUIRED},
{"skip-past-updates", 's', OPTPARSE_NONE},
+ {"locking", 'L', OPTPARSE_REQUIRED},
{0},
};
struct optparse options;
int opt;
char *tmplt = NULL;
- int extra_flags = 0;
+ int extra_flags = _rrd_lock_default();
int rc = -1;
char *opt_daemon = NULL;
}
break;
+ case 'L':
+ if (_rrd_lock_from_opt(&extra_flags, options.optarg) < 0)
+ goto out;
+ break;
+
case '?':
rrd_set_error("%s", options.errmsg);
goto out;
{
rrd_clear_error();
if (tmplt) {
- if (extra_flags != 0) {
+ if ((extra_flags & RRD_SKIP_PAST_UPDATES) != 0) {
rrd_set_error("The caching daemon cannot be used together with "
"templates and skip-past-updates yet.");
goto out;
}
rrd_init(&rrd);
- rrd_file = rrd_open(filename, &rrd, RRD_READWRITE | RRD_LOCK);
+ rrd_file = rrd_open(filename, &rrd, RRD_READWRITE |
+ _rrd_lock_flags(extra_flags));
if (rrd_file == NULL) {
goto err_free;
}
}
else {
printf("Usage: rrdupdate <filename>\n"
+ "\t\t\t[--locking|-L <try|block|none>]\n"
"\t\t\t[--template|-t ds-name[:ds-name]...]\n"
"\t\t\t[--skip-past-updates]\n"
"\t\t\ttime|N:value[:value...]\n\n"