diff --git a/bin/uuidgen/uuidgen.1 b/bin/uuidgen/uuidgen.1 --- a/bin/uuidgen/uuidgen.1 +++ b/bin/uuidgen/uuidgen.1 @@ -33,6 +33,7 @@ .Sh SYNOPSIS .Nm .Op Fl 1 +.Op Fl r .Op Fl n Ar count .Op Fl o Ar filename .Sh DESCRIPTION @@ -50,6 +51,8 @@ instructs .Nm to not generate them in batch, but one at a time. +.It Fl r +This option controls creation of random UUID (version 4). .It Fl n This option controls the number of identifiers generated. By default, multiple identifiers are generated in batch. diff --git a/bin/uuidgen/uuidgen.c b/bin/uuidgen/uuidgen.c --- a/bin/uuidgen/uuidgen.c +++ b/bin/uuidgen/uuidgen.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2002 Marcel Moolenaar + * Copyright (c) 2022 Tobias C. Berner * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,26 +41,63 @@ static void usage(void) { - (void)fprintf(stderr, "usage: uuidgen [-1] [-n count] [-o filename]\n"); + (void)fprintf(stderr, + "usage: uuidgen [-1] [-r] [-n count] [-o filename]\n"); exit(1); } +static int +uuidgen_v4(struct uuid *store, int count) +{ + int size; + struct uuid *item; + + if (count < 1) { + errno = EINVAL; + return (-1); + } + size = sizeof(struct uuid) * count; + arc4random_buf(store, size); + item = store; + for (int i = 0; i < count; ++i) { + /* + * Set the two most significant bits (bits 6 and 7) of the + * clock_seq_hi_and_reserved to zero and one, respectively. + */ + item->clock_seq_hi_and_reserved &= ~(3 << 6); + item->clock_seq_hi_and_reserved |= (2 << 6); + /* + * Set the four most significant bits (bits 12 through 15) of + * the time_hi_and_version field to the 4-bit version number + * from Section 4.1.3. + */ + item->time_hi_and_version &= ~(15 << 12); + item->time_hi_and_version |= (4 << 12); + item++; + }; + return (0); +} + int main(int argc, char *argv[]) { FILE *fp; uuid_t *store, *uuid; char *p; - int ch, count, i, iterate, status; + int ch, count, i, iterate, status, version; - count = -1; /* no count yet */ - fp = stdout; /* default output file */ - iterate = 0; /* not one at a time */ - while ((ch = getopt(argc, argv, "1n:o:")) != -1) + count = -1; /* no count yet */ + fp = stdout; /* default output file */ + iterate = 0; /* not one at a time */ + version = 1; /* create uuid v1 by default */ + while ((ch = getopt(argc, argv, "1rn:o:")) != -1) switch (ch) { case '1': iterate = 1; break; + case 'r': + version = 4; + break; case 'n': if (count > 0) usage(); @@ -92,19 +130,33 @@ if (count == -1) count = 1; - store = (uuid_t*)malloc(sizeof(uuid_t) * count); + store = (uuid_t *)malloc(sizeof(uuid_t) * count); if (store == NULL) err(1, "malloc()"); if (!iterate) { /* Get them all in a single batch */ - if (uuidgen(store, count) != 0) - err(1, "uuidgen()"); + if (version == 1) { + if (uuidgen(store, count) != 0) + err(1, "uuidgen()"); + } else if (version == 4) { + if (uuidgen_v4(store, count) != 0) + err(1, "uuidgen_v4()"); + } else { + err(1, "unsupported version"); + } } else { uuid = store; for (i = 0; i < count; i++) { - if (uuidgen(uuid++, 1) != 0) - err(1, "uuidgen()"); + if (version == 1) { + if (uuidgen(uuid++, 1) != 0) + err(1, "uuidgen()"); + } else if (version == 4) { + if (uuidgen_v4(uuid++, 1) != 0) + err(1, "uuidgen_v4()"); + } else { + err(1, "unsupported version"); + } } } @@ -112,7 +164,7 @@ while (count--) { uuid_to_string(uuid++, &p, &status); if (status != uuid_s_ok) - err(1, "cannot stringify a UUID"); + err(1, "cannot stringify a UUID"); fprintf(fp, "%s\n", p); free(p); }