// file : libbutl/uuid-linux.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #ifndef BUILD2_BOOTSTRAP #include #include #include #include #include #include // move() #include #include // function_cast() using namespace std; namespace butl { // While we can safely assume libuuid.so.1 is present on every Linux machine // (it is part of the essential util-linux package since version 2.15.1), // the development files (uuid.h, .so/.a symlinks) are a different story. // So for maximum portability we will use dlopen()/dlsym() to "link" to this // library without any development files. // // It appears that not all execution paths in uuid_generate() are thread- // safe (see Ubuntu bug #1005878). So for now the calls are serialized but // maybe this could be optimized (e.g., if we can be sure a thread-safe path // will be taken). Note also that we may still end up in trouble if someone // else in the process calls libuuid directly. // // Note also that the Linux kernel has support for generatin random UUIDs // which is exposed to userspace via /proc/sys/kernel/random/uuid. This // could be another implementation option (though it's not clear since which // version it is available, seem at least from the 2.6 days). // using lock = unique_lock; static mutex uuid_mutex; // // using uuid_t = unsigned char[16]; static void (*uuid_generate) (uuid_t); static int (*uuid_generate_time_safe) (uuid_t); static void* libuuid; static inline void dlfail (string what) { what += ": "; what += dlerror (); throw system_error (ENOSYS, system_category (), move (what)); }; void uuid_system_generator:: initialize () { assert (libuuid == nullptr); libuuid = dlopen ("libuuid.so.1", RTLD_LAZY | RTLD_GLOBAL); if (libuuid == nullptr) dlfail ("unable to load libuuid.so.1"); uuid_generate = function_cast ( dlsym (libuuid, "uuid_generate")); if (uuid_generate == nullptr) dlfail ("unable to lookup uuid_generate() in libuuid.so.1"); uuid_generate_time_safe = function_cast ( dlsym (libuuid, "uuid_generate_time_safe")); // Delay the failure until/if we need this function (it was only added in // 2011 so may not be available on older systems). // //if (uuid_generate_time_safe == nullptr) // dlfail ("unable to lookup uuid_generate_time_safe() in libuuid.so.1"); } void uuid_system_generator:: terminate () { assert (libuuid != nullptr); if (dlclose (libuuid) != 0) dlfail ("unable to unload libuuid.so.1"); libuuid = nullptr; } void uuid_throw_weak (); // uuid.cxx uuid uuid_system_generator:: generate (bool strong) { lock l (uuid_mutex); if (libuuid == nullptr) initialize (); uuid_t d; uuid_generate (d); uuid r (d); assert (r.variant () == uuid_variant::dce); // Sanity check. // According to the uuid_generate() documentation (and confirmed by the // implementation) it generates a random uuid if high-quality randomness // is available and a MAC/time-based one otherwise (in other words, it // should never generate a pseudo-random UUID). // if (strong && r.version () != uuid_version::random) { if (uuid_generate_time_safe == nullptr || uuid_generate_time_safe (d) == -1) uuid_throw_weak (); r.assign (d); assert (r.variant () == uuid_variant::dce); } return r; } } #endif // BUILD2_BOOTSTRAP