93a10f
From ccc5db8102728d37e2e28dd50da3370e8c8de33a Mon Sep 17 00:00:00 2001
93a10f
From: Michael Trapp <michael.trapp@sap.com>
93a10f
Date: Mon, 20 Jun 2022 17:10:36 +0200
93a10f
Subject: libuuid: Implement continuous clock handling for time based UUIDs
93a10f
93a10f
In a uuidd setup, the daemon is a singleton and can maintain it's own
93a10f
resources for time based UUID generation. This requires a dedicated
93a10f
'clock sequence range' but does not need any further lock/update of
93a10f
the LIBUUID_CLOCK_FILE from uuidd. The range of available clock values
93a10f
is extended by a continuous handling of the clock updates - instead of
93a10f
updating the value to the current timestamp, it is incremented by
93a10f
the number of requested UUIDs.
93a10f
93a10f
[kzak@redhat.com: - backport from upstream v2.39 to to RHEL-8]
93a10f
93a10f
Upstream: http://github.com/util-linux/util-linux/commit/3cfba7d39b66eff4307218fefd8bb34bb1621f83
93a10f
Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=2141969
93a10f
Signed-off-by: Karel Zak <kzak@redhat.com>
93a10f
---
93a10f
 libuuid/src/gen_uuid.c  | 91 ++++++++++++++++++++++++++++++++++++++---
93a10f
 libuuid/src/libuuid.sym |  1 +
93a10f
 libuuid/src/uuidd.h     |  1 +
93a10f
 misc-utils/uuidd.8.in   | 12 ++++++
93a10f
 misc-utils/uuidd.c      | 86 ++++++++++++++++++++++++++++++++++++--
93a10f
 5 files changed, 182 insertions(+), 9 deletions(-)
93a10f
93a10f
diff --git a/libuuid/src/gen_uuid.c b/libuuid/src/gen_uuid.c
93a10f
index 27c135db5..f557053f7 100644
93a10f
--- a/libuuid/src/gen_uuid.c
93a10f
+++ b/libuuid/src/gen_uuid.c
93a10f
@@ -209,6 +209,8 @@ static int get_node_id(unsigned char *node_id)
93a10f
 
93a10f
 /* Assume that the gettimeofday() has microsecond granularity */
93a10f
 #define MAX_ADJUSTMENT 10
93a10f
+/* Reserve a clock_seq value for the 'continuous clock' implementation */
93a10f
+#define CLOCK_SEQ_CONT 0
93a10f
 
93a10f
 /*
93a10f
  * Get clock from global sequence clock counter.
93a10f
@@ -275,8 +277,10 @@ static int get_clock(uint32_t *clock_high, uint32_t *clock_low,
93a10f
 	}
93a10f
 
93a10f
 	if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
93a10f
-		random_get_bytes(&clock_seq, sizeof(clock_seq));
93a10f
-		clock_seq &= 0x3FFF;
93a10f
+		do {
93a10f
+			random_get_bytes(&clock_seq, sizeof(clock_seq));
93a10f
+			clock_seq &= 0x3FFF;
93a10f
+		} while (clock_seq == CLOCK_SEQ_CONT);
93a10f
 		gettimeofday(&last, NULL);
93a10f
 		last.tv_sec--;
93a10f
 	}
93a10f
@@ -286,7 +290,9 @@ try_again:
93a10f
 	if ((tv.tv_sec < last.tv_sec) ||
93a10f
 	    ((tv.tv_sec == last.tv_sec) &&
93a10f
 	     (tv.tv_usec < last.tv_usec))) {
93a10f
-		clock_seq = (clock_seq+1) & 0x3FFF;
93a10f
+		do {
93a10f
+			clock_seq = (clock_seq+1) & 0x3FFF;
93a10f
+		} while (clock_seq == CLOCK_SEQ_CONT);
93a10f
 		adjustment = 0;
93a10f
 		last = tv;
93a10f
 	} else if ((tv.tv_sec == last.tv_sec) &&
93a10f
@@ -331,6 +337,64 @@ try_again:
93a10f
 	return ret;
93a10f
 }
93a10f
 
93a10f
+/*
93a10f
+ * Get current time in 100ns ticks.
93a10f
+ */
93a10f
+static uint64_t get_clock_counter(void)
93a10f
+{
93a10f
+	struct timeval tv;
93a10f
+	uint64_t clock_reg;
93a10f
+
93a10f
+	gettimeofday(&tv, NULL);
93a10f
+	clock_reg = tv.tv_usec*10;
93a10f
+	clock_reg += ((uint64_t) tv.tv_sec) * 10000000ULL;
93a10f
+
93a10f
+	return clock_reg;
93a10f
+}
93a10f
+
93a10f
+/*
93a10f
+ * Get continuous clock value.
93a10f
+ *
93a10f
+ * Return -1 if there is no further clock counter available,
93a10f
+ * otherwise return 0.
93a10f
+ *
93a10f
+ * This implementation doesn't deliver clock counters based on
93a10f
+ * the current time because last_clock_reg is only incremented
93a10f
+ * by the number of requested UUIDs.
93a10f
+ * max_clock_offset is used to limit the offset of last_clock_reg.
93a10f
+ */
93a10f
+static int get_clock_cont(uint32_t *clock_high,
93a10f
+			  uint32_t *clock_low,
93a10f
+			  int num,
93a10f
+			  uint32_t max_clock_offset)
93a10f
+{
93a10f
+	/* 100ns based time offset according to RFC 4122. 4.1.4. */
93a10f
+	const uint64_t reg_offset = (((uint64_t) 0x01B21DD2) << 32) + 0x13814000;
93a10f
+	static uint64_t last_clock_reg = 0;
93a10f
+	uint64_t clock_reg;
93a10f
+
93a10f
+	if (last_clock_reg == 0)
93a10f
+		last_clock_reg = get_clock_counter();
93a10f
+
93a10f
+	clock_reg = get_clock_counter();
93a10f
+	if (max_clock_offset) {
93a10f
+		uint64_t clock_offset = max_clock_offset * 10000000ULL;
93a10f
+		if (last_clock_reg < (clock_reg - clock_offset))
93a10f
+			last_clock_reg = clock_reg - clock_offset;
93a10f
+	}
93a10f
+
93a10f
+	clock_reg += MAX_ADJUSTMENT;
93a10f
+
93a10f
+	if ((last_clock_reg + num) >= clock_reg)
93a10f
+		return -1;
93a10f
+
93a10f
+	*clock_high = (last_clock_reg + reg_offset) >> 32;
93a10f
+	*clock_low = last_clock_reg + reg_offset;
93a10f
+	last_clock_reg += num;
93a10f
+
93a10f
+	return 0;
93a10f
+}
93a10f
+
93a10f
 #if defined(HAVE_UUIDD) && defined(HAVE_SYS_UN_H)
93a10f
 
93a10f
 /*
93a10f
@@ -403,7 +467,7 @@ static int get_uuid_via_daemon(int op __attribute__((__unused__)),
93a10f
 }
93a10f
 #endif
93a10f
 
93a10f
-int __uuid_generate_time(uuid_t out, int *num)
93a10f
+static int __uuid_generate_time_internal(uuid_t out, int *num, uint32_t cont_offset)
93a10f
 {
93a10f
 	static unsigned char node_id[6];
93a10f
 	static int has_init = 0;
93a10f
@@ -423,7 +487,14 @@ int __uuid_generate_time(uuid_t out, int *num)
93a10f
 		}
93a10f
 		has_init = 1;
93a10f
 	}
93a10f
-	ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num);
93a10f
+	if (cont_offset) {
93a10f
+		ret = get_clock_cont(&clock_mid, &uu.time_low, *num, cont_offset);
93a10f
+		uu.clock_seq = CLOCK_SEQ_CONT;
93a10f
+		if (ret != 0)	/* fallback to previous implpementation */
93a10f
+			ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num);
93a10f
+	} else {
93a10f
+		ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num);
93a10f
+	}
93a10f
 	uu.clock_seq |= 0x8000;
93a10f
 	uu.time_mid = (uint16_t) clock_mid;
93a10f
 	uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000;
93a10f
@@ -432,6 +503,16 @@ int __uuid_generate_time(uuid_t out, int *num)
93a10f
 	return ret;
93a10f
 }
93a10f
 
93a10f
+int __uuid_generate_time(uuid_t out, int *num)
93a10f
+{
93a10f
+	return __uuid_generate_time_internal(out, num, 0);
93a10f
+}
93a10f
+
93a10f
+int __uuid_generate_time_cont(uuid_t out, int *num, uint32_t cont_offset)
93a10f
+{
93a10f
+	return __uuid_generate_time_internal(out, num, cont_offset);
93a10f
+}
93a10f
+
93a10f
 /*
93a10f
  * Generate time-based UUID and store it to @out
93a10f
  *
93a10f
diff --git a/libuuid/src/libuuid.sym b/libuuid/src/libuuid.sym
93a10f
index 9e3e80035..0a072b703 100644
93a10f
--- a/libuuid/src/libuuid.sym
93a10f
+++ b/libuuid/src/libuuid.sym
93a10f
@@ -51,6 +51,7 @@ global:
93a10f
 UUIDD_PRIVATE {
93a10f
 global:
93a10f
 	__uuid_generate_time;
93a10f
+	__uuid_generate_time_cont;
93a10f
 	__uuid_generate_random;
93a10f
 local:
93a10f
 	*;
93a10f
diff --git a/libuuid/src/uuidd.h b/libuuid/src/uuidd.h
93a10f
index e55c86f2f..14a01ade2 100644
93a10f
--- a/libuuid/src/uuidd.h
93a10f
+++ b/libuuid/src/uuidd.h
93a10f
@@ -50,5 +50,6 @@
93a10f
 
93a10f
 extern int __uuid_generate_time(uuid_t out, int *num);
93a10f
 extern void __uuid_generate_random(uuid_t out, int *num);
93a10f
+extern int __uuid_generate_time_cont(uuid_t out, int *num, uint32_t cont);
93a10f
 
93a10f
 #endif /* _UUID_UUID_H */
93a10f
diff --git a/misc-utils/uuidd.8.in b/misc-utils/uuidd.8.in
93a10f
index 0a5cf471b..28bcb48b5 100644
93a10f
--- a/misc-utils/uuidd.8.in
93a10f
+++ b/misc-utils/uuidd.8.in
93a10f
@@ -16,6 +16,18 @@ universally unique identifiers (UUIDs), especially time-based UUIDs,
93a10f
 in a secure and guaranteed-unique fashion, even in the face of large
93a10f
 numbers of threads running on different CPUs trying to grab UUIDs.
93a10f
 .SH OPTIONS
93a10f
+
93a10f
+.TP
93a10f
+.BR \-C , " \-\-cont\-clock " [\fInumber\fR]
93a10f
+Activate continuous clock handling for time based UUIDs. uuidd could use all
93a10f
+possible clock values, beginning with the daemon's start time. The optional
93a10f
+argument can be used to set a value for the max_clock_offset. This gurantees,
93a10f
+that a clock value of a UUID will always be within the range of the
93a10f
+max_clock_offset. '-C' or '--cont-clock' enables the feature with a default
93a10f
+max_clock_offset of 2 hours. '-C<NUM>[hd]' or '--cont-clock=<NUM>[hd]' enables
93a10f
+the feature with a max_clock_offset of NUM seconds. In case of an appended h or
93a10f
+d, the NUM value is read in hours or days. The minimum value is 60 seconds, the
93a10f
+maximum value is 365 days.
93a10f
 .TP
93a10f
 .BR \-d , " \-\-debug "
93a10f
 Run uuidd in debugging mode.  This prevents uuidd from running as a daemon.
93a10f
diff --git a/misc-utils/uuidd.c b/misc-utils/uuidd.c
93a10f
index 8b83d91c0..e3c0abad7 100644
93a10f
--- a/misc-utils/uuidd.c
93a10f
+++ b/misc-utils/uuidd.c
93a10f
@@ -49,6 +49,8 @@ struct uuidd_cxt_t {
93a10f
 	const char	*cleanup_pidfile;
93a10f
 	const char	*cleanup_socket;
93a10f
 	uint32_t	timeout;
93a10f
+	uint32_t	cont_clock_offset;
93a10f
+
93a10f
 	unsigned int	debug: 1,
93a10f
 			quiet: 1,
93a10f
 			no_fork: 1,
93a10f
@@ -73,6 +75,8 @@ static void __attribute__((__noreturn__)) usage(void)
93a10f
 	fputs(_(" -P, --no-pid            do not create pid file\n"), out);
93a10f
 	fputs(_(" -F, --no-fork           do not daemonize using double-fork\n"), out);
93a10f
 	fputs(_(" -S, --socket-activation do not create listening socket\n"), out);
93a10f
+	fputs(_(" -C, --cont-clock[=<NUM>[hd]]\n"), out);
93a10f
+	fputs(_("                         activate continuous clock handling\n"), out);
93a10f
 	fputs(_(" -d, --debug             run in debugging mode\n"), out);
93a10f
 	fputs(_(" -q, --quiet             turn on quiet mode\n"), out);
93a10f
 	fputs(USAGE_SEPARATOR, out);
93a10f
@@ -401,6 +405,15 @@ static void server_loop(const char *socket_path, const char *pidfile_path,
93a10f
 	pfd[POLLFD_SOCKET].fd = s;
93a10f
 	pfd[POLLFD_SIGNAL].events = pfd[POLLFD_SOCKET].events = POLLIN | POLLERR | POLLHUP;
93a10f
 
93a10f
+	num = 1;
93a10f
+	if (uuidd_cxt->cont_clock_offset) {
93a10f
+		/* trigger initialization */
93a10f
+		(void) __uuid_generate_time_cont(uu, &num, uuidd_cxt->cont_clock_offset);
93a10f
+		if (uuidd_cxt->debug)
93a10f
+			fprintf(stderr, _("max_clock_offset = %u sec\n"),
93a10f
+				uuidd_cxt->cont_clock_offset);
93a10f
+	}
93a10f
+
93a10f
 	while (1) {
93a10f
 		ret = poll(pfd, ARRAY_SIZE(pfd),
93a10f
 				uuidd_cxt->timeout ?
93a10f
@@ -458,7 +471,7 @@ static void server_loop(const char *socket_path, const char *pidfile_path,
93a10f
 			break;
93a10f
 		case UUIDD_OP_TIME_UUID:
93a10f
 			num = 1;
93a10f
-			__uuid_generate_time(uu, &num);
93a10f
+			__uuid_generate_time_cont(uu, &num, uuidd_cxt->cont_clock_offset);
93a10f
 			if (uuidd_cxt->debug) {
93a10f
 				uuid_unparse(uu, str);
93a10f
 				fprintf(stderr, _("Generated time UUID: %s\n"), str);
93a10f
@@ -477,7 +490,7 @@ static void server_loop(const char *socket_path, const char *pidfile_path,
93a10f
 			reply_len = sizeof(uu);
93a10f
 			break;
93a10f
 		case UUIDD_OP_BULK_TIME_UUID:
93a10f
-			__uuid_generate_time(uu, &num);
93a10f
+			 __uuid_generate_time_cont(uu, &num, uuidd_cxt->cont_clock_offset);
93a10f
 			if (uuidd_cxt->debug) {
93a10f
 				uuid_unparse(uu, str);
93a10f
 				fprintf(stderr, P_("Generated time UUID %s "
93a10f
@@ -530,6 +543,64 @@ static void __attribute__ ((__noreturn__)) unexpected_size(int size)
93a10f
 	errx(EXIT_FAILURE, _("Unexpected reply length from server %d"), size);
93a10f
 }
93a10f
 
93a10f
+/* Backport from v2.39 lib/strutils.c */
93a10f
+static int ul_strtos64(const char *str, int64_t *num, int base)
93a10f
+{
93a10f
+	char *end = NULL;
93a10f
+
93a10f
+	if (str == NULL || *str == '\0')
93a10f
+		return -(errno = EINVAL);
93a10f
+
93a10f
+	errno = 0;
93a10f
+	*num = (int64_t) strtoimax(str, &end, base);
93a10f
+
93a10f
+	if (errno != 0)
93a10f
+		return -errno;
93a10f
+	if (str == end || (end && *end))
93a10f
+		return -(errno = EINVAL);
93a10f
+	return 0;
93a10f
+}
93a10f
+
93a10f
+/* Backport from v2.39 lib/strutils.c */
93a10f
+static int64_t str2num_or_err(const char *str, int base, const char *errmesg,
93a10f
+			     int64_t low, int64_t up)
93a10f
+{
93a10f
+	int64_t num = 0;
93a10f
+	int rc;
93a10f
+
93a10f
+	rc = ul_strtos64(str, &num, base);
93a10f
+	if (rc == 0 && ((low && num < low) || (up && num > up)))
93a10f
+		rc = -(errno = ERANGE);
93a10f
+
93a10f
+	if (rc) {
93a10f
+		if (errno == ERANGE)
93a10f
+			err(EXIT_FAILURE, "%s: '%s'", errmesg, str);
93a10f
+		errx(EXIT_FAILURE, "%s: '%s'", errmesg, str);
93a10f
+	}
93a10f
+	return num;
93a10f
+}
93a10f
+
93a10f
+static uint32_t parse_cont_clock(char *arg)
93a10f
+{
93a10f
+	uint32_t min_val = 60,
93a10f
+		 max_val = (3600 * 24 * 365),
93a10f
+		 factor = 1;
93a10f
+	char *p = &arg[strlen(arg)-1];
93a10f
+
93a10f
+	if ('h' == *p) {
93a10f
+		*p = '\0';
93a10f
+		factor = 3600;
93a10f
+		min_val = 1;
93a10f
+	}
93a10f
+	if ('d' == *p) {
93a10f
+		*p = '\0';
93a10f
+		factor = 24 * 3600;
93a10f
+		min_val = 1;
93a10f
+	}
93a10f
+	return factor * str2num_or_err(optarg, 10, _("failed to parse --cont-clock/-C"),
93a10f
+				       min_val, max_val / factor);
93a10f
+}
93a10f
+
93a10f
 int main(int argc, char **argv)
93a10f
 {
93a10f
 	const char	*socket_path = UUIDD_SOCKET_PATH;
93a10f
@@ -543,7 +614,7 @@ int main(int argc, char **argv)
93a10f
 	int		no_pid = 0;
93a10f
 	int		s_flag = 0;
93a10f
 
93a10f
-	struct uuidd_cxt_t uuidd_cxt = { .timeout = 0 };
93a10f
+	struct uuidd_cxt_t uuidd_cxt = { .timeout = 0, .cont_clock_offset = 0 };
93a10f
 
93a10f
 	static const struct option longopts[] = {
93a10f
 		{"pid", required_argument, NULL, 'p'},
93a10f
@@ -556,6 +627,7 @@ int main(int argc, char **argv)
93a10f
 		{"no-pid", no_argument, NULL, 'P'},
93a10f
 		{"no-fork", no_argument, NULL, 'F'},
93a10f
 		{"socket-activation", no_argument, NULL, 'S'},
93a10f
+		{"cont-clock", optional_argument, NULL, 'C'},
93a10f
 		{"debug", no_argument, NULL, 'd'},
93a10f
 		{"quiet", no_argument, NULL, 'q'},
93a10f
 		{"version", no_argument, NULL, 'V'},
93a10f
@@ -576,10 +648,16 @@ int main(int argc, char **argv)
93a10f
 	atexit(close_stdout);
93a10f
 
93a10f
 	while ((c =
93a10f
-		getopt_long(argc, argv, "p:s:T:krtn:PFSdqVh", longopts,
93a10f
+		getopt_long(argc, argv, "p:s:T:krtn:PFSC::dqVh", longopts,
93a10f
 			    NULL)) != -1) {
93a10f
 		err_exclusive_options(c, longopts, excl, excl_st);
93a10f
 		switch (c) {
93a10f
+		case 'C':
93a10f
+			if (optarg != NULL)
93a10f
+				uuidd_cxt.cont_clock_offset = parse_cont_clock(optarg);
93a10f
+			else
93a10f
+				uuidd_cxt.cont_clock_offset = 7200; /* default 2h */
93a10f
+			break;
93a10f
 		case 'd':
93a10f
 			uuidd_cxt.debug = 1;
93a10f
 			break;
93a10f
-- 
93a10f
2.38.1
93a10f