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 clock update - instead of updating the value to the current timestamp, it is incremented by the number of requested UUIDs. --- Here is the first implementation of a continuous clock for time based UUIDs. Of course, there are pros + cons to have the current time in the timestamp - which needs be discussed, but in some use cases it's not relevant at all. The feature has to be enabled with '-C' and can be checked with # uuidgen -t d1ff28e1-c21a-11ec-8000-f80f41fc650a # uuidgen -t d1ff28eb-c21a-11ec-8000-f80f41fc650a which shows an increment of 10 UUIDs for each request (due to the cache in libuuid). libuuid/src/gen_uuid.c | 84 ++++++++++++++++++++++++++++++++++++++--- libuuid/src/libuuid.sym | 1 + libuuid/src/uuidd.h | 1 + misc-utils/uuidd.8.adoc | 3 ++ misc-utils/uuidd.c | 32 +++++++++++++--- 5 files changed, 111 insertions(+), 10 deletions(-) diff --git a/libuuid/src/gen_uuid.c b/libuuid/src/gen_uuid.c index 805b40d90..cb6ceb50e 100644 --- a/libuuid/src/gen_uuid.c +++ b/libuuid/src/gen_uuid.c @@ -209,6 +209,8 @@ static int get_node_id(unsigned char *node_id) /* 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_high, uint32_t *clock_low, } 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,56 @@ 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)*10000000; + + 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 the last clock counter is only + * incremented by the number of requested UUIDs. + */ +static int get_clock_cont(uint32_t *clock_high, + uint32_t *clock_low, + int num) +{ + /* 100ns based time offset according to RFC 4122. 4.1.4. */ + const uint64_t reg_offset = (((uint64_t) 0x01B21DD2) << 32) + 0x13814000; + THREAD_LOCAL 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(); + 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 +459,8 @@ static int get_uuid_via_daemon(int op __attribute__((__unused__)), } #endif -int __uuid_generate_time(uuid_t out, int *num) +static int __uuid_generate_time_internal(uuid_t out, int *num, + int cont) { static unsigned char node_id[6]; static int has_init = 0; @@ -423,7 +480,14 @@ int __uuid_generate_time(uuid_t out, int *num) } has_init = 1; } - ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num); + if (cont) { + ret = get_clock_cont(&clock_mid, &uu.time_low, *num); + uu.clock_seq = CLOCK_SEQ_CONT; + if (ret != 0) /* falback 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 +496,16 @@ int __uuid_generate_time(uuid_t out, int *num) 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) +{ + return __uuid_generate_time_internal(out, num, 1); +} + /* * Generate time-based UUID and store it to @out * diff --git a/libuuid/src/libuuid.sym b/libuuid/src/libuuid.sym index 342453368..96372a857 100644 --- a/libuuid/src/libuuid.sym +++ b/libuuid/src/libuuid.sym @@ -60,6 +60,7 @@ global: UUIDD_PRIVATE { global: __uuid_generate_time; + __uuid_generate_time_cont; __uuid_generate_random; local: *; diff --git a/libuuid/src/uuidd.h b/libuuid/src/uuidd.h index fbe821ff3..1557bb6ca 100644 --- a/libuuid/src/uuidd.h +++ b/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); extern int __uuid_generate_random(uuid_t out, int *num); #endif /* _UUID_UUID_H */ diff --git a/misc-utils/uuidd.8.adoc b/misc-utils/uuidd.8.adoc index 49e7b6359..07c01c20b 100644 --- a/misc-utils/uuidd.8.adoc +++ b/misc-utils/uuidd.8.adoc @@ -24,6 +24,9 @@ The *uuidd* daemon is used by the UUID library to generate universally unique id == OPTIONS +*-C*, *--cont-clock*:: +Activate continuous clock handling for time based UUIDs. *uuidd* uses all possible clock values, beginning with the daemon's start time. + *-d*, *--debug*:: Run *uuidd* in debugging mode. This prevents *uuidd* from running as a daemon. diff --git a/misc-utils/uuidd.c b/misc-utils/uuidd.c index dfcd1487b..51e4e78a4 100644 --- a/misc-utils/uuidd.c +++ b/misc-utils/uuidd.c @@ -75,7 +75,8 @@ struct uuidd_cxt_t { unsigned int debug: 1, quiet: 1, no_fork: 1, - no_sock: 1; + no_sock: 1, + cont_clock:1; }; struct uuidd_options_t { @@ -106,6 +107,7 @@ static void __attribute__((__noreturn__)) usage(void) 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 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); @@ -438,6 +440,10 @@ static void server_loop(const char *socket_path, const char *pidfile_path, pfd[POLLFD_SOCKET].fd = s; pfd[POLLFD_SIGNAL].events = pfd[POLLFD_SOCKET].events = POLLIN | POLLERR | POLLHUP; + num = 1; + /* trigger initialization */ + (void) __uuid_generate_time_cont(uu, &num); + while (1) { ret = poll(pfd, ARRAY_SIZE(pfd), uuidd_cxt->timeout ? @@ -494,7 +500,11 @@ static void server_loop(const char *socket_path, const char *pidfile_path, break; case UUIDD_OP_TIME_UUID: num = 1; - if (__uuid_generate_time(uu, &num) < 0 && !uuidd_cxt->quiet) + if (uuidd_cxt->cont_clock) + ret = __uuid_generate_time_cont(uu, &num); + else + ret = __uuid_generate_time(uu, &num); + if (ret < 0 && !uuidd_cxt->quiet) warnx(_("failed to open/lock clock counter")); if (uuidd_cxt->debug) { uuid_unparse(uu, str); @@ -505,7 +515,11 @@ static void server_loop(const char *socket_path, const char *pidfile_path, break; case UUIDD_OP_RANDOM_UUID: num = 1; - if (__uuid_generate_time(uu, &num) < 0 && !uuidd_cxt->quiet) + if (uuidd_cxt->cont_clock) + ret = __uuid_generate_time_cont(uu, &num); + else + ret = __uuid_generate_time(uu, &num); + if (ret < 0 && !uuidd_cxt->quiet) warnx(_("failed to open/lock clock counter")); if (uuidd_cxt->debug) { uuid_unparse(uu, str); @@ -515,7 +529,11 @@ static void server_loop(const char *socket_path, const char *pidfile_path, reply_len = sizeof(uu); break; case UUIDD_OP_BULK_TIME_UUID: - if (__uuid_generate_time(uu, &num) < 0 && !uuidd_cxt->quiet) + if (uuidd_cxt->cont_clock) + ret = __uuid_generate_time_cont(uu, &num); + else + ret = __uuid_generate_time(uu, &num); + if (ret < 0 && !uuidd_cxt->quiet) warnx(_("failed to open/lock clock counter")); if (uuidd_cxt->debug) { uuid_unparse(uu, str); @@ -581,6 +599,7 @@ static void parse_options(int argc, char **argv, struct uuidd_cxt_t *uuidd_cxt, {"no-pid", no_argument, NULL, 'P'}, {"no-fork", no_argument, NULL, 'F'}, {"socket-activation", no_argument, NULL, 'S'}, + {"cont-clock", no_argument, NULL, 'c'}, {"debug", no_argument, NULL, 'd'}, {"quiet", no_argument, NULL, 'q'}, {"version", no_argument, NULL, 'V'}, @@ -596,9 +615,12 @@ static void parse_options(int argc, char **argv, struct uuidd_cxt_t *uuidd_cxt, int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; int c; - while ((c = getopt_long(argc, argv, "p:s:T:krtn:PFSdqVh", longopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "Cp:s:T:krtn:PFSdqVh", longopts, NULL)) != -1) { err_exclusive_options(c, longopts, excl, excl_st); switch (c) { + case 'C': + uuidd_cxt->cont_clock = 1; + break; case 'd': uuidd_cxt->debug = 1; break; -- 2.32.0 (Apple Git-132)