Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15-SP2:GA
util-linux
util-linux-libuuid-continuous-clock-handling.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File util-linux-libuuid-continuous-clock-handling.patch of Package util-linux
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.33.2/libuuid/src/gen_uuid.c =================================================================== --- util-linux-2.33.2.orig/libuuid/src/gen_uuid.c +++ util-linux-2.33.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)) { - random_get_bytes(&clock_seq, sizeof(clock_seq)); - clock_seq &= 0x3FFF; + do { + 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.33.2/libuuid/src/libuuid.sym =================================================================== --- util-linux-2.33.2.orig/libuuid/src/libuuid.sym +++ util-linux-2.33.2/libuuid/src/libuuid.sym @@ -51,6 +51,7 @@ global: UUIDD_PRIVATE { global: __uuid_generate_time; + __uuid_generate_time_cont; __uuid_generate_random; local: *; Index: util-linux-2.33.2/libuuid/src/uuidd.h =================================================================== --- util-linux-2.33.2.orig/libuuid/src/uuidd.h +++ util-linux-2.33.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.33.2/misc-utils/uuidd.c =================================================================== --- util-linux-2.33.2.orig/misc-utils/uuidd.c +++ util-linux-2.33.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); @@ -401,6 +405,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 ? @@ -458,7 +471,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); @@ -469,7 +483,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); @@ -479,7 +494,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); @@ -533,6 +549,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; @@ -546,7 +583,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'}, @@ -559,6 +596,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'}, @@ -579,10 +617,16 @@ int main(int argc, char **argv) atexit(close_stdout); 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.33.2/misc-utils/uuidd.8.in =================================================================== --- util-linux-2.33.2.orig/misc-utils/uuidd.8.in +++ util-linux-2.33.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.33.2/misc-utils/uuidd.8 =================================================================== --- util-linux-2.33.2.orig/misc-utils/uuidd.8 +++ util-linux-2.33.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.33.2/include/strutils.h =================================================================== --- util-linux-2.33.2.orig/include/strutils.h +++ util-linux-2.33.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.33.2/lib/strutils.c =================================================================== --- util-linux-2.33.2.orig/lib/strutils.c +++ util-linux-2.33.2/lib/strutils.c @@ -280,6 +280,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);
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor