QSysInfo: add a function that returns a somewhat permanent unique ID

We can use the D-Bus / systemd machine-id file (which is a UUID without
the dashes) on systems with D-Bus. On Windows, there's a value in the
registry that is filled when Windows is installed, like on Linux. For
BSD systems, the kernel has a UUID we can use too, so extract that.

Task-number: QTBUG-63425
Change-Id: I27eaacb532114dd188c4ffff13d32f2e3c1d74bb
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Thiago Macieira 2015-04-08 16:56:22 -07:00
parent dfbf5a7779
commit 16fba4a28f
3 changed files with 91 additions and 3 deletions

View File

@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -73,6 +73,10 @@
#include <Ws2tcpip.h>
#endif // Q_OS_WINRT
#ifdef Q_OS_WIN
# include <qt_windows.h>
#endif
#if defined(Q_OS_VXWORKS) && defined(_WRS_KERNEL)
# include <envLib.h>
#endif
@ -2818,10 +2822,11 @@ QString QSysInfo::prettyProductName()
This function returns the same as QHostInfo::localHostName().
\sa QHostInfo::localDomainName
\sa QHostInfo::localDomainName, machineUniqueId()
*/
QString QSysInfo::machineHostName()
{
// the hostname can change, so we can't cache it
#if defined(Q_OS_LINUX)
// gethostname(3) on Linux just calls uname(2), so do it ourselves
// and avoid a memcpy
@ -2844,6 +2849,87 @@ QString QSysInfo::machineHostName()
}
#endif // QT_BOOTSTRAPPED
enum {
UuidStringLen = sizeof("00000000-0000-0000-0000-000000000000") - 1
};
/*!
\since 5.10
Returns a unique ID for this machine, if one can be determined. If no
unique ID could be determined, this function returns an empty byte array.
Unlike machineHostName(), the value returned by this function is likely
globally unique.
A unique ID is useful in network operations to identify this machine for an
extended period of time, when the IP address could change or if this
machine could have more than one IP address. For example, the ID could be
used when communicating with a server or when storing device-specific data
in shared network storage.
Note that on some systems, this value will persist across reboots and on
some it will not. Applications should not blindly depend on this fact
without verifying the OS capabilities. In particular, on Linux systems,
this ID is usually permanent and it matches the D-Bus machine ID, except
for nodes without their own storage (replicated nodes).
\sa machineHostName()
*/
QByteArray QSysInfo::machineUniqueId()
{
// the machine unique ID cannot change
static const QByteArray cache = []() {
#ifdef Q_OS_BSD4
char uuid[UuidStringLen];
size_t uuidlen = sizeof(uuid);
# ifdef KERN_HOSTUUID
int name[] = { CTL_KERN, KERN_HOSTUUID };
if (sysctl(name, sizeof name / sizeof name[0], &uuid, &uuidlen, nullptr, 0) == 0
&& uuidlen == sizeof(uuid))
return QByteArray(uuid, uuidlen);
# else
// Darwin: no fixed value, we need to search by name
if (sysctlbyname("kern.uuid", uuid, &uuidlen, nullptr, 0) == 0 && uuidlen == sizeof(uuid))
return QByteArray(uuid, uuidlen);
# endif
#elif defined(Q_OS_UNIX)
// the modern name on Linux is /etc/machine-id, but that path is
// unlikely to exist on non-Linux (non-systemd) systems. The old
// path is more than enough.
static const char fullfilename[] = "/usr/local/var/lib/dbus/machine-id";
const char *firstfilename = fullfilename + sizeof("/usr/local") - 1;
int fd = qt_safe_open(firstfilename, O_RDONLY);
if (fd == -1 && errno == ENOENT)
fd = qt_safe_open(fullfilename, O_RDONLY);
if (fd != -1) {
char buffer[32]; // 128 bits, hex-encoded
qint64 len = qt_safe_read(fd, buffer, sizeof(buffer));
qt_safe_close(fd);
if (len != -1)
return QByteArray(buffer, len);
}
#elif defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
// Let's poke at the registry
HKEY key = NULL;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Cryptography", 0, KEY_READ, &key)
== ERROR_SUCCESS) {
wchar_t buffer[UuidStringLen + 1];
DWORD size = sizeof(buffer);
bool ok = (RegQueryValueEx(key, L"MachineGuid", NULL, NULL, (LPBYTE)buffer, &size) ==
ERROR_SUCCESS);
RegCloseKey(key);
if (ok)
return QStringView(buffer, (size - 1) / 2).toLatin1();
}
#endif
return QByteArray();
}();
return cache;
}
/*!
\macro void Q_ASSERT(bool test)
\relates <QtGlobal>

View File

@ -240,6 +240,7 @@ QT_WARNING_POP
static QString prettyProductName();
static QString machineHostName();
static QByteArray machineUniqueId();
};
#undef QT_SYSINFO_DEPRECATED_X

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 Intel Corporation.
** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
@ -133,6 +133,7 @@ int main(int argc, char *argv[])
printf("QSysInfo::productVersion() = %s\n", qPrintable(QSysInfo::productVersion()));
printf("QSysInfo::prettyProductName() = %s\n", qPrintable(QSysInfo::prettyProductName()));
printf("QSysInfo::machineHostName() = %s\n", qPrintable(QSysInfo::machineHostName()));
printf("QSysInfo::machineUniqueId() = %s\n", QSysInfo::machineUniqueId().constData());
const auto osv = QOperatingSystemVersion::current();
printf("QOperatingSystemVersion::current() = %s %d.%d.%d\n",