Add uuidd daemon to prevent duplicate time-based UUID's

Also store the clock sequence information in a state file in
/var/lib/misc/uuid-clock so that if the time goes backwards the clock
sequence counter can get bumped.  This allows us to completely
correctly generate time-based (version 1) UUID's according to the
algorithm specified RFC 4122.

Addresses-Sourceforge-Bug: #1529672
Addresses-Red-Hat-Bugzilla: #233471

Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
bitmap-optimize
Theodore Ts'o 2007-12-16 17:21:38 -05:00
parent 5221837e62
commit 740837def7
20 changed files with 1206 additions and 23 deletions

49
aclocal.m4 vendored
View File

@ -2600,3 +2600,52 @@ AC_DEFUN([gl_XSIZE],
AC_REQUIRE([gl_SIZE_MAX])
AC_CHECK_HEADERS(stdint.h)
])
# from http://autoconf-archive.cryp.to/ax_tls.html
#
# This was licensed under the GPL with the following exception:
#
# As a special exception, the respective Autoconf Macro's copyright
# owner gives unlimited permission to copy, distribute and modify the
# configure scripts that are the output of Autoconf when processing
# the Macro. You need not follow the terms of the GNU General Public
# License when using or distributing such scripts, even though
# portions of the text of the Macro appear in them. The GNU General
# Public License (GPL) does govern all other use of the material that
# constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the
# Autoconf Macro released by the Autoconf Macro Archive. When you make
# and distribute a modified version of the Autoconf Macro, you may
# extend this special exception to the GPL to apply to your modified
# version as well.
#
AC_DEFUN([AX_TLS], [
AC_MSG_CHECKING(for thread local storage (TLS) class)
AC_CACHE_VAL(ac_cv_tls, [
ax_tls_keywords="__thread __declspec(thread) none"
for ax_tls_keyword in $ax_tls_keywords; do
case $ax_tls_keyword in
none) ac_cv_tls=none ; break ;;
*)
AC_TRY_COMPILE(
[#include <stdlib.h>
static void
foo(void) {
static ] $ax_tls_keyword [ int bar;
exit(1);
}],
[],
[ac_cv_tls=$ax_tls_keyword ; break],
ac_cv_tls=none
)
esac
done
])
if test "$ac_cv_tls" != "none"; then
dnl AC_DEFINE([TLS], [], [If the compiler supports a TLS storage class define it to that here])
AC_DEFINE_UNQUOTED([TLS], $ac_cv_tls, [If the compiler supports a TLS storage class define it to that here])
fi
AC_MSG_RESULT($ac_cv_tls)
])

79
configure vendored
View File

@ -11434,6 +11434,81 @@ done
fi
{ echo "$as_me:$LINENO: checking for thread local storage (TLS) class" >&5
echo $ECHO_N "checking for thread local storage (TLS) class... $ECHO_C" >&6; }
if test "${ac_cv_tls+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
ax_tls_keywords="__thread __declspec(thread) none"
for ax_tls_keyword in $ax_tls_keywords; do
case $ax_tls_keyword in
none) ac_cv_tls=none ; break ;;
*)
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <stdlib.h>
static void
foo(void) {
static $ax_tls_keyword int bar;
exit(1);
}
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
ac_cv_tls=$ax_tls_keyword ; break
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_cv_tls=none
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
esac
done
fi
if test "$ac_cv_tls" != "none"; then
cat >>confdefs.h <<_ACEOF
#define TLS $ac_cv_tls
_ACEOF
fi
{ echo "$as_me:$LINENO: result: $ac_cv_tls" >&5
echo "${ECHO_T}$ac_cv_tls" >&6; }
@ -14518,7 +14593,9 @@ fi
for ac_func in chflags getrusage llseek lseek64 open64 fstat64 getmntinfo strtoull strcasecmp srandom jrand48 fchown mallinfo fdatasync strnlen strptime strdup sysconf pathconf posix_memalign memalign valloc __secure_getenv prctl mmap utime
for ac_func in chflags getrusage llseek lseek64 open64 fstat64 getmntinfo strtoull strcasecmp srandom jrand48 fchown mallinfo fdatasync strnlen strptime strdup sysconf pathconf posix_memalign memalign valloc __secure_getenv prctl mmap utime setresuid setresgid
do
as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
{ echo "$as_me:$LINENO: checking for $ac_func" >&5

View File

@ -570,6 +570,7 @@ if test $cross_compiling = no; then
else
AC_CHECK_PROGS(BUILD_CC, gcc cc)
fi
AX_TLS
AC_CHECK_HEADERS(stdlib.h unistd.h stdarg.h stdint.h errno.h malloc.h mntent.h paths.h dirent.h getopt.h setjmp.h signal.h termios.h linux/fd.h linux/major.h sys/disklabel.h sys/ioctl.h sys/mman.h sys/mkdev.h sys/prctl.h sys/queue.h sys/sockio.h sys/socket.h sys/sysmacros.h sys/time.h sys/stat.h sys/types.h sys/wait.h sys/resource.h net/if_dl.h netinet/in.h utime.h)
AC_CHECK_HEADERS(sys/disk.h sys/mount.h,,,
[[
@ -676,7 +677,7 @@ AC_CHECK_MEMBER(struct sockaddr.sa_len,
[#include <sys/types.h>
#include <sys/socket.h>])
dnl
AC_CHECK_FUNCS(chflags getrusage llseek lseek64 open64 fstat64 getmntinfo strtoull strcasecmp srandom jrand48 fchown mallinfo fdatasync strnlen strptime strdup sysconf pathconf posix_memalign memalign valloc __secure_getenv prctl mmap utime)
AC_CHECK_FUNCS(chflags getrusage llseek lseek64 open64 fstat64 getmntinfo strtoull strcasecmp srandom jrand48 fchown mallinfo fdatasync strnlen strptime strdup sysconf pathconf posix_memalign memalign valloc __secure_getenv prctl mmap utime setresuid setresgid)
dnl
dnl Check to see if -lsocket is required (solaris) to make something
dnl that uses socket() to compile; this is needed for the UUID library

6
debian/changelog vendored
View File

@ -1,3 +1,9 @@
e2fsprogs (1.40.3-2) unstable; urgency=low
* Add uuidd daemon
-- Theodore Y. Ts'o <tytso@mit.edu> Sun, 09 Dec 2007 22:47:53 -0500
e2fsprogs (1.40.3-1) unstable; urgency=medium
* New upstream release

14
debian/control vendored
View File

@ -78,12 +78,26 @@ Package: libuuid1
Section: libs
Priority: required
Depends: ${shlibs:Depends}
Recommends: uuid-runtime
Replaces: e2fsprogs (<< 1.34-1)
Architecture: any
Description: universally unique id library
libuuid generates and parses 128-bit universally unique id's (UUID's).
See RFC 4122 for more information.
Package: uuid-runtime
Section: libs
Priority: optional
Depends: ${shlibs:Depends}
Replaces: e2fsprogs (<= 1.40.3-1ubuntu1)
Architecture: any
Description: universally unique id library
libuuid generates and parses 128-bit universally unique id's (UUID's).
See RFC 4122 for more information.
.
This package contains the uuidd daemon which is used by libuuid as well as
the uuidgen program.
Package: libuuid1-udeb
Section: debian-installer
Priority: optional

8
debian/libuuid1.postinst vendored Normal file
View File

@ -0,0 +1,8 @@
#!/bin/sh
adduser --system --group --no-create-home --disabled-login \
--quiet --home /var/lib/libuuid libuuid
mkdir -p /var/lib/libuuid
chown libuuid:libuuid /var/lib/libuuid
chmod 2775 /var/lib/libuuid

6
debian/libuuid1.postrm vendored Normal file
View File

@ -0,0 +1,6 @@
#!/bin/sh
set -e
if [ "$1" = purge ]
then
rm -rf /var/lib/libuuid
fi

2
debian/rules vendored
View File

@ -359,7 +359,7 @@ binary-arch: install install-udeb
DH_OPTIONS= dh_installchangelogs -pe2fsprogs \
-plibblkid${BLKID_SOVERSION} -plibcomerr${COMERR_SOVERSION} \
-plibss${SS_SOVERSION} -plibuuid${UUID_SOVERSION} \
-pe2fslibs -puuid-dev -pe2fsck-static
-pe2fslibs -puuid-dev -puuid-runtime -pe2fsck-static
dh_fixperms
ifneq ($(ismips),)

39
debian/uuid-runtime.copyright vendored Normal file
View File

@ -0,0 +1,39 @@
This package was added to the e2fsprogs debian source package by
Theodore Ts'o <tytso@mit.edu> on Fri Dec 14 22:24:35 EST 2007
It is part of the main e2fsprogs distribution, which can be found at:
http://sourceforge.net/projects/e2fsprogs
Upstream Author: Theodore Ts'o <tytso@mit.edu>
Copyright:
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 by
Theodore Ts'o
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, and the entire permission notice in its entirety,
including the disclaimer of warranties.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote
products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.

4
debian/uuid-runtime.files vendored Normal file
View File

@ -0,0 +1,4 @@
usr/bin/uuidgen
usr/sbin/uuidd
usr/share/man/man8/uuidd.*
usr/share/man/man1/uuidgen.*

9
debian/uuid-runtime.postinst vendored Normal file
View File

@ -0,0 +1,9 @@
#!/bin/sh
adduser --system --group --no-create-home --disabled-login \
--quiet --home /var/lib/libuuid libuuid
mkdir -p /var/run/uuidd
chown libuuid:libuuid /var/run/uuidd
chmod 775 /var/run/uuidd
chown libuuid:libuuid /usr/sbin/uuidd
chmod 6755 /usr/sbin/uuidd

7
debian/uuid-runtime.postrm vendored Normal file
View File

@ -0,0 +1,7 @@
#!/bin/sh
set -e
if [ "$1" = purge ]
then
rm -rf /var/run/uuidd
fi

6
debian/uuid-runtime.prerm vendored Normal file
View File

@ -0,0 +1,6 @@
#! /bin/sh
if [ -x /usr/sbin/uuidd ]
then
/usr/sbin/uuidd -k || true
fi

1
debian/uuid-runtime.shlibs.local vendored Normal file
View File

@ -0,0 +1 @@
libuuid 1 libuuid1 (> 1.40.3-1)

View File

@ -38,6 +38,7 @@
*/
#define _SVID_SOURCE
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@ -57,6 +58,7 @@
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include <sys/un.h>
#ifdef HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
#endif
@ -74,15 +76,22 @@
#endif
#include "uuidP.h"
#include "uuidd.h"
#ifdef HAVE_SRANDOM
#define srand(x) srandom(x)
#define rand() random()
#endif
#ifdef TLS
#define THREAD_LOCAL static TLS
#else
#define THREAD_LOCAL static
#endif
#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
#define DO_JRAND_MIX
static unsigned short jrand_seed[3];
THREAD_LOCAL unsigned short jrand_seed[3];
#endif
static int get_random_fd(void)
@ -247,22 +256,62 @@ static int get_node_id(unsigned char *node_id)
/* Assume that the gettimeofday() has microsecond granularity */
#define MAX_ADJUSTMENT 10
static int get_clock(uint32_t *clock_high, uint32_t *clock_low, uint16_t *ret_clock_seq)
static int get_clock(uint32_t *clock_high, uint32_t *clock_low,
uint16_t *ret_clock_seq, int *num)
{
static int adjustment = 0;
static struct timeval last = {0, 0};
static uint16_t clock_seq;
THREAD_LOCAL int adjustment = 0;
THREAD_LOCAL struct timeval last = {0, 0};
THREAD_LOCAL int state_fd = -2;
THREAD_LOCAL FILE *state_f;
THREAD_LOCAL uint16_t clock_seq;
struct timeval tv;
unsigned long long clock_reg;
try_again:
gettimeofday(&tv, 0);
mode_t save_umask;
if (state_fd == -2) {
save_umask = umask(0);
state_fd = open("/var/lib/libuuid/clock.txt",
O_RDWR|O_CREAT, 0660);
(void) umask(save_umask);
state_f = fdopen(state_fd, "r+");
if (!state_f) {
close(state_fd);
state_fd = -1;
}
}
if (state_fd >= 0) {
rewind(state_f);
while (lockf(state_fd, F_LOCK, 0) < 0) {
if ((errno == EAGAIN) || (errno == EINTR))
continue;
fclose(state_f);
close(state_fd);
state_fd = -1;
}
}
if (state_fd >= 0) {
unsigned int cl;
unsigned long tv1, tv2;
int a;
if (fscanf(state_f, "clock: %04x tv: %lu %lu adj: %d\n",
&cl, &tv1, &tv2, &a) == 4) {
clock_seq = cl & 0x3FFF;
last.tv_sec = tv1;
last.tv_usec = tv2;
adjustment = a;
}
}
if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
get_random_bytes(&clock_seq, sizeof(clock_seq));
clock_seq &= 0x3FFF;
last = tv;
last.tv_sec--;
}
try_again:
gettimeofday(&tv, 0);
if ((tv.tv_sec < last.tv_sec) ||
((tv.tv_sec == last.tv_sec) &&
(tv.tv_usec < last.tv_usec))) {
@ -283,13 +332,124 @@ try_again:
clock_reg += ((unsigned long long) tv.tv_sec)*10000000;
clock_reg += (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000;
if (num && (*num > 1)) {
adjustment += *num - 1;
last.tv_usec += adjustment / 10;
adjustment = adjustment % 10;
last.tv_sec += last.tv_usec / 1000000;
last.tv_usec = last.tv_usec % 1000000;
}
if (state_fd > 0) {
rewind(state_f);
ftruncate(state_fd, 0);
fprintf(state_f, "clock: %04x tv: %lu %lu adj: %d\n",
clock_seq, last.tv_sec, last.tv_usec, adjustment);
fflush(state_f);
rewind(state_f);
lockf(state_fd, F_ULOCK, 0);
}
*clock_high = clock_reg >> 32;
*clock_low = clock_reg;
*ret_clock_seq = clock_seq;
return 0;
}
void uuid_generate_time(uuid_t out)
static ssize_t read_all(int fd, char *buf, size_t count)
{
ssize_t ret;
ssize_t c = 0;
memset(buf, 0, count);
while (count > 0) {
ret = read(fd, buf, count);
if (ret < 0) {
if ((errno == EAGAIN) || (errno == EINTR))
continue;
return -1;
}
count -= ret;
buf += ret;
c += ret;
}
return c;
}
/*
* Try using the uuidd daemon to generate the UUID
*
* Returns 0 on success, non-zero on failure.
*/
static int get_uuid_via_daemon(int op, uuid_t out, int *num)
{
char op_buf[64];
int op_len;
int s;
ssize_t ret;
int32_t reply_len = 0, expected = 16;
struct sockaddr_un srv_addr;
static const char *uuidd_path = UUIDD_PATH;
static int access_ret = -2;
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
return -1;
srv_addr.sun_family = AF_UNIX;
strcpy(srv_addr.sun_path, UUIDD_SOCKET_PATH);
if (connect(s, (const struct sockaddr *) &srv_addr,
sizeof(struct sockaddr_un)) < 0) {
if (access_ret == -2)
access_ret = access(uuidd_path, X_OK);
if (access_ret == 0) {
if (fork() == 0) {
execl(uuidd_path, "uuidd", "-qT", "300", 0);
exit(1);
}
usleep(500);
if (connect(s, (const struct sockaddr *) &srv_addr,
sizeof(struct sockaddr_un)) < 0)
goto fail;
} else
goto fail;
}
op_buf[0] = op;
op_len = 1;
if (op == UUIDD_OP_BULK_TIME_UUID) {
memcpy(op_buf+1, num, sizeof(num));
op_len += sizeof(num);
expected += sizeof(num);
}
ret = write(s, op_buf, op_len);
if (ret < 1)
goto fail;
ret = read_all(s, (char *) &reply_len, sizeof(reply_len));
if (ret < 0)
goto fail;
if (reply_len != expected)
goto fail;
ret = read_all(s, op_buf, reply_len);
if (op == UUIDD_OP_BULK_TIME_UUID)
memcpy(op_buf+16, num, sizeof(int));
memcpy(out, op_buf, 16);
close(s);
return ((ret == expected) ? 0 : -1);
fail:
close(s);
return -1;
}
void uuid__generate_time(uuid_t out, int *num)
{
static unsigned char node_id[6];
static int has_init = 0;
@ -308,7 +468,7 @@ void uuid_generate_time(uuid_t out)
}
has_init = 1;
}
get_clock(&clock_mid, &uu.time_low, &uu.clock_seq);
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;
@ -316,19 +476,82 @@ void uuid_generate_time(uuid_t out)
uuid_pack(&uu, out);
}
void uuid_generate_random(uuid_t out)
void uuid_generate_time(uuid_t out)
{
#ifdef TLS
THREAD_LOCAL int num = 0;
THREAD_LOCAL struct uuid uu;
THREAD_LOCAL time_t last_time = 0;
time_t now;
if (num > 0) {
now = time(0);
if (now > last_time+1)
num = 0;
}
if (num <= 0) {
num = 1000;
if (get_uuid_via_daemon(UUIDD_OP_BULK_TIME_UUID,
out, &num) == 0) {
last_time = time(0);
uuid_unpack(out, &uu);
num--;
return;
}
num = 0;
}
if (num > 0) {
uu.time_low++;
if (uu.time_low == 0) {
uu.time_mid++;
if (uu.time_mid == 0)
uu.time_hi_and_version++;
}
num--;
uuid_pack(&uu, out);
return;
}
#else
if (get_uuid_via_daemon(UUIDD_OP_TIME_UUID, out, 0) == 0)
return;
#endif
uuid__generate_time(out, 0);
}
void uuid__generate_random(uuid_t out, int *num)
{
uuid_t buf;
struct uuid uu;
int i, n;
get_random_bytes(buf, sizeof(buf));
uuid_unpack(buf, &uu);
if (!num || !*num)
n = 1;
else
n = *num;
uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
uuid_pack(&uu, out);
for (i = 0; i < n; i++) {
get_random_bytes(buf, sizeof(buf));
uuid_unpack(buf, &uu);
uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF)
| 0x4000;
uuid_pack(&uu, out);
out += sizeof(uuid_t);
}
}
void uuid_generate_random(uuid_t out)
{
int num = 1;
/* No real reason to use the daemon for random uuid's -- yet */
uuid__generate_random(out, &num);
}
/*
* This is the generic front-end to uuid_generate_random and
* uuid_generate_time. It uses uuid_generate_random only if

53
lib/uuid/uuidd.h Normal file
View File

@ -0,0 +1,53 @@
/*
* Definitions used by the uuidd daemon
*
* Copyright (C) 2007 Theodore Ts'o.
*
* %Begin-Header%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, and the entire permission notice in its entirety,
* including the disclaimer of warranties.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
* WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
* %End-Header%
*/
#ifndef _UUID_UUIDD_H
#define _UUID_UUIDD_H
#define UUIDD_SOCKET_PATH "/var/run/uuidd/request"
#define UUIDD_PIDFILE_PATH "/var/run/uuidd/uuidd.pid"
#define UUIDD_PATH "/usr/sbin/uuidd"
#define UUIDD_OP_GETPID 0
#define UUIDD_OP_GET_MAXOP 1
#define UUIDD_OP_TIME_UUID 2
#define UUIDD_OP_RANDOM_UUID 3
#define UUIDD_OP_BULK_TIME_UUID 4
#define UUIDD_OP_BULK_RANDOM_UUID 5
#define UUIDD_MAX_OP UUIDD_OP_BULK_RANDOM_UUID
extern void uuid__generate_time(uuid_t out, int *num);
extern void uuid__generate_random(uuid_t out, int *num);
#endif /* _UUID_UUID_H */

View File

@ -15,11 +15,11 @@ INSTALL = @INSTALL@
@IMAGER_CMT@E2IMAGE_MAN= e2image.8
SPROGS= mke2fs badblocks tune2fs dumpe2fs blkid logsave \
$(E2IMAGE_PROG) @FSCK_PROG@
USPROGS= mklost+found filefrag
$(E2IMAGE_PROG) @FSCK_PROG@
USPROGS= mklost+found filefrag uuidd
SMANPAGES= tune2fs.8 mklost+found.8 mke2fs.8 dumpe2fs.8 badblocks.8 \
e2label.8 findfs.8 blkid.8 $(E2IMAGE_MAN) \
logsave.8 filefrag.8 @FSCK_MAN@
logsave.8 filefrag.8 uuidd.8 @FSCK_MAN@
FMANPAGES= mke2fs.conf.5
UPROGS= chattr lsattr uuidgen
@ -33,6 +33,7 @@ MKE2FS_OBJS= mke2fs.o util.o profile.o prof_err.o default_profile.o
CHATTR_OBJS= chattr.o
LSATTR_OBJS= lsattr.o
UUIDGEN_OBJS= uuidgen.o
UUIDD_OBJS= uuidd.o
DUMPE2FS_OBJS= dumpe2fs.o
BADBLOCKS_OBJS= badblocks.o
E2IMAGE_OBJS= e2image.o
@ -144,6 +145,10 @@ uuidgen: $(UUIDGEN_OBJS) $(DEPLIBUUID)
@echo " LD $@"
@$(CC) $(ALL_LDFLAGS) -o uuidgen $(UUIDGEN_OBJS) $(LIBUUID) $(LIBINTL)
uuidd: $(UUIDD_OBJS) $(DEPLIBUUID)
@echo " LD $@"
@$(CC) $(ALL_LDFLAGS) -o uuidd $(UUIDD_OBJS) $(LIBUUID) $(LIBINTL)
dumpe2fs: $(DUMPE2FS_OBJS) $(DEPLIBS) $(DEPLIBS_E2P) $(DEPLIBUUID)
@echo " LD $@"
@$(CC) $(ALL_LDFLAGS) -o dumpe2fs $(DUMPE2FS_OBJS) $(LIBS) \
@ -213,6 +218,10 @@ logsave.8: $(DEP_SUBSTITUTE) $(srcdir)/logsave.8.in
@echo " SUBST $@"
@$(SUBSTITUTE_UPTIME) $(srcdir)/logsave.8.in logsave.8
uuidd.8: $(DEP_SUBSTITUTE) $(srcdir)/uuidd.8.in
@echo " SUBST $@"
@$(SUBSTITUTE_UPTIME) $(srcdir)/uuidd.8.in uuidd.8
chattr.1: $(DEP_SUBSTITUTE) $(srcdir)/chattr.1.in
@echo " SUBST $@"
@$(SUBSTITUTE_UPTIME) $(srcdir)/chattr.1.in chattr.1
@ -239,7 +248,8 @@ installdirs:
$(DESTDIR)$(root_sbindir) $(DESTDIR)$(bindir) \
$(DESTDIR)$(man1dir) $(DESTDIR)$(man8dir) \
$(DESTDIR)$(man1dir) $(DESTDIR)$(man5dir) \
$(DESTDIR)$(libdir) $(DESTDIR)/$(root_sysconfdir)
$(DESTDIR)$(libdir) $(DESTDIR)/$(root_sysconfdir) \
$(DESTDIR)/etc/init.d
install: all $(SMANPAGES) $(UMANPAGES) installdirs
@for i in $(SPROGS); do \

97
misc/uuidd.8.in Normal file
View File

@ -0,0 +1,97 @@
.\" -*- nroff -*-
.\" Copyright 2007 by Theodore Ts'o. All Rights Reserved.
.\" This file may be copied under the terms of the GNU Public License.
.\"
.TH UUIDD 8 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
.SH NAME
uuidd \- UUID generation daemon
.SH SYNOPSIS
.B uuidd
[
.B \-d
]
[
.B \-p
.I pidfile
]
[
.B \-s
.I socketpath
]
[
.B \-T
.I timeout
]
.B uuidd
[
.B \-r
|
.B \-t
]
[
.B \-n
.I number
]
[
.B \-s
.I socketpath
]
.B uuidd \-k
.SH DESCRIPTION
The
.B uuidd
daemon is used by the UUID library to generate
universally unique identifiers (UUIDs), especially time-based UUID's
in a secure and guaranteed-unique fashion, even in the face of large
numbers of threads trying to grab UUID's running on different CPU's.
.SH OPTIONS
.TP
.B \-d
Run
.B uuidd
in debugging mode. This prevents uuidd from running as a daemon.
.TP
.B \-k
If a currently uuidd daemon is running, kill it.
.TP
.BI \-n " number"
When issuing a test request to a running uuidd, request a bulk response
of
.I number
UUID's.
.TP
.BI \-p " pidfile"
Specify the pathname where the pid file should be written. By default,
the pid file is written to /var/run/uuidd.pid.
.TP
.BI \-s " socketpath"
Specify the pathname used for the unix-domain socket used by uuidd. By
qdefault, the pathname used is /var/run/uuidd.sock. This is primarily
for debugging purposes, since the pathname is hard-coded in the libuuid
library.
.TP
.B \-r
Test uuidd by trying to connect to a running uuidd daemon and
request it to return a random-based UUID.
.TP
.B \-t
Test uuidd by trying to connect to a running uuidd daemon and
request it to return a time-based UUID.
.TP
.BI \-T " timeout"
Specify a timeout for uuidd. If specified, then uuidd will exit after
.I timeout
seconds of inactivity.
.SH AUTHOR
The
.B uuidd
daemon was written by Theodore Ts'o <tytso@mit.edu>.
.SH AVAILABILITY
.B uuidd
is part of libuuid from the e2fsprogs package and is available from
http://e2fsprogs.sourceforge.net.
.SH "SEE ALSO"
.BR libuuid (3),
.BR uuidgen (1)

518
misc/uuidd.c Normal file
View File

@ -0,0 +1,518 @@
/*
* uuidd.c --- UUID-generation daemon
*
* Copyright (C) 2007 Theodore Ts'o
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Public
* License.
* %End-Header%
*/
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <unistd.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <signal.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
extern int getopt(int argc, char * const argv[], const char *optstring);
extern char *optarg;
extern int optind;
#endif
#include "uuid/uuid.h"
#include "uuid/uuidd.h"
#include "nls-enable.h"
#ifdef __GNUC__
#define CODE_ATTR(x) __attribute__(x)
#else
#define CODE_ATTR(x)
#endif
static void usage(const char *progname)
{
fprintf(stderr, _("Usage: %s [-d] [-p pidfile] [-s socketpath] "
"[-T timeout]\n"), progname);
fprintf(stderr, _(" %s [-r|t] [-n num] [-s socketpath]\n"),
progname);
fprintf(stderr, _(" %s -k\n"), progname);
exit(1);
}
static void create_daemon(const char *pidfile_path)
{
pid_t pid;
uid_t euid;
FILE *f;
pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
} else if (pid != 0) {
exit(0);
}
close(0);
close(1);
close(2);
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR);
chdir("/");
(void) setsid();
euid = geteuid();
(void) setreuid(euid, euid);
f = fopen(pidfile_path, "w");
if (f) {
fprintf(f, "%d\n", getpid());
fclose(f);
}
}
static int read_all(int fd, char *buf, size_t count)
{
ssize_t ret;
int c = 0;
memset(buf, 0, count);
while (count > 0) {
ret = read(fd, buf, count);
if (ret < 0) {
if ((errno == EAGAIN) || (errno == EINTR))
continue;
return -1;
}
count -= ret;
buf += ret;
c += ret;
}
return c;
}
static const char *cleanup_pidfile, *cleanup_socket;
static void terminate_intr(int signo CODE_ATTR((unused)))
{
(void) unlink(cleanup_pidfile);
(void) unlink(cleanup_socket);
exit(0);
}
static void server_loop(const char *socket_path, int debug,
const char *pidfile_path,
int timeout, int quiet)
{
struct sockaddr_un my_addr, from_addr;
unsigned char reply_buf[1024], *cp;
socklen_t fromlen;
int32_t reply_len = 0;
uuid_t uu;
mode_t save_umask;
char op, str[37];
int i, s, ns, len, num;
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
if (!quiet)
fprintf(stderr, _("Couldn't create unix stream "
"socket: %s"), strerror(errno));
exit(1);
}
/*
* Create the address we will be binding to.
*/
my_addr.sun_family = AF_UNIX;
strcpy(my_addr.sun_path, socket_path);
(void) unlink(socket_path);
save_umask = umask(0);
if (bind(s, (const struct sockaddr *) &my_addr,
sizeof(struct sockaddr_un)) < 0) {
if (!quiet)
fprintf(stderr,
_("Couldn't bind unix socket %s: %s\n"),
socket_path, strerror(errno));
exit(1);
}
(void) umask(save_umask);
if (listen(s, 5) < 0) {
if (!quiet)
fprintf(stderr, _("Couldn't listen on unix "
"socket %s: %s\n"), socket_path,
strerror(errno));
exit(1);
}
if (!debug) {
create_daemon(pidfile_path);
cleanup_pidfile = pidfile_path;
cleanup_socket = socket_path;
signal(SIGHUP, terminate_intr);
signal(SIGINT, terminate_intr);
signal(SIGPIPE, terminate_intr);
signal(SIGTERM, terminate_intr);
signal(SIGALRM, terminate_intr);
}
signal(SIGPIPE, SIG_IGN);
while (1) {
fromlen = sizeof(from_addr);
if (timeout > 0)
alarm(timeout);
ns = accept(s, (struct sockaddr *) &from_addr, &fromlen);
alarm(0);
if (ns < 0) {
if ((errno == EAGAIN) || (errno == EINTR))
continue;
perror("accept");
exit(1);
}
len = read(ns, &op, 1);
if (len != 1) {
if (len < 0)
perror("read");
else
printf(_("Error reading from client, "
"len = %d\n"), len);
goto shutdown_socket;
}
if ((op == 4) || (op == 5)) {
if (read_all(ns, (char *) &num, sizeof(num)) != 4)
goto shutdown_socket;
if (debug)
printf(_("operation %d, incoming num = %d\n"),
op, num);
} else if (debug)
printf("operation %d\n", op);
switch(op) {
case UUIDD_OP_GETPID:
sprintf((char *) reply_buf, "%d", getpid());
reply_len = strlen((char *) reply_buf)+1;
break;
case UUIDD_OP_GET_MAXOP:
sprintf((char *) reply_buf, "%d", UUIDD_MAX_OP);
reply_len = strlen((char *) reply_buf)+1;
break;
case UUIDD_OP_TIME_UUID:
num = 1;
uuid__generate_time(uu, &num);
if (debug) {
uuid_unparse(uu, str);
printf(_("Generated time UUID: %s\n"), str);
}
memcpy(reply_buf, uu, sizeof(uu));
reply_len = sizeof(uu);
break;
case UUIDD_OP_RANDOM_UUID:
num = 1;
uuid__generate_random(uu, &num);
if (debug) {
uuid_unparse(uu, str);
printf(_("Generated random UUID: %s\n"), str);
}
memcpy(reply_buf, uu, sizeof(uu));
reply_len = sizeof(uu);
break;
case UUIDD_OP_BULK_TIME_UUID:
uuid__generate_time(uu, &num);
if (debug) {
uuid_unparse(uu, str);
printf(_("Generated time UUID %s and %d "
"following\n"), str, num);
}
memcpy(reply_buf, uu, sizeof(uu));
reply_len = sizeof(uu);
memcpy(reply_buf+reply_len, &num, sizeof(num));
reply_len += sizeof(num);
break;
case UUIDD_OP_BULK_RANDOM_UUID:
if (num < 0)
num = 1;
if (num > 1000)
num = 1000;
if (num*16 > (int) (sizeof(reply_buf)-sizeof(num)))
num = (sizeof(reply_buf)-sizeof(num)) / 16;
uuid__generate_random(reply_buf+sizeof(num), &num);
if (debug) {
printf(_("Generated %d UUID's:\n"), num);
for (i=0, cp=reply_buf+sizeof(num);
i < num; i++, cp+=16) {
uuid_unparse(cp, str);
printf("\t%s\n", str);
}
}
reply_len = (num*16) + sizeof(num);
memcpy(reply_buf, &num, sizeof(num));
break;
default:
if (debug)
printf(_("Invalid operation %d\n"), op);
goto shutdown_socket;
}
write(ns, &reply_len, sizeof(reply_len));
write(ns, reply_buf, reply_len);
shutdown_socket:
close(ns);
}
}
static int call_daemon(const char *socket_path, int op, unsigned char *buf,
int buflen, int *num, const char **err_context)
{
char op_buf[8];
int op_len;
int s;
ssize_t ret;
int32_t reply_len = 0;
struct sockaddr_un srv_addr;
if (((op == 4) || (op == 5)) && !num) {
if (err_context)
*err_context = _("bad arguments");
errno = EINVAL;
return -1;
}
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
if (err_context)
*err_context = _("socket");
return -1;
}
srv_addr.sun_family = AF_UNIX;
strcpy(srv_addr.sun_path, socket_path);
if (connect(s, (const struct sockaddr *) &srv_addr,
sizeof(struct sockaddr_un)) < 0) {
if (err_context)
*err_context = _("connect");
close(s);
return -1;
}
if (op == 5) {
if ((*num)*16 > buflen-4)
*num = (buflen-4) / 16;
}
op_buf[0] = op;
op_len = 1;
if ((op == 4) || (op == 5)) {
memcpy(op_buf+1, num, sizeof(int));
op_len += sizeof(int);
}
ret = write(s, op_buf, op_len);
if (ret < op_len) {
if (err_context)
*err_context = _("write");
close(s);
return -1;
}
ret = read_all(s, (char *) &reply_len, sizeof(reply_len));
if (ret < 0) {
if (err_context)
*err_context = _("read count");
close(s);
return -1;
}
if (reply_len < 0 || reply_len > buflen) {
if (err_context)
*err_context = _("bad response length");
close(s);
return -1;
}
ret = read_all(s, (char *) buf, reply_len);
if ((ret > 0) && (op == 4)) {
if (reply_len >= (int) (16+sizeof(int)))
memcpy(buf+16, num, sizeof(int));
else
*num = -1;
}
if ((ret > 0) && (op == 5)) {
if (*num >= (int) sizeof(int))
memcpy(buf, num, sizeof(int));
else
*num = -1;
}
close(s);
return ret;
}
int main(int argc, char **argv)
{
const char *socket_path = UUIDD_SOCKET_PATH;
const char *pidfile_path = UUIDD_PIDFILE_PATH;
const char *err_context;
unsigned char buf[1024], *cp;
char str[37], *tmp;
uuid_t uu;
uid_t uid;
gid_t gid;
int i, c, ret;
int debug = 0, do_type = 0, do_kill = 0, num = 0;
int timeout = 0, quiet = 0, drop_privs = 0;
#ifdef ENABLE_NLS
setlocale(LC_MESSAGES, "");
setlocale(LC_CTYPE, "");
bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
textdomain(NLS_CAT_NAME);
#endif
while ((c = getopt (argc, argv, "dkn:qp:s:tT:r")) != EOF) {
switch (c) {
case 'd':
debug++;
drop_privs++;
break;
case 'k':
do_kill++;
drop_privs++;
break;
case 'n':
num = strtol(optarg, &tmp, 0);
if ((num < 0) || *tmp) {
fprintf(stderr, _("Bad number: %s\n"), optarg);
exit(1);
}
case 'p':
pidfile_path = optarg;
drop_privs++;
break;
case 'q':
quiet++;
break;
case 's':
socket_path = optarg;
drop_privs++;
break;
case 't':
do_type = UUIDD_OP_TIME_UUID;
drop_privs++;
break;
case 'T':
timeout = strtol(optarg, &tmp, 0);
if ((timeout < 0) || *tmp) {
fprintf(stderr, _("Bad number: %s\n"), optarg);
exit(1);
}
break;
case 'r':
do_type = UUIDD_OP_RANDOM_UUID;
drop_privs++;
break;
default:
usage(argv[0]);
}
}
uid = getuid();
if (uid && drop_privs) {
gid = getgid();
#ifdef HAVE_SETRESUID
setresuid(uid, uid, uid);
#else
setreuid(uid, uid);
#endif
#ifdef HAVE_SETRESGID
setresgid(gid, gid, gid);
#else
setregid(gid, gid);
#endif
}
if (num && do_type) {
ret = call_daemon(socket_path, do_type+2, buf,
sizeof(buf), &num, &err_context);
if (ret < 0) {
printf(_("Error calling uuidd daemon (%s): %s\n"),
err_context, strerror(errno));
exit(1);
}
if (do_type == UUIDD_OP_TIME_UUID) {
if (ret != sizeof(uu) + sizeof(num))
goto unexpected_size;
uuid_unparse(buf, str);
printf(_("%s and subsequent %d UUID's\n"), str, num);
} else {
printf(_("List of UUID's:\n"));
cp = buf + 4;
if (ret != sizeof(num) + num*sizeof(uu))
goto unexpected_size;
for (i=0; i < num; i++, cp+=16) {
uuid_unparse(cp, str);
printf("\t%s\n", str);
}
}
exit(0);
}
if (do_type) {
ret = call_daemon(socket_path, do_type, (unsigned char *) &uu,
sizeof(uu), 0, &err_context);
if (ret < 0) {
printf(_("Error calling uuidd daemon (%s): %s\n"),
err_context, strerror(errno));
exit(1);
}
if (ret != sizeof(uu)) {
unexpected_size:
printf(_("Unexpected reply length from server %d\n"),
ret);
exit(1);
}
uuid_unparse(uu, str);
printf("%s\n", str);
exit(0);
}
/*
* Check to make sure there isn't another daemon running already
*/
ret = call_daemon(socket_path, 0, buf, sizeof(buf), 0, 0);
if (ret > 0) {
if (do_kill && ((do_kill = atoi((char *) buf)) > 0)) {
ret = kill(do_kill, SIGTERM);
if (ret < 0) {
if (!quiet)
fprintf(stderr,
_("Couldn't kill uuidd running "
"at pid %d: %s\n"), do_kill,
strerror(errno));
exit(1);
}
if (!quiet)
printf(_("Killed uuidd running at pid %d\n"),
do_kill);
exit(0);
}
if (!quiet)
printf(_("uuidd daemon already running at pid %s\n"),
buf);
exit(1);
}
if (do_kill)
exit(0); /* Nothing to kill */
server_loop(socket_path, debug, pidfile_path, timeout, quiet);
return 0;
}

55
misc/uuidd.rc Normal file
View File

@ -0,0 +1,55 @@
#! /bin/sh -e
### BEGIN INIT INFO
# Provides: uuidd
# Required-Start: $time $local_fs
# Required-Stop: $time $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: uuidd daemon
# Description: Init script for the uuid generation daemon
### END INIT INFO
#
# Author: "Theodore Ts'o" <tytso@mit.edu>
#
set -e
PATH=/bin:/usr/bin:/sbin:/usr/sbin
DAEMON=/usr/sbin/uuidd
PIDFILE=/var/run/uuidd/uuidd.pid
test -x $DAEMON || exit 0
. /lib/lsb/init-functions
case "$1" in
start)
log_daemon_msg "Starting uuid generator" "uuidd"
start_daemon -p $PIDFILE $DAEMON
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping uuidd generator" "uuidd"
killproc -p $PIDFILE $DAEMON
log_end_msg $?
;;
status)
if pidofproc -p $PIDFILE $DAEMON >& /dev/null ; then
echo "$DAEMON is running";
exit 0;
else
echo "$DAEMON is NOT running";
if test -f /var/run/uuidd.pid; then exit 2; fi
exit 3;
fi
;;
force-reload|restart)
$0 stop
$0 start
;;
*)
echo "Usage: /etc/init.d/uuidd {start|stop|restart|force-reload}"
exit 1
;;
esac
exit 0