File util-linux-libuuid-continuous-clock-handling.patch of Package util-linux.27359
The backport contains parts of:
From 9fc0f69c255ccd62e5841c6376285e3de8f009b6 Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@redhat.com>
Date: Thu, 24 Jun 2021 16:36:58 +0200
Subject: [PATCH] include/strutils: consolidate string to number conversion
From 3cfba7d39b66eff4307218fefd8bb34bb1621f83 Mon Sep 17 00:00:00 2001
From: Michael Trapp <michael.trapp@sap.com>
Date: Mon, 20 Jun 2022 17:10:36 +0200
Subject: [PATCH 3/4] libuuid: Implement continuous clock handling for time based
UUIDs
In a uuidd setup, the daemon is a singleton and can maintain it's own
resources for time based UUID generation. This requires a dedicated
'clock sequence range' but does not need any further lock/update of
the LIBUUID_CLOCK_FILE from uuidd. The range of available clock values
is extended by a continuous handling of the clock updates - instead of
updating the value to the current timestamp, it is incremented by
the number of requested UUIDs.
---
libuuid/src/gen_uuid.c | 91 ++++++++++++++++++++++++++++++++++++++---
libuuid/src/libuuid.sym | 1 +
libuuid/src/uuidd.h | 1 +
misc-utils/uuidd.8.adoc | 3 ++
misc-utils/uuidd.c | 54 +++++++++++++++++++++---
5 files changed, 140 insertions(+), 10 deletions(-)
Index: util-linux-2.36.2/libuuid/src/gen_uuid.c
===================================================================
--- util-linux-2.36.2.orig/libuuid/src/gen_uuid.c
+++ util-linux-2.36.2/libuuid/src/gen_uuid.c
@@ -209,6 +209,8 @@ static int get_node_id(unsigned char *no
/* Assume that the gettimeofday() has microsecond granularity */
#define MAX_ADJUSTMENT 10
+/* Reserve a clock_seq value for the 'continuous clock' implementation */
+#define CLOCK_SEQ_CONT 0
/*
* Get clock from global sequence clock counter.
@@ -275,8 +277,10 @@ static int get_clock(uint32_t *clock_hig
}
if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
- ul_random_get_bytes(&clock_seq, sizeof(clock_seq));
- clock_seq &= 0x3FFF;
+ do {
+ ul_random_get_bytes(&clock_seq, sizeof(clock_seq));
+ clock_seq &= 0x3FFF;
+ } while (clock_seq == CLOCK_SEQ_CONT);
gettimeofday(&last, NULL);
last.tv_sec--;
}
@@ -286,7 +290,9 @@ try_again:
if ((tv.tv_sec < last.tv_sec) ||
((tv.tv_sec == last.tv_sec) &&
(tv.tv_usec < last.tv_usec))) {
- clock_seq = (clock_seq+1) & 0x3FFF;
+ do {
+ clock_seq = (clock_seq+1) & 0x3FFF;
+ } while (clock_seq == CLOCK_SEQ_CONT);
adjustment = 0;
last = tv;
} else if ((tv.tv_sec == last.tv_sec) &&
@@ -331,6 +337,64 @@ try_again:
return ret;
}
+/*
+ * Get current time in 100ns ticks.
+ */
+static uint64_t get_clock_counter(void)
+{
+ struct timeval tv;
+ uint64_t clock_reg;
+
+ gettimeofday(&tv, NULL);
+ clock_reg = tv.tv_usec*10;
+ clock_reg += ((uint64_t) tv.tv_sec) * 10000000ULL;
+
+ return clock_reg;
+}
+
+/*
+ * Get continuous clock value.
+ *
+ * Return -1 if there is no further clock counter available,
+ * otherwise return 0.
+ *
+ * This implementation doesn't deliver clock counters based on
+ * the current time because last_clock_reg is only incremented
+ * by the number of requested UUIDs.
+ * max_clock_offset is used to limit the offset of last_clock_reg.
+ */
+static int get_clock_cont(uint32_t *clock_high,
+ uint32_t *clock_low,
+ int num,
+ uint32_t max_clock_offset)
+{
+ /* 100ns based time offset according to RFC 4122. 4.1.4. */
+ const uint64_t reg_offset = (((uint64_t) 0x01B21DD2) << 32) + 0x13814000;
+ static uint64_t last_clock_reg = 0;
+ uint64_t clock_reg;
+
+ if (last_clock_reg == 0)
+ last_clock_reg = get_clock_counter();
+
+ clock_reg = get_clock_counter();
+ if (max_clock_offset) {
+ uint64_t clock_offset = max_clock_offset * 10000000ULL;
+ if (last_clock_reg < (clock_reg - clock_offset))
+ last_clock_reg = clock_reg - clock_offset;
+ }
+
+ clock_reg += MAX_ADJUSTMENT;
+
+ if ((last_clock_reg + num) >= clock_reg)
+ return -1;
+
+ *clock_high = (last_clock_reg + reg_offset) >> 32;
+ *clock_low = last_clock_reg + reg_offset;
+ last_clock_reg += num;
+
+ return 0;
+}
+
#if defined(HAVE_UUIDD) && defined(HAVE_SYS_UN_H)
/*
@@ -403,7 +467,7 @@ static int get_uuid_via_daemon(int op __
}
#endif
-int __uuid_generate_time(uuid_t out, int *num)
+static int __uuid_generate_time_internal(uuid_t out, int *num, uint32_t cont_offset)
{
static unsigned char node_id[6];
static int has_init = 0;
@@ -423,7 +487,14 @@ int __uuid_generate_time(uuid_t out, int
}
has_init = 1;
}
- ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num);
+ if (cont_offset) {
+ ret = get_clock_cont(&clock_mid, &uu.time_low, *num, cont_offset);
+ uu.clock_seq = CLOCK_SEQ_CONT;
+ if (ret != 0) /* fallback to previous implpementation */
+ ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num);
+ } else {
+ ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num);
+ }
uu.clock_seq |= 0x8000;
uu.time_mid = (uint16_t) clock_mid;
uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000;
@@ -432,6 +503,16 @@ int __uuid_generate_time(uuid_t out, int
return ret;
}
+int __uuid_generate_time(uuid_t out, int *num)
+{
+ return __uuid_generate_time_internal(out, num, 0);
+}
+
+int __uuid_generate_time_cont(uuid_t out, int *num, uint32_t cont_offset)
+{
+ return __uuid_generate_time_internal(out, num, cont_offset);
+}
+
/*
* Generate time-based UUID and store it to @out
*
Index: util-linux-2.36.2/libuuid/src/libuuid.sym
===================================================================
--- util-linux-2.36.2.orig/libuuid/src/libuuid.sym
+++ util-linux-2.36.2/libuuid/src/libuuid.sym
@@ -60,6 +60,7 @@ global:
UUIDD_PRIVATE {
global:
__uuid_generate_time;
+ __uuid_generate_time_cont;
__uuid_generate_random;
local:
*;
Index: util-linux-2.36.2/libuuid/src/uuidd.h
===================================================================
--- util-linux-2.36.2.orig/libuuid/src/uuidd.h
+++ util-linux-2.36.2/libuuid/src/uuidd.h
@@ -49,6 +49,7 @@
#define UUIDD_MAX_OP UUIDD_OP_BULK_RANDOM_UUID
extern int __uuid_generate_time(uuid_t out, int *num);
+extern int __uuid_generate_time_cont(uuid_t out, int *num, uint32_t cont);
extern void __uuid_generate_random(uuid_t out, int *num);
#endif /* _UUID_UUID_H */
Index: util-linux-2.36.2/misc-utils/uuidd.c
===================================================================
--- util-linux-2.36.2.orig/misc-utils/uuidd.c
+++ util-linux-2.36.2/misc-utils/uuidd.c
@@ -49,6 +49,8 @@ struct uuidd_cxt_t {
const char *cleanup_pidfile;
const char *cleanup_socket;
uint32_t timeout;
+ uint32_t cont_clock_offset;
+
unsigned int debug: 1,
quiet: 1,
no_fork: 1,
@@ -73,6 +75,8 @@ static void __attribute__((__noreturn__)
fputs(_(" -P, --no-pid do not create pid file\n"), out);
fputs(_(" -F, --no-fork do not daemonize using double-fork\n"), out);
fputs(_(" -S, --socket-activation do not create listening socket\n"), out);
+ fputs(_(" -C, --cont-clock[=<NUM>[hd]]\n"), out);
+ fputs(_(" activate continuous clock handling\n"), out);
fputs(_(" -d, --debug run in debugging mode\n"), out);
fputs(_(" -q, --quiet turn on quiet mode\n"), out);
fputs(USAGE_SEPARATOR, out);
@@ -404,6 +408,15 @@ static void server_loop(const char *sock
pfd[POLLFD_SOCKET].fd = s;
pfd[POLLFD_SIGNAL].events = pfd[POLLFD_SOCKET].events = POLLIN | POLLERR | POLLHUP;
+ num = 1;
+ if (uuidd_cxt->cont_clock_offset) {
+ /* trigger initialization */
+ (void) __uuid_generate_time_cont(uu, &num, uuidd_cxt->cont_clock_offset);
+ if (uuidd_cxt->debug)
+ fprintf(stderr, _("max_clock_offset = %u sec\n"),
+ uuidd_cxt->cont_clock_offset);
+ }
+
while (1) {
ret = poll(pfd, ARRAY_SIZE(pfd),
uuidd_cxt->timeout ?
@@ -460,7 +473,8 @@ static void server_loop(const char *sock
break;
case UUIDD_OP_TIME_UUID:
num = 1;
- if (__uuid_generate_time(uu, &num) < 0 && !uuidd_cxt->quiet)
+ ret = __uuid_generate_time_cont(uu, &num, uuidd_cxt->cont_clock_offset);
+ if (ret < 0 && !uuidd_cxt->quiet)
warnx(_("failed to open/lock clock counter"));
if (uuidd_cxt->debug) {
uuid_unparse(uu, str);
@@ -471,7 +485,8 @@ static void server_loop(const char *sock
break;
case UUIDD_OP_RANDOM_UUID:
num = 1;
- if (__uuid_generate_time(uu, &num) < 0 && !uuidd_cxt->quiet)
+ ret = __uuid_generate_time_cont(uu, &num, uuidd_cxt->cont_clock_offset);
+ if (ret < 0 && !uuidd_cxt->quiet)
warnx(_("failed to open/lock clock counter"));
if (uuidd_cxt->debug) {
uuid_unparse(uu, str);
@@ -481,7 +496,8 @@ static void server_loop(const char *sock
reply_len = sizeof(uu);
break;
case UUIDD_OP_BULK_TIME_UUID:
- if (__uuid_generate_time(uu, &num) < 0 && !uuidd_cxt->quiet)
+ ret = __uuid_generate_time_cont(uu, &num, uuidd_cxt->cont_clock_offset);
+ if (ret < 0 && !uuidd_cxt->quiet)
warnx(_("failed to open/lock clock counter"));
if (uuidd_cxt->debug) {
uuid_unparse(uu, str);
@@ -535,6 +551,27 @@ static void __attribute__ ((__noreturn__
errx(EXIT_FAILURE, _("Unexpected reply length from server %d"), size);
}
+static uint32_t parse_cont_clock(char *arg)
+{
+ uint32_t min_val = 60,
+ max_val = (3600 * 24 * 365),
+ factor = 1;
+ char *p = &arg[strlen(arg)-1];
+
+ if ('h' == *p) {
+ *p = '\0';
+ factor = 3600;
+ min_val = 1;
+ }
+ if ('d' == *p) {
+ *p = '\0';
+ factor = 24 * 3600;
+ min_val = 1;
+ }
+ return factor * str2num_or_err(optarg, 10, _("failed to parse --cont-clock/-C"),
+ min_val, max_val / factor);
+}
+
int main(int argc, char **argv)
{
const char *socket_path = UUIDD_SOCKET_PATH;
@@ -548,7 +585,7 @@ int main(int argc, char **argv)
int no_pid = 0;
int s_flag = 0;
- struct uuidd_cxt_t uuidd_cxt = { .timeout = 0 };
+ struct uuidd_cxt_t uuidd_cxt = { .timeout = 0, .cont_clock_offset = 0 };
static const struct option longopts[] = {
{"pid", required_argument, NULL, 'p'},
@@ -561,6 +598,7 @@ int main(int argc, char **argv)
{"no-pid", no_argument, NULL, 'P'},
{"no-fork", no_argument, NULL, 'F'},
{"socket-activation", no_argument, NULL, 'S'},
+ {"cont-clock", optional_argument, NULL, 'C'},
{"debug", no_argument, NULL, 'd'},
{"quiet", no_argument, NULL, 'q'},
{"version", no_argument, NULL, 'V'},
@@ -581,10 +619,16 @@ int main(int argc, char **argv)
close_stdout_atexit();
while ((c =
- getopt_long(argc, argv, "p:s:T:krtn:PFSdqVh", longopts,
+ getopt_long(argc, argv, "p:s:T:krtn:PFSC::dqVh", longopts,
NULL)) != -1) {
err_exclusive_options(c, longopts, excl, excl_st);
switch (c) {
+ case 'C':
+ if (optarg != NULL)
+ uuidd_cxt.cont_clock_offset = parse_cont_clock(optarg);
+ else
+ uuidd_cxt.cont_clock_offset = 7200; /* default 2h */
+ break;
case 'd':
uuidd_cxt.debug = 1;
break;
Index: util-linux-2.36.2/misc-utils/uuidd.8.in
===================================================================
--- util-linux-2.36.2.orig/misc-utils/uuidd.8.in
+++ util-linux-2.36.2/misc-utils/uuidd.8.in
@@ -17,6 +17,18 @@ in a secure and guaranteed-unique fashio
numbers of threads running on different CPUs trying to grab UUIDs.
.SH OPTIONS
.TP
+.BR \-C , " \-\-cont-clock" \fIopt_arg\fR
+Activate continuous clock handling for time based UUIDs. uuidd could use all
+possible clock values, beginning with the daemon's start time. The optional
+argument can be used to set a value for the max_clock_offset. This
+gurantees, that a clock value of a UUID will always be within the range of
+the max_clock_offset. \fB-C\fR or \fB\-\-cont\-clock\fR enables the feature
+with a default max_clock_offset of 2 hours. \fB\-C<NUM>[hd]\fR or
+\fB\-\-cont\-clock=<NUM>[hd]\fR enables the feature with a max_clock_offset
+of NUM seconds. In case of an appended h or d, the NUM value is read in
+hours or days. The minimum value is 60 seconds, the maximum value is 365
+days.
+.TP
.BR \-d , " \-\-debug"
Run uuidd in debugging mode. This prevents uuidd from running as a daemon.
.TP
Index: util-linux-2.36.2/misc-utils/uuidd.8
===================================================================
--- util-linux-2.36.2.orig/misc-utils/uuidd.8
+++ util-linux-2.36.2/misc-utils/uuidd.8
@@ -17,6 +17,18 @@ in a secure and guaranteed-unique fashio
numbers of threads running on different CPUs trying to grab UUIDs.
.SH OPTIONS
.TP
+.BR \-C , " \-\-cont-clock" \fIopt_arg\fR
+Activate continuous clock handling for time based UUIDs. uuidd could use all
+possible clock values, beginning with the daemon's start time. The optional
+argument can be used to set a value for the max_clock_offset. This
+gurantees, that a clock value of a UUID will always be within the range of
+the max_clock_offset. \fB-C\fR or \fB\-\-cont\-clock\fR enables the feature
+with a default max_clock_offset of 2 hours. \fB\-C<NUM>[hd]\fR or
+\fB\-\-cont\-clock=<NUM>[hd]\fR enables the feature with a max_clock_offset
+of NUM seconds. In case of an appended h or d, the NUM value is read in
+hours or days. The minimum value is 60 seconds, the maximum value is 365
+days.
+.TP
.BR \-d , " \-\-debug"
Run uuidd in debugging mode. This prevents uuidd from running as a daemon.
.TP
Index: util-linux-2.36.2/include/strutils.h
===================================================================
--- util-linux-2.36.2.orig/include/strutils.h
+++ util-linux-2.36.2/include/strutils.h
@@ -24,6 +24,8 @@ extern int32_t strtos32_or_err(const cha
extern uint32_t strtou32_or_err(const char *str, const char *errmesg);
extern uint32_t strtox32_or_err(const char *str, const char *errmesg);
+extern int64_t str2num_or_err(const char *str, int base, const char *errmesg, int64_t low, int64_t up);
+
extern int64_t strtos64_or_err(const char *str, const char *errmesg);
extern uint64_t strtou64_or_err(const char *str, const char *errmesg);
extern uint64_t strtox64_or_err(const char *str, const char *errmesg);
Index: util-linux-2.36.2/lib/strutils.c
===================================================================
--- util-linux-2.36.2.orig/lib/strutils.c
+++ util-linux-2.36.2/lib/strutils.c
@@ -322,6 +322,48 @@ char *strndup(const char *s, size_t n)
static uint32_t _strtou32_or_err(const char *str, const char *errmesg, int base);
static uint64_t _strtou64_or_err(const char *str, const char *errmesg, int base);
+/*
+ * convert strings to numbers; returns <0 on error, and 0 on success
+ */
+int ul_strtos64(const char *str, int64_t *num, int base)
+{
+ char *end = NULL;
+
+ errno = 0;
+ if (str == NULL || *str == '\0')
+ return -EINVAL;
+ *num = (int64_t) strtoimax(str, &end, base);
+
+ if (errno || str == end || (end && *end))
+ return -EINVAL;
+ return 0;
+}
+
+/*
+ * Covert strings to numbers in defined range and print message on error.
+ *
+ * These functions are used when we read input from users (getopt() etc.). It's
+ * better to consolidate the code and keep it all based on 64-bit numbers then
+ * implement it for 32 and 16-bit numbers too.
+ */
+int64_t str2num_or_err(const char *str, int base, const char *errmesg,
+ int64_t low, int64_t up)
+{
+ int64_t num = 0;
+ int rc;
+
+ rc = ul_strtos64(str, &num, base);
+ if (rc == 0 && ((low && num < low) || (up && num > up)))
+ rc = -(errno = ERANGE);
+
+ if (rc) {
+ if (errno == ERANGE)
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+ }
+ return num;
+}
+
int16_t strtos16_or_err(const char *str, const char *errmesg)
{
int32_t num = strtos32_or_err(str, errmesg);