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.
This commit is contained in:
parent
6d8292f22a
commit
03b7962e80
|
@ -209,6 +209,8 @@ static int get_node_id(unsigned char *node_id)
|
||||||
|
|
||||||
/* Assume that the gettimeofday() has microsecond granularity */
|
/* Assume that the gettimeofday() has microsecond granularity */
|
||||||
#define MAX_ADJUSTMENT 10
|
#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.
|
* 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)) {
|
if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
|
||||||
ul_random_get_bytes(&clock_seq, sizeof(clock_seq));
|
do {
|
||||||
clock_seq &= 0x3FFF;
|
ul_random_get_bytes(&clock_seq, sizeof(clock_seq));
|
||||||
|
clock_seq &= 0x3FFF;
|
||||||
|
} while (clock_seq == CLOCK_SEQ_CONT);
|
||||||
gettimeofday(&last, NULL);
|
gettimeofday(&last, NULL);
|
||||||
last.tv_sec--;
|
last.tv_sec--;
|
||||||
}
|
}
|
||||||
|
@ -286,7 +290,9 @@ try_again:
|
||||||
if ((tv.tv_sec < last.tv_sec) ||
|
if ((tv.tv_sec < last.tv_sec) ||
|
||||||
((tv.tv_sec == last.tv_sec) &&
|
((tv.tv_sec == last.tv_sec) &&
|
||||||
(tv.tv_usec < last.tv_usec))) {
|
(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;
|
adjustment = 0;
|
||||||
last = tv;
|
last = tv;
|
||||||
} else if ((tv.tv_sec == last.tv_sec) &&
|
} else if ((tv.tv_sec == last.tv_sec) &&
|
||||||
|
@ -331,6 +337,64 @@ try_again:
|
||||||
return ret;
|
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)
|
#if defined(HAVE_UUIDD) && defined(HAVE_SYS_UN_H)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -403,7 +467,7 @@ static int get_uuid_via_daemon(int op __attribute__((__unused__)),
|
||||||
}
|
}
|
||||||
#endif
|
#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 unsigned char node_id[6];
|
||||||
static int has_init = 0;
|
static int has_init = 0;
|
||||||
|
@ -423,7 +487,14 @@ int __uuid_generate_time(uuid_t out, int *num)
|
||||||
}
|
}
|
||||||
has_init = 1;
|
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.clock_seq |= 0x8000;
|
||||||
uu.time_mid = (uint16_t) clock_mid;
|
uu.time_mid = (uint16_t) clock_mid;
|
||||||
uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000;
|
uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000;
|
||||||
|
@ -432,6 +503,16 @@ int __uuid_generate_time(uuid_t out, int *num)
|
||||||
return ret;
|
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
|
* Generate time-based UUID and store it to @out
|
||||||
*
|
*
|
||||||
|
|
|
@ -60,6 +60,7 @@ global:
|
||||||
UUIDD_PRIVATE {
|
UUIDD_PRIVATE {
|
||||||
global:
|
global:
|
||||||
__uuid_generate_time;
|
__uuid_generate_time;
|
||||||
|
__uuid_generate_time_cont;
|
||||||
__uuid_generate_random;
|
__uuid_generate_random;
|
||||||
local:
|
local:
|
||||||
*;
|
*;
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
#define UUIDD_MAX_OP UUIDD_OP_BULK_RANDOM_UUID
|
#define UUIDD_MAX_OP UUIDD_OP_BULK_RANDOM_UUID
|
||||||
|
|
||||||
extern int __uuid_generate_time(uuid_t out, int *num);
|
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 int __uuid_generate_random(uuid_t out, int *num);
|
extern int __uuid_generate_random(uuid_t out, int *num);
|
||||||
|
|
||||||
#endif /* _UUID_UUID_H */
|
#endif /* _UUID_UUID_H */
|
||||||
|
|
|
@ -24,6 +24,9 @@ The *uuidd* daemon is used by the UUID library to generate universally unique id
|
||||||
|
|
||||||
== OPTIONS
|
== OPTIONS
|
||||||
|
|
||||||
|
*-C*, *--cont-clock* _opt_arg_::
|
||||||
|
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. '-C' or '--cont-clock' enables the feature with a default max_clock_offset of 2 hours. '-C<NUM>[hd]' or '--cont-clock=<NUM>[hd]' 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.
|
||||||
|
|
||||||
*-d*, *--debug*::
|
*-d*, *--debug*::
|
||||||
Run *uuidd* in debugging mode. This prevents *uuidd* from running as a daemon.
|
Run *uuidd* in debugging mode. This prevents *uuidd* from running as a daemon.
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,8 @@ struct uuidd_cxt_t {
|
||||||
const char *cleanup_pidfile;
|
const char *cleanup_pidfile;
|
||||||
const char *cleanup_socket;
|
const char *cleanup_socket;
|
||||||
uint32_t timeout;
|
uint32_t timeout;
|
||||||
|
uint32_t cont_clock_offset;
|
||||||
|
|
||||||
unsigned int debug: 1,
|
unsigned int debug: 1,
|
||||||
quiet: 1,
|
quiet: 1,
|
||||||
no_fork: 1,
|
no_fork: 1,
|
||||||
|
@ -106,6 +108,8 @@ static void __attribute__((__noreturn__)) usage(void)
|
||||||
fputs(_(" -P, --no-pid do not create pid file\n"), out);
|
fputs(_(" -P, --no-pid do not create pid file\n"), out);
|
||||||
fputs(_(" -F, --no-fork do not daemonize using double-fork\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(_(" -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(_(" -d, --debug run in debugging mode\n"), out);
|
||||||
fputs(_(" -q, --quiet turn on quiet mode\n"), out);
|
fputs(_(" -q, --quiet turn on quiet mode\n"), out);
|
||||||
fputs(USAGE_SEPARATOR, out);
|
fputs(USAGE_SEPARATOR, out);
|
||||||
|
@ -438,6 +442,15 @@ static void server_loop(const char *socket_path, const char *pidfile_path,
|
||||||
pfd[POLLFD_SOCKET].fd = s;
|
pfd[POLLFD_SOCKET].fd = s;
|
||||||
pfd[POLLFD_SIGNAL].events = pfd[POLLFD_SOCKET].events = POLLIN | POLLERR | POLLHUP;
|
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) {
|
while (1) {
|
||||||
ret = poll(pfd, ARRAY_SIZE(pfd),
|
ret = poll(pfd, ARRAY_SIZE(pfd),
|
||||||
uuidd_cxt->timeout ?
|
uuidd_cxt->timeout ?
|
||||||
|
@ -494,7 +507,8 @@ static void server_loop(const char *socket_path, const char *pidfile_path,
|
||||||
break;
|
break;
|
||||||
case UUIDD_OP_TIME_UUID:
|
case UUIDD_OP_TIME_UUID:
|
||||||
num = 1;
|
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"));
|
warnx(_("failed to open/lock clock counter"));
|
||||||
if (uuidd_cxt->debug) {
|
if (uuidd_cxt->debug) {
|
||||||
uuid_unparse(uu, str);
|
uuid_unparse(uu, str);
|
||||||
|
@ -505,7 +519,8 @@ static void server_loop(const char *socket_path, const char *pidfile_path,
|
||||||
break;
|
break;
|
||||||
case UUIDD_OP_RANDOM_UUID:
|
case UUIDD_OP_RANDOM_UUID:
|
||||||
num = 1;
|
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"));
|
warnx(_("failed to open/lock clock counter"));
|
||||||
if (uuidd_cxt->debug) {
|
if (uuidd_cxt->debug) {
|
||||||
uuid_unparse(uu, str);
|
uuid_unparse(uu, str);
|
||||||
|
@ -515,7 +530,8 @@ static void server_loop(const char *socket_path, const char *pidfile_path,
|
||||||
reply_len = sizeof(uu);
|
reply_len = sizeof(uu);
|
||||||
break;
|
break;
|
||||||
case UUIDD_OP_BULK_TIME_UUID:
|
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"));
|
warnx(_("failed to open/lock clock counter"));
|
||||||
if (uuidd_cxt->debug) {
|
if (uuidd_cxt->debug) {
|
||||||
uuid_unparse(uu, str);
|
uuid_unparse(uu, str);
|
||||||
|
@ -567,6 +583,27 @@ static void __attribute__ ((__noreturn__)) unexpected_size(int size)
|
||||||
errx(EXIT_FAILURE, _("Unexpected reply length from server %d"), size);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
static void parse_options(int argc, char **argv, struct uuidd_cxt_t *uuidd_cxt,
|
static void parse_options(int argc, char **argv, struct uuidd_cxt_t *uuidd_cxt,
|
||||||
struct uuidd_options_t *uuidd_opts)
|
struct uuidd_options_t *uuidd_opts)
|
||||||
{
|
{
|
||||||
|
@ -581,6 +618,7 @@ static void parse_options(int argc, char **argv, struct uuidd_cxt_t *uuidd_cxt,
|
||||||
{"no-pid", no_argument, NULL, 'P'},
|
{"no-pid", no_argument, NULL, 'P'},
|
||||||
{"no-fork", no_argument, NULL, 'F'},
|
{"no-fork", no_argument, NULL, 'F'},
|
||||||
{"socket-activation", no_argument, NULL, 'S'},
|
{"socket-activation", no_argument, NULL, 'S'},
|
||||||
|
{"cont-clock", optional_argument, NULL, 'C'},
|
||||||
{"debug", no_argument, NULL, 'd'},
|
{"debug", no_argument, NULL, 'd'},
|
||||||
{"quiet", no_argument, NULL, 'q'},
|
{"quiet", no_argument, NULL, 'q'},
|
||||||
{"version", no_argument, NULL, 'V'},
|
{"version", no_argument, NULL, 'V'},
|
||||||
|
@ -596,9 +634,15 @@ 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 excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
while ((c = getopt_long(argc, argv, "p:s:T:krtn:PFSdqVh", longopts, NULL)) != -1) {
|
while ((c = getopt_long(argc, argv, "p:s:T:krtn:PFSC::dqVh", longopts, NULL)) != -1) {
|
||||||
err_exclusive_options(c, longopts, excl, excl_st);
|
err_exclusive_options(c, longopts, excl, excl_st);
|
||||||
switch (c) {
|
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':
|
case 'd':
|
||||||
uuidd_cxt->debug = 1;
|
uuidd_cxt->debug = 1;
|
||||||
break;
|
break;
|
||||||
|
@ -673,7 +717,7 @@ int main(int argc, char **argv)
|
||||||
char *cp;
|
char *cp;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
struct uuidd_cxt_t uuidd_cxt = { .timeout = 0 };
|
struct uuidd_cxt_t uuidd_cxt = { .timeout = 0, .cont_clock_offset = 0 };
|
||||||
struct uuidd_options_t uuidd_opts = { .socket_path = UUIDD_SOCKET_PATH };
|
struct uuidd_options_t uuidd_opts = { .socket_path = UUIDD_SOCKET_PATH };
|
||||||
|
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
|
|
Loading…
Reference in New Issue