diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 0a3ebd0b87..702322344d 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -75,7 +75,7 @@ qt_internal_add_module(Core global/qprocessordetection.h global/qrandom.cpp global/qrandom.h global/qrandom_p.h global/qswap.h - global/qsysinfo.h + global/qsysinfo.cpp global/qsysinfo.h global/qsystemdetection.h global/qtclasshelpermacros.h global/qtconfigmacros.h diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index 1228c98863..4b53b7e9f7 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -8,12 +8,6 @@ #include "qlist.h" #include "qdir.h" #include "qdatetime.h" -#include "qoperatingsystemversion.h" -#include "qoperatingsystemversion_p.h" -#if defined(Q_OS_WIN) || defined(Q_OS_CYGWIN) -# include "qoperatingsystemversion_win_p.h" -# include "private/qwinregistry_p.h" -#endif // Q_OS_WIN || Q_OS_CYGWIN #include #include "qnativeinterface.h" #include "qnativeinterface_p.h" @@ -35,28 +29,6 @@ # include #endif -#ifdef Q_OS_ANDROID -#include -#endif - -#if defined(Q_OS_SOLARIS) -# include -#endif - -#if defined(Q_OS_DARWIN) && __has_include() -# include -# include -#endif - -#ifdef Q_OS_UNIX -#include -#include -#endif - -#ifdef Q_OS_BSD4 -#include -#endif - #if defined(Q_OS_INTEGRITY) extern "C" { // Function mmap resides in libshm_client.a. To be able to link with it one needs @@ -70,8 +42,6 @@ extern "C" { } #endif -#include "archdetect.cpp" - #ifdef qFatal // the qFatal in this file are just redirections from elsewhere, so // don't capture any context again @@ -207,44 +177,6 @@ using namespace Qt::StringLiterals; System detection routines *****************************************************************************/ -/*! - \class QSysInfo - \inmodule QtCore - \brief The QSysInfo class provides information about the system. - - \list - \li \l WordSize specifies the size of a pointer for the platform - on which the application is compiled. - \li \l ByteOrder specifies whether the platform is big-endian or - little-endian. - \endlist - - Some constants are defined only on certain platforms. You can use - the preprocessor symbols Q_OS_WIN and Q_OS_MACOS to test that - the application is compiled under Windows or \macos. - - \sa QLibraryInfo -*/ - -/*! - \enum QSysInfo::Sizes - - This enum provides platform-specific information about the sizes of data - structures used by the underlying architecture. - - \value WordSize The size in bits of a pointer for the platform on which - the application is compiled (32 or 64). -*/ - -/*! - \enum QSysInfo::Endian - - \value BigEndian Big-endian byte order (also called Network byte order) - \value LittleEndian Little-endian byte order - \value ByteOrder Equals BigEndian or LittleEndian, depending on - the platform's byte order. -*/ - /*! \macro Q_OS_DARWIN \relates @@ -945,984 +877,6 @@ using namespace Qt::StringLiterals; \sa QT_DISABLE_DEPRECATED_BEFORE */ -#if defined(Q_OS_MAC) - -QT_BEGIN_INCLUDE_NAMESPACE -#include "private/qcore_mac_p.h" -#include "qnamespace.h" -QT_END_INCLUDE_NAMESPACE - -#ifdef Q_OS_DARWIN -static const char *osVer_helper(QOperatingSystemVersion version = QOperatingSystemVersion::current()) -{ -#ifdef Q_OS_MACOS - if (version.majorVersion() == 12) - return "Monterey"; - // Compare against predefined constant to handle 10.16/11.0 - if (QOperatingSystemVersion::MacOSBigSur.version().isPrefixOf(version.version())) - return "Big Sur"; - if (version.majorVersion() == 10) { - switch (version.minorVersion()) { - case 9: - return "Mavericks"; - case 10: - return "Yosemite"; - case 11: - return "El Capitan"; - case 12: - return "Sierra"; - case 13: - return "High Sierra"; - case 14: - return "Mojave"; - case 15: - return "Catalina"; - } - } - // unknown, future version -#else - Q_UNUSED(version); -#endif - return nullptr; -} -#endif - -#elif defined(Q_OS_WIN) || defined(Q_OS_CYGWIN) - -QT_BEGIN_INCLUDE_NAMESPACE -#include "qt_windows.h" -QT_END_INCLUDE_NAMESPACE - -# ifndef QT_BOOTSTRAPPED -class QWindowsSockInit -{ -public: - QWindowsSockInit(); - ~QWindowsSockInit(); - int version; -}; - -QWindowsSockInit::QWindowsSockInit() -: version(0) -{ - //### should we try for 2.2 on all platforms ?? - WSAData wsadata; - - // IPv6 requires Winsock v2.0 or better. - if (WSAStartup(MAKEWORD(2, 0), &wsadata) != 0) { - qWarning("QTcpSocketAPI: WinSock v2.0 initialization failed."); - } else { - version = 0x20; - } -} - -QWindowsSockInit::~QWindowsSockInit() -{ - WSACleanup(); -} -Q_GLOBAL_STATIC(QWindowsSockInit, winsockInit) -# endif // QT_BOOTSTRAPPED - -static QString readVersionRegistryString(const wchar_t *subKey) -{ - return QWinRegistryKey(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)") - .stringValue(subKey); -} - -static inline QString windowsDisplayVersion() -{ - // https://tickets.puppetlabs.com/browse/FACT-3058 - // The "ReleaseId" key stopped updating since Windows 10 20H2. - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10_20H2) - return readVersionRegistryString(L"DisplayVersion"); - else - return readVersionRegistryString(L"ReleaseId"); -} - -static QString winSp_helper() -{ - const auto osv = qWindowsVersionInfo(); - const qint16 major = osv.wServicePackMajor; - if (major) { - QString sp = QStringLiteral("SP ") + QString::number(major); - const qint16 minor = osv.wServicePackMinor; - if (minor) - sp += u'.' + QString::number(minor); - - return sp; - } - return QString(); -} - -static const char *osVer_helper(QOperatingSystemVersion version = QOperatingSystemVersion::current()) -{ - Q_UNUSED(version); - const OSVERSIONINFOEX osver = qWindowsVersionInfo(); - const bool workstation = osver.wProductType == VER_NT_WORKSTATION; - -#define Q_WINVER(major, minor) (major << 8 | minor) - switch (Q_WINVER(osver.dwMajorVersion, osver.dwMinorVersion)) { - case Q_WINVER(10, 0): - if (workstation) { - if (osver.dwBuildNumber >= 22000) - return "11"; - return "10"; - } - // else: Server - if (osver.dwBuildNumber >= 20348) - return "Server 2022"; - if (osver.dwBuildNumber >= 17763) - return "Server 2019"; - return "Server 2016"; - } -#undef Q_WINVER - // unknown, future version - return nullptr; -} - -#endif -#if defined(Q_OS_UNIX) -# if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_FREEBSD) -# define USE_ETC_OS_RELEASE -struct QUnixOSVersion -{ - // from /etc/os-release older /etc/lsb-release // redhat /etc/redhat-release // debian /etc/debian_version - QString productType; // $ID $DISTRIB_ID // single line file containing: // Debian - QString productVersion; // $VERSION_ID $DISTRIB_RELEASE // // single line file - QString prettyName; // $PRETTY_NAME $DISTRIB_DESCRIPTION -}; - -static QString unquote(const char *begin, const char *end) -{ - // man os-release says: - // Variable assignment values must be enclosed in double - // or single quotes if they include spaces, semicolons or - // other special characters outside of A–Z, a–z, 0–9. Shell - // special characters ("$", quotes, backslash, backtick) - // must be escaped with backslashes, following shell style. - // All strings should be in UTF-8 format, and non-printable - // characters should not be used. It is not supported to - // concatenate multiple individually quoted strings. - if (*begin == '"') { - Q_ASSERT(end[-1] == '"'); - return QString::fromUtf8(begin + 1, end - begin - 2); - } - return QString::fromUtf8(begin, end - begin); -} -static QByteArray getEtcFileContent(const char *filename) -{ - // we're avoiding QFile here - int fd = qt_safe_open(filename, O_RDONLY); - if (fd == -1) - return QByteArray(); - - QT_STATBUF sbuf; - if (QT_FSTAT(fd, &sbuf) == -1) { - qt_safe_close(fd); - return QByteArray(); - } - - QByteArray buffer(sbuf.st_size, Qt::Uninitialized); - buffer.resize(qt_safe_read(fd, buffer.data(), sbuf.st_size)); - qt_safe_close(fd); - return buffer; -} - -static bool readEtcFile(QUnixOSVersion &v, const char *filename, - const QByteArray &idKey, const QByteArray &versionKey, const QByteArray &prettyNameKey) -{ - - QByteArray buffer = getEtcFileContent(filename); - if (buffer.isEmpty()) - return false; - - const char *ptr = buffer.constData(); - const char *end = buffer.constEnd(); - const char *eol; - QByteArray line; - for (; ptr != end; ptr = eol + 1) { - // find the end of the line after ptr - eol = static_cast(memchr(ptr, '\n', end - ptr)); - if (!eol) - eol = end - 1; - line.setRawData(ptr, eol - ptr); - - if (line.startsWith(idKey)) { - ptr += idKey.length(); - v.productType = unquote(ptr, eol); - continue; - } - - if (line.startsWith(prettyNameKey)) { - ptr += prettyNameKey.length(); - v.prettyName = unquote(ptr, eol); - continue; - } - - if (line.startsWith(versionKey)) { - ptr += versionKey.length(); - v.productVersion = unquote(ptr, eol); - continue; - } - } - - return true; -} - -static bool readOsRelease(QUnixOSVersion &v) -{ - QByteArray id = QByteArrayLiteral("ID="); - QByteArray versionId = QByteArrayLiteral("VERSION_ID="); - QByteArray prettyName = QByteArrayLiteral("PRETTY_NAME="); - - // man os-release(5) says: - // The file /etc/os-release takes precedence over /usr/lib/os-release. - // Applications should check for the former, and exclusively use its data - // if it exists, and only fall back to /usr/lib/os-release if it is - // missing. - return readEtcFile(v, "/etc/os-release", id, versionId, prettyName) || - readEtcFile(v, "/usr/lib/os-release", id, versionId, prettyName); -} - -static bool readEtcLsbRelease(QUnixOSVersion &v) -{ - bool ok = readEtcFile(v, "/etc/lsb-release", QByteArrayLiteral("DISTRIB_ID="), - QByteArrayLiteral("DISTRIB_RELEASE="), QByteArrayLiteral("DISTRIB_DESCRIPTION=")); - if (ok && (v.prettyName.isEmpty() || v.prettyName == v.productType)) { - // some distributions have redundant information for the pretty name, - // so try /etc/-release - - // we're still avoiding QFile here - QByteArray distrorelease = "/etc/" + v.productType.toLatin1().toLower() + "-release"; - int fd = qt_safe_open(distrorelease, O_RDONLY); - if (fd != -1) { - QT_STATBUF sbuf; - if (QT_FSTAT(fd, &sbuf) != -1 && sbuf.st_size > v.prettyName.length()) { - // file apparently contains interesting information - QByteArray buffer(sbuf.st_size, Qt::Uninitialized); - buffer.resize(qt_safe_read(fd, buffer.data(), sbuf.st_size)); - v.prettyName = QString::fromLatin1(buffer.trimmed()); - } - qt_safe_close(fd); - } - } - - // some distributions have a /etc/lsb-release file that does not provide the values - // we are looking for, i.e. DISTRIB_ID, DISTRIB_RELEASE and DISTRIB_DESCRIPTION. - // Assuming that neither DISTRIB_ID nor DISTRIB_RELEASE were found, or contained valid values, - // returning false for readEtcLsbRelease will allow further /etc/-release parsing. - return ok && !(v.productType.isEmpty() && v.productVersion.isEmpty()); -} - -#if defined(Q_OS_LINUX) -static QByteArray getEtcFileFirstLine(const char *fileName) -{ - QByteArray buffer = getEtcFileContent(fileName); - if (buffer.isEmpty()) - return QByteArray(); - - const char *ptr = buffer.constData(); - int eol = buffer.indexOf("\n"); - return QByteArray(ptr, eol).trimmed(); -} - -static bool readEtcRedHatRelease(QUnixOSVersion &v) -{ - // /etc/redhat-release analysed should be a one line file - // the format of its content is - // i.e. "Red Hat Enterprise Linux Workstation release 6.5 (Santiago)" - QByteArray line = getEtcFileFirstLine("/etc/redhat-release"); - if (line.isEmpty()) - return false; - - v.prettyName = QString::fromLatin1(line); - - const char keyword[] = "release "; - int releaseIndex = line.indexOf(keyword); - v.productType = QString::fromLatin1(line.mid(0, releaseIndex)).remove(u' '); - int spaceIndex = line.indexOf(' ', releaseIndex + strlen(keyword)); - v.productVersion = QString::fromLatin1(line.mid(releaseIndex + strlen(keyword), - spaceIndex > -1 ? spaceIndex - releaseIndex - int(strlen(keyword)) : -1)); - return true; -} - -static bool readEtcDebianVersion(QUnixOSVersion &v) -{ - // /etc/debian_version analysed should be a one line file - // the format of its content is - // i.e. "jessie/sid" - QByteArray line = getEtcFileFirstLine("/etc/debian_version"); - if (line.isEmpty()) - return false; - - v.productType = QStringLiteral("Debian"); - v.productVersion = QString::fromLatin1(line); - return true; -} -#endif - -static bool findUnixOsVersion(QUnixOSVersion &v) -{ - if (readOsRelease(v)) - return true; - if (readEtcLsbRelease(v)) - return true; -#if defined(Q_OS_LINUX) - if (readEtcRedHatRelease(v)) - return true; - if (readEtcDebianVersion(v)) - return true; -#endif - return false; -} -# endif // USE_ETC_OS_RELEASE -#endif // Q_OS_UNIX - -#ifdef Q_OS_ANDROID -static const char *osVer_helper(QOperatingSystemVersion) -{ -/* Data: - - - -Cupcake -Donut -Eclair -Eclair -Eclair -Froyo -Gingerbread -Gingerbread -Honeycomb -Honeycomb -Honeycomb -Ice Cream Sandwich -Ice Cream Sandwich -Jelly Bean -Jelly Bean -Jelly Bean -KitKat -KitKat -Lollipop -Lollipop -Marshmallow -Nougat -Nougat -Oreo -*/ - static const char versions_string[] = - "\0" - "Cupcake\0" - "Donut\0" - "Eclair\0" - "Froyo\0" - "Gingerbread\0" - "Honeycomb\0" - "Ice Cream Sandwich\0" - "Jelly Bean\0" - "KitKat\0" - "Lollipop\0" - "Marshmallow\0" - "Nougat\0" - "Oreo\0" - "\0"; - - static const int versions_indices[] = { - 0, 0, 0, 1, 9, 15, 15, 15, - 22, 28, 28, 40, 40, 40, 50, 50, - 69, 69, 69, 80, 80, 87, 87, 96, - 108, 108, 115, -1 - }; - - static const int versions_count = (sizeof versions_indices) / (sizeof versions_indices[0]); - - // https://source.android.com/source/build-numbers.html - // https://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels - const int sdk_int = QJniObject::getStaticField("android/os/Build$VERSION", "SDK_INT"); - return &versions_string[versions_indices[qBound(0, sdk_int, versions_count - 1)]]; -} -#endif - -/*! - \since 5.4 - - Returns the architecture of the CPU that Qt was compiled for, in text - format. Note that this may not match the actual CPU that the application is - running on if there's an emulation layer or if the CPU supports multiple - architectures (like x86-64 processors supporting i386 applications). To - detect that, use currentCpuArchitecture(). - - Values returned by this function are stable and will not change over time, - so applications can rely on the returned value as an identifier, except - that new CPU types may be added over time. - - Typical returned values are (note: list not exhaustive): - \list - \li "arm" - \li "arm64" - \li "i386" - \li "ia64" - \li "mips" - \li "mips64" - \li "power" - \li "power64" - \li "sparc" - \li "sparcv9" - \li "x86_64" - \endlist - - \sa QSysInfo::buildAbi(), QSysInfo::currentCpuArchitecture() -*/ -QString QSysInfo::buildCpuArchitecture() -{ - return QStringLiteral(ARCH_PROCESSOR); -} - -/*! - \since 5.4 - - Returns the architecture of the CPU that the application is running on, in - text format. Note that this function depends on what the OS will report and - may not detect the actual CPU architecture if the OS hides that information - or is unable to provide it. For example, a 32-bit OS running on a 64-bit - CPU is usually unable to determine the CPU is actually capable of running - 64-bit programs. - - Values returned by this function are mostly stable: an attempt will be made - to ensure that they stay constant over time and match the values returned - by QSysInfo::builldCpuArchitecture(). However, due to the nature of the - operating system functions being used, there may be discrepancies. - - Typical returned values are (note: list not exhaustive): - \list - \li "arm" - \li "arm64" - \li "i386" - \li "ia64" - \li "mips" - \li "mips64" - \li "power" - \li "power64" - \li "sparc" - \li "sparcv9" - \li "x86_64" - \endlist - - \sa QSysInfo::buildAbi(), QSysInfo::buildCpuArchitecture() -*/ -QString QSysInfo::currentCpuArchitecture() -{ -#if defined(Q_OS_WIN) - // We don't need to catch all the CPU architectures in this function; - // only those where the host CPU might be different than the build target - // (usually, 64-bit platforms). - SYSTEM_INFO info; - GetNativeSystemInfo(&info); - switch (info.wProcessorArchitecture) { -# ifdef PROCESSOR_ARCHITECTURE_AMD64 - case PROCESSOR_ARCHITECTURE_AMD64: - return QStringLiteral("x86_64"); -# endif -# ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 - case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: -# endif - case PROCESSOR_ARCHITECTURE_IA64: - return QStringLiteral("ia64"); - } -#elif defined(Q_OS_DARWIN) && !defined(Q_OS_MACOS) - // iOS-based OSes do not return the architecture on uname(2)'s result. - return buildCpuArchitecture(); -#elif defined(Q_OS_UNIX) - long ret = -1; - struct utsname u; - -# if defined(Q_OS_SOLARIS) - // We need a special call for Solaris because uname(2) on x86 returns "i86pc" for - // both 32- and 64-bit CPUs. Reference: - // http://docs.oracle.com/cd/E18752_01/html/816-5167/sysinfo-2.html#REFMAN2sysinfo-2 - // http://fxr.watson.org/fxr/source/common/syscall/systeminfo.c?v=OPENSOLARIS - // http://fxr.watson.org/fxr/source/common/conf/param.c?v=OPENSOLARIS;im=10#L530 - if (ret == -1) - ret = sysinfo(SI_ARCHITECTURE_64, u.machine, sizeof u.machine); -# endif - - if (ret == -1) - ret = uname(&u); - - // we could use detectUnixVersion() above, but we only need a field no other function does - if (ret != -1) { - // the use of QT_BUILD_INTERNAL here is simply to ensure all branches build - // as we don't often build on some of the less common platforms -# if defined(Q_PROCESSOR_ARM) || defined(QT_BUILD_INTERNAL) - if (strcmp(u.machine, "aarch64") == 0) - return QStringLiteral("arm64"); - if (strncmp(u.machine, "armv", 4) == 0) - return QStringLiteral("arm"); -# endif -# if defined(Q_PROCESSOR_POWER) || defined(QT_BUILD_INTERNAL) - // harmonize "powerpc" and "ppc" to "power" - if (strncmp(u.machine, "ppc", 3) == 0) - return "power"_L1 + QLatin1StringView(u.machine + 3); - if (strncmp(u.machine, "powerpc", 7) == 0) - return "power"_L1 + QLatin1StringView(u.machine + 7); - if (strcmp(u.machine, "Power Macintosh") == 0) - return "power"_L1; -# endif -# if defined(Q_PROCESSOR_SPARC) || defined(QT_BUILD_INTERNAL) - // Solaris sysinfo(2) (above) uses "sparcv9", but uname -m says "sun4u"; - // Linux says "sparc64" - if (strcmp(u.machine, "sun4u") == 0 || strcmp(u.machine, "sparc64") == 0) - return QStringLiteral("sparcv9"); - if (strcmp(u.machine, "sparc32") == 0) - return QStringLiteral("sparc"); -# endif -# if defined(Q_PROCESSOR_X86) || defined(QT_BUILD_INTERNAL) - // harmonize all "i?86" to "i386" - if (strlen(u.machine) == 4 && u.machine[0] == 'i' - && u.machine[2] == '8' && u.machine[3] == '6') - return QStringLiteral("i386"); - if (strcmp(u.machine, "amd64") == 0) // Solaris - return QStringLiteral("x86_64"); -# endif - return QString::fromLatin1(u.machine); - } -#endif - return buildCpuArchitecture(); -} - -/*! - \since 5.4 - - Returns the full architecture string that Qt was compiled for. This string - is useful for identifying different, incompatible builds. For example, it - can be used as an identifier to request an upgrade package from a server. - - The values returned from this function are kept stable as follows: the - mandatory components of the result will not change in future versions of - Qt, but optional suffixes may be added. - - The returned value is composed of three or more parts, separated by dashes - ("-"). They are: - - \table - \header \li Component \li Value - \row \li CPU Architecture \li The same as QSysInfo::buildCpuArchitecture(), such as "arm", "i386", "mips" or "x86_64" - \row \li Endianness \li "little_endian" or "big_endian" - \row \li Word size \li Whether it's a 32- or 64-bit application. Possible values are: - "llp64" (Windows 64-bit), "lp64" (Unix 64-bit), "ilp32" (32-bit) - \row \li (Optional) ABI \li Zero or more components identifying different ABIs possible in this architecture. - Currently, Qt has optional ABI components for ARM and MIPS processors: one - component is the main ABI (such as "eabi", "o32", "n32", "o64"); another is - whether the calling convention is using hardware floating point registers ("hardfloat" - is present). - - Additionally, if Qt was configured with \c{-qreal float}, the ABI option tag "qreal_float" - will be present. If Qt was configured with another type as qreal, that type is present after - "qreal_", with all characters other than letters and digits escaped by an underscore, followed - by two hex digits. For example, \c{-qreal long double} becomes "qreal_long_20double". - \endtable - - \sa QSysInfo::buildCpuArchitecture() -*/ -QString QSysInfo::buildAbi() -{ - // ARCH_FULL is a concatenation of strings (incl. ARCH_PROCESSOR), which breaks - // QStringLiteral on MSVC. Since the concatenation behavior we want is specified - // the same C++11 paper as the Unicode strings, we'll use that macro and hope - // that Microsoft implements the new behavior when they add support for Unicode strings. - return QStringLiteral(ARCH_FULL); -} - -static QString unknownText() -{ - return QStringLiteral("unknown"); -} - -/*! - \since 5.4 - - Returns the type of the operating system kernel Qt was compiled for. It's - also the kernel the application is running on, unless the host operating - system is running a form of compatibility or virtualization layer. - - Values returned by this function are stable and will not change over time, - so applications can rely on the returned value as an identifier, except - that new OS kernel types may be added over time. - - On Windows, this function returns the type of Windows kernel, like "winnt". - On Unix systems, it returns the same as the output of \c{uname - -s} (lowercased). - - \note This function may return surprising values: it returns "linux" - for all operating systems running Linux (including Android), "qnx" for all - operating systems running QNX, "freebsd" for - Debian/kFreeBSD, and "darwin" for \macos and iOS. For information on the type - of product the application is running on, see productType(). - - \sa QFileSelector, kernelVersion(), productType(), productVersion(), prettyProductName() -*/ -QString QSysInfo::kernelType() -{ -#if defined(Q_OS_WIN) - return QStringLiteral("winnt"); -#elif defined(Q_OS_UNIX) - struct utsname u; - if (uname(&u) == 0) - return QString::fromLatin1(u.sysname).toLower(); -#endif - return unknownText(); -} - -/*! - \since 5.4 - - Returns the release version of the operating system kernel. On Windows, it - returns the version of the NT kernel. On Unix systems, including - Android and \macos, it returns the same as the \c{uname -r} - command would return. - - If the version could not be determined, this function may return an empty - string. - - \sa kernelType(), productType(), productVersion(), prettyProductName() -*/ -QString QSysInfo::kernelVersion() -{ -#ifdef Q_OS_WIN - const auto osver = QOperatingSystemVersion::current(); - return QString::asprintf("%d.%d.%d", - osver.majorVersion(), osver.minorVersion(), osver.microVersion()); -#else - struct utsname u; - if (uname(&u) == 0) - return QString::fromLatin1(u.release); - return QString(); -#endif -} - - -/*! - \since 5.4 - - Returns the product name of the operating system this application is - running in. If the application is running on some sort of emulation or - virtualization layer (such as WINE on a Unix system), this function will - inspect the emulation / virtualization layer. - - Values returned by this function are stable and will not change over time, - so applications can rely on the returned value as an identifier, except - that new OS types may be added over time. - - \b{Linux and Android note}: this function returns "android" for Linux - systems running Android userspace, notably when using the Bionic library. - For all other Linux systems, regardless of C library being used, it tries - to determine the distribution name and returns that. If determining the - distribution name failed, it returns "unknown". - - \b{\macos note}: this function returns "macos" for all \macos systems, - regardless of Apple naming convention. Previously, in Qt 5, it returned - "osx", again regardless of Apple naming conventions. - - \b{Darwin, iOS, tvOS, and watchOS note}: this function returns "ios" for - iOS systems, "tvos" for tvOS systems, "watchos" for watchOS systems, and - "darwin" in case the system could not be determined. - - \b{FreeBSD note}: this function returns "debian" for Debian/kFreeBSD and - "unknown" otherwise. - - \b{Windows note}: this function return "windows" - - For other Unix-type systems, this function usually returns "unknown". - - \sa QFileSelector, kernelType(), kernelVersion(), productVersion(), prettyProductName() -*/ -QString QSysInfo::productType() -{ - // similar, but not identical to QFileSelectorPrivate::platformSelectors -#if defined(Q_OS_WIN) - return QStringLiteral("windows"); - -#elif defined(Q_OS_QNX) - return QStringLiteral("qnx"); - -#elif defined(Q_OS_ANDROID) - return QStringLiteral("android"); - -#elif defined(Q_OS_IOS) - return QStringLiteral("ios"); -#elif defined(Q_OS_TVOS) - return QStringLiteral("tvos"); -#elif defined(Q_OS_WATCHOS) - return QStringLiteral("watchos"); -#elif defined(Q_OS_MACOS) - return QStringLiteral("macos"); -#elif defined(Q_OS_DARWIN) - return QStringLiteral("darwin"); - -#elif defined(USE_ETC_OS_RELEASE) // Q_OS_UNIX - QUnixOSVersion unixOsVersion; - findUnixOsVersion(unixOsVersion); - if (!unixOsVersion.productType.isEmpty()) - return unixOsVersion.productType; -#endif - return unknownText(); -} - -/*! - \since 5.4 - - Returns the product version of the operating system in string form. If the - version could not be determined, this function returns "unknown". - - It will return the Android, iOS, \macos, Windows full-product - versions on those systems. - - Typical returned values are (note: list not exhaustive): - \list - \li "12" (Android 12) - \li "36" (Fedora 36) - \li "15.5" (iOS 15.5) - \li "12.4" (macOS Monterey) - \li "22.04" (Ubuntu 22.04) - \li "8.6" (watchOS 8.6) - \li "11" (Windows 11) - \li "Server 2022" (Windows Server 2022) - \endlist - - On Linux systems, it will try to determine the distribution version and will - return that. This is also done on Debian/kFreeBSD, so this function will - return Debian version in that case. - - In all other Unix-type systems, this function always returns "unknown". - - \note The version string returned from this function is not guaranteed to - be orderable. On Linux, the version of - the distribution may jump unexpectedly, please refer to the distribution's - documentation for versioning practices. - - \sa kernelType(), kernelVersion(), productType(), prettyProductName() -*/ -QString QSysInfo::productVersion() -{ -#if defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN) - const auto version = QOperatingSystemVersion::current(); - return QString::asprintf("%d.%d", version.majorVersion(), version.minorVersion()); -#elif defined(Q_OS_WIN) - const char *version = osVer_helper(); - if (version) { - const QLatin1Char spaceChar(' '); - return QString::fromLatin1(version).remove(spaceChar).toLower() + winSp_helper().remove(spaceChar).toLower(); - } - // fall through - -#elif defined(USE_ETC_OS_RELEASE) // Q_OS_UNIX - QUnixOSVersion unixOsVersion; - findUnixOsVersion(unixOsVersion); - if (!unixOsVersion.productVersion.isEmpty()) - return unixOsVersion.productVersion; -#endif - - // fallback - return unknownText(); -} - -/*! - \since 5.4 - - Returns a prettier form of productType() and productVersion(), containing - other tokens like the operating system type, codenames and other - information. The result of this function is suitable for displaying to the - user, but not for long-term storage, as the string may change with updates - to Qt. - - If productType() is "unknown", this function will instead use the - kernelType() and kernelVersion() functions. - - \sa kernelType(), kernelVersion(), productType(), productVersion() -*/ -QString QSysInfo::prettyProductName() -{ -#if defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN) || defined(Q_OS_WIN) - const auto version = QOperatingSystemVersion::current(); - const int majorVersion = version.majorVersion(); - const QString versionString = QString::asprintf("%d.%d", majorVersion, version.minorVersion()); - QString result = version.name() + u' '; - const char *name = osVer_helper(version); - if (!name) - return result + versionString; - result += QLatin1StringView(name); -# if !defined(Q_OS_WIN) - return result + " ("_L1 + versionString + u')'; -# else - // (resembling winver.exe): Windows 10 "Windows 10 Version 1809" - const auto displayVersion = windowsDisplayVersion(); - if (!displayVersion.isEmpty()) - result += " Version "_L1 + displayVersion; - return result; -# endif // Windows -#elif defined(Q_OS_HAIKU) - return "Haiku "_L1 + productVersion(); -#elif defined(Q_OS_UNIX) -# ifdef USE_ETC_OS_RELEASE - QUnixOSVersion unixOsVersion; - findUnixOsVersion(unixOsVersion); - if (!unixOsVersion.prettyName.isEmpty()) - return unixOsVersion.prettyName; -# endif - struct utsname u; - if (uname(&u) == 0) - return QString::fromLatin1(u.sysname) + u' ' + QString::fromLatin1(u.release); -#endif - return unknownText(); -} - -#ifndef QT_BOOTSTRAPPED -/*! - \since 5.6 - - Returns this machine's host name, if one is configured. Note that hostnames - are not guaranteed to be globally unique, especially if they were - configured automatically. - - This function does not guarantee the returned host name is a Fully - Qualified Domain Name (FQDN). For that, use QHostInfo to resolve the - returned name to an FQDN. - - This function returns the same as QHostInfo::localHostName(). - - \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 - struct utsname u; - if (uname(&u) == 0) - return QString::fromLocal8Bit(u.nodename); - return QString(); -#else -# ifdef Q_OS_WIN - // Important: QtNetwork depends on machineHostName() initializing ws2_32.dll - winsockInit(); -# endif - - char hostName[512]; - if (gethostname(hostName, sizeof(hostName)) == -1) - return QString(); - hostName[sizeof(hostName) - 1] = '\0'; - return QString::fromLocal8Bit(hostName); -#endif -} -#endif // QT_BOOTSTRAPPED - -enum { - UuidStringLen = sizeof("00000000-0000-0000-0000-000000000000") - 1 -}; - -/*! - \since 5.11 - - 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(), bootUniqueId() -*/ -QByteArray QSysInfo::machineUniqueId() -{ -#if defined(Q_OS_DARWIN) && __has_include() - char uuid[UuidStringLen + 1]; - static const mach_port_t defaultPort = 0; // Effectively kIOMasterPortDefault/kIOMainPortDefault - io_service_t service = IOServiceGetMatchingService(defaultPort, IOServiceMatching("IOPlatformExpertDevice")); - QCFString stringRef = (CFStringRef)IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0); - CFStringGetCString(stringRef, uuid, sizeof(uuid), kCFStringEncodingMacRoman); - return QByteArray(uuid); -#elif defined(Q_OS_BSD4) && defined(KERN_HOSTUUID) - char uuid[UuidStringLen + 1]; - size_t uuidlen = sizeof(uuid); - 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 - 1); -#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) - // Let's poke at the registry - const QString machineGuid = QWinRegistryKey(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Cryptography)") - .stringValue(u"MachineGuid"_s); - if (!machineGuid.isEmpty()) - return machineGuid.toLatin1(); -#endif - return QByteArray(); -} - -/*! - \since 5.11 - - Returns a unique ID for this machine's boot, if one can be determined. If - no unique ID could be determined, this function returns an empty byte - array. This value is expected to change after every boot and can be - considered globally unique. - - This function is currently only implemented for Linux and Apple operating - systems. - - \sa machineUniqueId() -*/ -QByteArray QSysInfo::bootUniqueId() -{ -#ifdef Q_OS_LINUX - // use low-level API here for simplicity - int fd = qt_safe_open("/proc/sys/kernel/random/boot_id", O_RDONLY); - if (fd != -1) { - char uuid[UuidStringLen]; - qint64 len = qt_safe_read(fd, uuid, sizeof(uuid)); - qt_safe_close(fd); - if (len == UuidStringLen) - return QByteArray(uuid, UuidStringLen); - } -#elif defined(Q_OS_DARWIN) - // "kern.bootsessionuuid" is only available by name - char uuid[UuidStringLen + 1]; - size_t uuidlen = sizeof(uuid); - if (sysctlbyname("kern.bootsessionuuid", uuid, &uuidlen, nullptr, 0) == 0 - && uuidlen == sizeof(uuid)) - return QByteArray(uuid, uuidlen - 1); -#endif - return QByteArray(); -}; - /*! \macro void Q_FALLTHROUGH() \relates diff --git a/src/corelib/global/qsysinfo.cpp b/src/corelib/global/qsysinfo.cpp new file mode 100644 index 0000000000..55e0fa0688 --- /dev/null +++ b/src/corelib/global/qsysinfo.cpp @@ -0,0 +1,1055 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2022 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qsysinfo.h" + +#include +#include +#include + +#include + +#ifdef Q_OS_UNIX +# include +# include +#endif + +#ifdef Q_OS_ANDROID +#include +#endif + +#if defined(Q_OS_SOLARIS) +# include +#endif + +#if defined(Q_OS_DARWIN) +# include "qnamespace.h" +# include +# if __has_include() +# include +# endif +#endif + +#ifdef Q_OS_BSD4 +# include +#endif + +#if defined(Q_OS_WIN) || defined(Q_OS_CYGWIN) +# include "qoperatingsystemversion_win_p.h" +# include "private/qwinregistry_p.h" +# include "qt_windows.h" +#endif // Q_OS_WIN || Q_OS_CYGWIN + +#include "archdetect.cpp" + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +/*! + \class QSysInfo + \inmodule QtCore + \brief The QSysInfo class provides information about the system. + + \list + \li \l WordSize specifies the size of a pointer for the platform + on which the application is compiled. + \li \l ByteOrder specifies whether the platform is big-endian or + little-endian. + \endlist + + Some constants are defined only on certain platforms. You can use + the preprocessor symbols Q_OS_WIN and Q_OS_MACOS to test that + the application is compiled under Windows or \macos. + + \sa QLibraryInfo +*/ + +/*! + \enum QSysInfo::Sizes + + This enum provides platform-specific information about the sizes of data + structures used by the underlying architecture. + + \value WordSize The size in bits of a pointer for the platform on which + the application is compiled (32 or 64). +*/ + +/*! + \enum QSysInfo::Endian + + \value BigEndian Big-endian byte order (also called Network byte order) + \value LittleEndian Little-endian byte order + \value ByteOrder Equals BigEndian or LittleEndian, depending on + the platform's byte order. +*/ + +#if defined(Q_OS_DARWIN) + +static const char *osVer_helper(QOperatingSystemVersion version = QOperatingSystemVersion::current()) +{ +#ifdef Q_OS_MACOS + if (version.majorVersion() == 12) + return "Monterey"; + // Compare against predefined constant to handle 10.16/11.0 + if (QOperatingSystemVersion::MacOSBigSur.version().isPrefixOf(version.version())) + return "Big Sur"; + if (version.majorVersion() == 10) { + switch (version.minorVersion()) { + case 9: + return "Mavericks"; + case 10: + return "Yosemite"; + case 11: + return "El Capitan"; + case 12: + return "Sierra"; + case 13: + return "High Sierra"; + case 14: + return "Mojave"; + case 15: + return "Catalina"; + } + } + // unknown, future version +#else + Q_UNUSED(version); +#endif + return nullptr; +} + +#elif defined(Q_OS_WIN) || defined(Q_OS_CYGWIN) + +# ifndef QT_BOOTSTRAPPED +class QWindowsSockInit +{ +public: + QWindowsSockInit(); + ~QWindowsSockInit(); + int version; +}; + +QWindowsSockInit::QWindowsSockInit() +: version(0) +{ + //### should we try for 2.2 on all platforms ?? + WSAData wsadata; + + // IPv6 requires Winsock v2.0 or better. + if (WSAStartup(MAKEWORD(2, 0), &wsadata) != 0) { + qWarning("QTcpSocketAPI: WinSock v2.0 initialization failed."); + } else { + version = 0x20; + } +} + +QWindowsSockInit::~QWindowsSockInit() +{ + WSACleanup(); +} +Q_GLOBAL_STATIC(QWindowsSockInit, winsockInit) +# endif // QT_BOOTSTRAPPED + +static QString readVersionRegistryString(const wchar_t *subKey) +{ + return QWinRegistryKey(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)") + .stringValue(subKey); +} + +static inline QString windowsDisplayVersion() +{ + // https://tickets.puppetlabs.com/browse/FACT-3058 + // The "ReleaseId" key stopped updating since Windows 10 20H2. + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10_20H2) + return readVersionRegistryString(L"DisplayVersion"); + else + return readVersionRegistryString(L"ReleaseId"); +} + +static QString winSp_helper() +{ + const auto osv = qWindowsVersionInfo(); + const qint16 major = osv.wServicePackMajor; + if (major) { + QString sp = QStringLiteral("SP ") + QString::number(major); + const qint16 minor = osv.wServicePackMinor; + if (minor) + sp += u'.' + QString::number(minor); + + return sp; + } + return QString(); +} + +static const char *osVer_helper(QOperatingSystemVersion version = QOperatingSystemVersion::current()) +{ + Q_UNUSED(version); + const OSVERSIONINFOEX osver = qWindowsVersionInfo(); + const bool workstation = osver.wProductType == VER_NT_WORKSTATION; + +#define Q_WINVER(major, minor) (major << 8 | minor) + switch (Q_WINVER(osver.dwMajorVersion, osver.dwMinorVersion)) { + case Q_WINVER(10, 0): + if (workstation) { + if (osver.dwBuildNumber >= 22000) + return "11"; + return "10"; + } + // else: Server + if (osver.dwBuildNumber >= 20348) + return "Server 2022"; + if (osver.dwBuildNumber >= 17763) + return "Server 2019"; + return "Server 2016"; + } +#undef Q_WINVER + // unknown, future version + return nullptr; +} + +#endif +#if defined(Q_OS_UNIX) +# if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_FREEBSD) +# define USE_ETC_OS_RELEASE +struct QUnixOSVersion +{ + // from /etc/os-release older /etc/lsb-release // redhat /etc/redhat-release // debian /etc/debian_version + QString productType; // $ID $DISTRIB_ID // single line file containing: // Debian + QString productVersion; // $VERSION_ID $DISTRIB_RELEASE // // single line file + QString prettyName; // $PRETTY_NAME $DISTRIB_DESCRIPTION +}; + +static QString unquote(const char *begin, const char *end) +{ + // man os-release says: + // Variable assignment values must be enclosed in double + // or single quotes if they include spaces, semicolons or + // other special characters outside of A–Z, a–z, 0–9. Shell + // special characters ("$", quotes, backslash, backtick) + // must be escaped with backslashes, following shell style. + // All strings should be in UTF-8 format, and non-printable + // characters should not be used. It is not supported to + // concatenate multiple individually quoted strings. + if (*begin == '"') { + Q_ASSERT(end[-1] == '"'); + return QString::fromUtf8(begin + 1, end - begin - 2); + } + return QString::fromUtf8(begin, end - begin); +} +static QByteArray getEtcFileContent(const char *filename) +{ + // we're avoiding QFile here + int fd = qt_safe_open(filename, O_RDONLY); + if (fd == -1) + return QByteArray(); + + QT_STATBUF sbuf; + if (QT_FSTAT(fd, &sbuf) == -1) { + qt_safe_close(fd); + return QByteArray(); + } + + QByteArray buffer(sbuf.st_size, Qt::Uninitialized); + buffer.resize(qt_safe_read(fd, buffer.data(), sbuf.st_size)); + qt_safe_close(fd); + return buffer; +} + +static bool readEtcFile(QUnixOSVersion &v, const char *filename, + const QByteArray &idKey, const QByteArray &versionKey, const QByteArray &prettyNameKey) +{ + + QByteArray buffer = getEtcFileContent(filename); + if (buffer.isEmpty()) + return false; + + const char *ptr = buffer.constData(); + const char *end = buffer.constEnd(); + const char *eol; + QByteArray line; + for (; ptr != end; ptr = eol + 1) { + // find the end of the line after ptr + eol = static_cast(memchr(ptr, '\n', end - ptr)); + if (!eol) + eol = end - 1; + line.setRawData(ptr, eol - ptr); + + if (line.startsWith(idKey)) { + ptr += idKey.length(); + v.productType = unquote(ptr, eol); + continue; + } + + if (line.startsWith(prettyNameKey)) { + ptr += prettyNameKey.length(); + v.prettyName = unquote(ptr, eol); + continue; + } + + if (line.startsWith(versionKey)) { + ptr += versionKey.length(); + v.productVersion = unquote(ptr, eol); + continue; + } + } + + return true; +} + +static bool readOsRelease(QUnixOSVersion &v) +{ + QByteArray id = QByteArrayLiteral("ID="); + QByteArray versionId = QByteArrayLiteral("VERSION_ID="); + QByteArray prettyName = QByteArrayLiteral("PRETTY_NAME="); + + // man os-release(5) says: + // The file /etc/os-release takes precedence over /usr/lib/os-release. + // Applications should check for the former, and exclusively use its data + // if it exists, and only fall back to /usr/lib/os-release if it is + // missing. + return readEtcFile(v, "/etc/os-release", id, versionId, prettyName) || + readEtcFile(v, "/usr/lib/os-release", id, versionId, prettyName); +} + +static bool readEtcLsbRelease(QUnixOSVersion &v) +{ + bool ok = readEtcFile(v, "/etc/lsb-release", QByteArrayLiteral("DISTRIB_ID="), + QByteArrayLiteral("DISTRIB_RELEASE="), QByteArrayLiteral("DISTRIB_DESCRIPTION=")); + if (ok && (v.prettyName.isEmpty() || v.prettyName == v.productType)) { + // some distributions have redundant information for the pretty name, + // so try /etc/-release + + // we're still avoiding QFile here + QByteArray distrorelease = "/etc/" + v.productType.toLatin1().toLower() + "-release"; + int fd = qt_safe_open(distrorelease, O_RDONLY); + if (fd != -1) { + QT_STATBUF sbuf; + if (QT_FSTAT(fd, &sbuf) != -1 && sbuf.st_size > v.prettyName.length()) { + // file apparently contains interesting information + QByteArray buffer(sbuf.st_size, Qt::Uninitialized); + buffer.resize(qt_safe_read(fd, buffer.data(), sbuf.st_size)); + v.prettyName = QString::fromLatin1(buffer.trimmed()); + } + qt_safe_close(fd); + } + } + + // some distributions have a /etc/lsb-release file that does not provide the values + // we are looking for, i.e. DISTRIB_ID, DISTRIB_RELEASE and DISTRIB_DESCRIPTION. + // Assuming that neither DISTRIB_ID nor DISTRIB_RELEASE were found, or contained valid values, + // returning false for readEtcLsbRelease will allow further /etc/-release parsing. + return ok && !(v.productType.isEmpty() && v.productVersion.isEmpty()); +} + +#if defined(Q_OS_LINUX) +static QByteArray getEtcFileFirstLine(const char *fileName) +{ + QByteArray buffer = getEtcFileContent(fileName); + if (buffer.isEmpty()) + return QByteArray(); + + const char *ptr = buffer.constData(); + int eol = buffer.indexOf("\n"); + return QByteArray(ptr, eol).trimmed(); +} + +static bool readEtcRedHatRelease(QUnixOSVersion &v) +{ + // /etc/redhat-release analysed should be a one line file + // the format of its content is + // i.e. "Red Hat Enterprise Linux Workstation release 6.5 (Santiago)" + QByteArray line = getEtcFileFirstLine("/etc/redhat-release"); + if (line.isEmpty()) + return false; + + v.prettyName = QString::fromLatin1(line); + + const char keyword[] = "release "; + int releaseIndex = line.indexOf(keyword); + v.productType = QString::fromLatin1(line.mid(0, releaseIndex)).remove(u' '); + int spaceIndex = line.indexOf(' ', releaseIndex + strlen(keyword)); + v.productVersion = QString::fromLatin1(line.mid(releaseIndex + strlen(keyword), + spaceIndex > -1 ? spaceIndex - releaseIndex - int(strlen(keyword)) : -1)); + return true; +} + +static bool readEtcDebianVersion(QUnixOSVersion &v) +{ + // /etc/debian_version analysed should be a one line file + // the format of its content is + // i.e. "jessie/sid" + QByteArray line = getEtcFileFirstLine("/etc/debian_version"); + if (line.isEmpty()) + return false; + + v.productType = QStringLiteral("Debian"); + v.productVersion = QString::fromLatin1(line); + return true; +} +#endif + +static bool findUnixOsVersion(QUnixOSVersion &v) +{ + if (readOsRelease(v)) + return true; + if (readEtcLsbRelease(v)) + return true; +#if defined(Q_OS_LINUX) + if (readEtcRedHatRelease(v)) + return true; + if (readEtcDebianVersion(v)) + return true; +#endif + return false; +} +# endif // USE_ETC_OS_RELEASE +#endif // Q_OS_UNIX + +#ifdef Q_OS_ANDROID +static const char *osVer_helper(QOperatingSystemVersion) +{ +/* Data: + + + +Cupcake +Donut +Eclair +Eclair +Eclair +Froyo +Gingerbread +Gingerbread +Honeycomb +Honeycomb +Honeycomb +Ice Cream Sandwich +Ice Cream Sandwich +Jelly Bean +Jelly Bean +Jelly Bean +KitKat +KitKat +Lollipop +Lollipop +Marshmallow +Nougat +Nougat +Oreo +*/ + static const char versions_string[] = + "\0" + "Cupcake\0" + "Donut\0" + "Eclair\0" + "Froyo\0" + "Gingerbread\0" + "Honeycomb\0" + "Ice Cream Sandwich\0" + "Jelly Bean\0" + "KitKat\0" + "Lollipop\0" + "Marshmallow\0" + "Nougat\0" + "Oreo\0" + "\0"; + + static const int versions_indices[] = { + 0, 0, 0, 1, 9, 15, 15, 15, + 22, 28, 28, 40, 40, 40, 50, 50, + 69, 69, 69, 80, 80, 87, 87, 96, + 108, 108, 115, -1 + }; + + static const int versions_count = (sizeof versions_indices) / (sizeof versions_indices[0]); + + // https://source.android.com/source/build-numbers.html + // https://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels + const int sdk_int = QJniObject::getStaticField("android/os/Build$VERSION", "SDK_INT"); + return &versions_string[versions_indices[qBound(0, sdk_int, versions_count - 1)]]; +} +#endif + +/*! + \since 5.4 + + Returns the architecture of the CPU that Qt was compiled for, in text + format. Note that this may not match the actual CPU that the application is + running on if there's an emulation layer or if the CPU supports multiple + architectures (like x86-64 processors supporting i386 applications). To + detect that, use currentCpuArchitecture(). + + Values returned by this function are stable and will not change over time, + so applications can rely on the returned value as an identifier, except + that new CPU types may be added over time. + + Typical returned values are (note: list not exhaustive): + \list + \li "arm" + \li "arm64" + \li "i386" + \li "ia64" + \li "mips" + \li "mips64" + \li "power" + \li "power64" + \li "sparc" + \li "sparcv9" + \li "x86_64" + \endlist + + \sa QSysInfo::buildAbi(), QSysInfo::currentCpuArchitecture() +*/ +QString QSysInfo::buildCpuArchitecture() +{ + return QStringLiteral(ARCH_PROCESSOR); +} + +/*! + \since 5.4 + + Returns the architecture of the CPU that the application is running on, in + text format. Note that this function depends on what the OS will report and + may not detect the actual CPU architecture if the OS hides that information + or is unable to provide it. For example, a 32-bit OS running on a 64-bit + CPU is usually unable to determine the CPU is actually capable of running + 64-bit programs. + + Values returned by this function are mostly stable: an attempt will be made + to ensure that they stay constant over time and match the values returned + by QSysInfo::builldCpuArchitecture(). However, due to the nature of the + operating system functions being used, there may be discrepancies. + + Typical returned values are (note: list not exhaustive): + \list + \li "arm" + \li "arm64" + \li "i386" + \li "ia64" + \li "mips" + \li "mips64" + \li "power" + \li "power64" + \li "sparc" + \li "sparcv9" + \li "x86_64" + \endlist + + \sa QSysInfo::buildAbi(), QSysInfo::buildCpuArchitecture() +*/ +QString QSysInfo::currentCpuArchitecture() +{ +#if defined(Q_OS_WIN) + // We don't need to catch all the CPU architectures in this function; + // only those where the host CPU might be different than the build target + // (usually, 64-bit platforms). + SYSTEM_INFO info; + GetNativeSystemInfo(&info); + switch (info.wProcessorArchitecture) { +# ifdef PROCESSOR_ARCHITECTURE_AMD64 + case PROCESSOR_ARCHITECTURE_AMD64: + return QStringLiteral("x86_64"); +# endif +# ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 + case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: +# endif + case PROCESSOR_ARCHITECTURE_IA64: + return QStringLiteral("ia64"); + } +#elif defined(Q_OS_DARWIN) && !defined(Q_OS_MACOS) + // iOS-based OSes do not return the architecture on uname(2)'s result. + return buildCpuArchitecture(); +#elif defined(Q_OS_UNIX) + long ret = -1; + struct utsname u; + +# if defined(Q_OS_SOLARIS) + // We need a special call for Solaris because uname(2) on x86 returns "i86pc" for + // both 32- and 64-bit CPUs. Reference: + // http://docs.oracle.com/cd/E18752_01/html/816-5167/sysinfo-2.html#REFMAN2sysinfo-2 + // http://fxr.watson.org/fxr/source/common/syscall/systeminfo.c?v=OPENSOLARIS + // http://fxr.watson.org/fxr/source/common/conf/param.c?v=OPENSOLARIS;im=10#L530 + if (ret == -1) + ret = sysinfo(SI_ARCHITECTURE_64, u.machine, sizeof u.machine); +# endif + + if (ret == -1) + ret = uname(&u); + + // we could use detectUnixVersion() above, but we only need a field no other function does + if (ret != -1) { + // the use of QT_BUILD_INTERNAL here is simply to ensure all branches build + // as we don't often build on some of the less common platforms +# if defined(Q_PROCESSOR_ARM) || defined(QT_BUILD_INTERNAL) + if (strcmp(u.machine, "aarch64") == 0) + return QStringLiteral("arm64"); + if (strncmp(u.machine, "armv", 4) == 0) + return QStringLiteral("arm"); +# endif +# if defined(Q_PROCESSOR_POWER) || defined(QT_BUILD_INTERNAL) + // harmonize "powerpc" and "ppc" to "power" + if (strncmp(u.machine, "ppc", 3) == 0) + return "power"_L1 + QLatin1StringView(u.machine + 3); + if (strncmp(u.machine, "powerpc", 7) == 0) + return "power"_L1 + QLatin1StringView(u.machine + 7); + if (strcmp(u.machine, "Power Macintosh") == 0) + return "power"_L1; +# endif +# if defined(Q_PROCESSOR_SPARC) || defined(QT_BUILD_INTERNAL) + // Solaris sysinfo(2) (above) uses "sparcv9", but uname -m says "sun4u"; + // Linux says "sparc64" + if (strcmp(u.machine, "sun4u") == 0 || strcmp(u.machine, "sparc64") == 0) + return QStringLiteral("sparcv9"); + if (strcmp(u.machine, "sparc32") == 0) + return QStringLiteral("sparc"); +# endif +# if defined(Q_PROCESSOR_X86) || defined(QT_BUILD_INTERNAL) + // harmonize all "i?86" to "i386" + if (strlen(u.machine) == 4 && u.machine[0] == 'i' + && u.machine[2] == '8' && u.machine[3] == '6') + return QStringLiteral("i386"); + if (strcmp(u.machine, "amd64") == 0) // Solaris + return QStringLiteral("x86_64"); +# endif + return QString::fromLatin1(u.machine); + } +#endif + return buildCpuArchitecture(); +} + +/*! + \since 5.4 + + Returns the full architecture string that Qt was compiled for. This string + is useful for identifying different, incompatible builds. For example, it + can be used as an identifier to request an upgrade package from a server. + + The values returned from this function are kept stable as follows: the + mandatory components of the result will not change in future versions of + Qt, but optional suffixes may be added. + + The returned value is composed of three or more parts, separated by dashes + ("-"). They are: + + \table + \header \li Component \li Value + \row \li CPU Architecture \li The same as QSysInfo::buildCpuArchitecture(), such as "arm", "i386", "mips" or "x86_64" + \row \li Endianness \li "little_endian" or "big_endian" + \row \li Word size \li Whether it's a 32- or 64-bit application. Possible values are: + "llp64" (Windows 64-bit), "lp64" (Unix 64-bit), "ilp32" (32-bit) + \row \li (Optional) ABI \li Zero or more components identifying different ABIs possible in this architecture. + Currently, Qt has optional ABI components for ARM and MIPS processors: one + component is the main ABI (such as "eabi", "o32", "n32", "o64"); another is + whether the calling convention is using hardware floating point registers ("hardfloat" + is present). + + Additionally, if Qt was configured with \c{-qreal float}, the ABI option tag "qreal_float" + will be present. If Qt was configured with another type as qreal, that type is present after + "qreal_", with all characters other than letters and digits escaped by an underscore, followed + by two hex digits. For example, \c{-qreal long double} becomes "qreal_long_20double". + \endtable + + \sa QSysInfo::buildCpuArchitecture() +*/ +QString QSysInfo::buildAbi() +{ + // ARCH_FULL is a concatenation of strings (incl. ARCH_PROCESSOR), which breaks + // QStringLiteral on MSVC. Since the concatenation behavior we want is specified + // the same C++11 paper as the Unicode strings, we'll use that macro and hope + // that Microsoft implements the new behavior when they add support for Unicode strings. + return QStringLiteral(ARCH_FULL); +} + +static QString unknownText() +{ + return QStringLiteral("unknown"); +} + +/*! + \since 5.4 + + Returns the type of the operating system kernel Qt was compiled for. It's + also the kernel the application is running on, unless the host operating + system is running a form of compatibility or virtualization layer. + + Values returned by this function are stable and will not change over time, + so applications can rely on the returned value as an identifier, except + that new OS kernel types may be added over time. + + On Windows, this function returns the type of Windows kernel, like "winnt". + On Unix systems, it returns the same as the output of \c{uname + -s} (lowercased). + + \note This function may return surprising values: it returns "linux" + for all operating systems running Linux (including Android), "qnx" for all + operating systems running QNX, "freebsd" for + Debian/kFreeBSD, and "darwin" for \macos and iOS. For information on the type + of product the application is running on, see productType(). + + \sa QFileSelector, kernelVersion(), productType(), productVersion(), prettyProductName() +*/ +QString QSysInfo::kernelType() +{ +#if defined(Q_OS_WIN) + return QStringLiteral("winnt"); +#elif defined(Q_OS_UNIX) + struct utsname u; + if (uname(&u) == 0) + return QString::fromLatin1(u.sysname).toLower(); +#endif + return unknownText(); +} + +/*! + \since 5.4 + + Returns the release version of the operating system kernel. On Windows, it + returns the version of the NT kernel. On Unix systems, including + Android and \macos, it returns the same as the \c{uname -r} + command would return. + + If the version could not be determined, this function may return an empty + string. + + \sa kernelType(), productType(), productVersion(), prettyProductName() +*/ +QString QSysInfo::kernelVersion() +{ +#ifdef Q_OS_WIN + const auto osver = QOperatingSystemVersion::current(); + return QString::asprintf("%d.%d.%d", + osver.majorVersion(), osver.minorVersion(), osver.microVersion()); +#else + struct utsname u; + if (uname(&u) == 0) + return QString::fromLatin1(u.release); + return QString(); +#endif +} + + +/*! + \since 5.4 + + Returns the product name of the operating system this application is + running in. If the application is running on some sort of emulation or + virtualization layer (such as WINE on a Unix system), this function will + inspect the emulation / virtualization layer. + + Values returned by this function are stable and will not change over time, + so applications can rely on the returned value as an identifier, except + that new OS types may be added over time. + + \b{Linux and Android note}: this function returns "android" for Linux + systems running Android userspace, notably when using the Bionic library. + For all other Linux systems, regardless of C library being used, it tries + to determine the distribution name and returns that. If determining the + distribution name failed, it returns "unknown". + + \b{\macos note}: this function returns "macos" for all \macos systems, + regardless of Apple naming convention. Previously, in Qt 5, it returned + "osx", again regardless of Apple naming conventions. + + \b{Darwin, iOS, tvOS, and watchOS note}: this function returns "ios" for + iOS systems, "tvos" for tvOS systems, "watchos" for watchOS systems, and + "darwin" in case the system could not be determined. + + \b{FreeBSD note}: this function returns "debian" for Debian/kFreeBSD and + "unknown" otherwise. + + \b{Windows note}: this function return "windows" + + For other Unix-type systems, this function usually returns "unknown". + + \sa QFileSelector, kernelType(), kernelVersion(), productVersion(), prettyProductName() +*/ +QString QSysInfo::productType() +{ + // similar, but not identical to QFileSelectorPrivate::platformSelectors +#if defined(Q_OS_WIN) + return QStringLiteral("windows"); + +#elif defined(Q_OS_QNX) + return QStringLiteral("qnx"); + +#elif defined(Q_OS_ANDROID) + return QStringLiteral("android"); + +#elif defined(Q_OS_IOS) + return QStringLiteral("ios"); +#elif defined(Q_OS_TVOS) + return QStringLiteral("tvos"); +#elif defined(Q_OS_WATCHOS) + return QStringLiteral("watchos"); +#elif defined(Q_OS_MACOS) + return QStringLiteral("macos"); +#elif defined(Q_OS_DARWIN) + return QStringLiteral("darwin"); + +#elif defined(USE_ETC_OS_RELEASE) // Q_OS_UNIX + QUnixOSVersion unixOsVersion; + findUnixOsVersion(unixOsVersion); + if (!unixOsVersion.productType.isEmpty()) + return unixOsVersion.productType; +#endif + return unknownText(); +} + +/*! + \since 5.4 + + Returns the product version of the operating system in string form. If the + version could not be determined, this function returns "unknown". + + It will return the Android, iOS, \macos, Windows full-product + versions on those systems. + + Typical returned values are (note: list not exhaustive): + \list + \li "12" (Android 12) + \li "36" (Fedora 36) + \li "15.5" (iOS 15.5) + \li "12.4" (macOS Monterey) + \li "22.04" (Ubuntu 22.04) + \li "8.6" (watchOS 8.6) + \li "11" (Windows 11) + \li "Server 2022" (Windows Server 2022) + \endlist + + On Linux systems, it will try to determine the distribution version and will + return that. This is also done on Debian/kFreeBSD, so this function will + return Debian version in that case. + + In all other Unix-type systems, this function always returns "unknown". + + \note The version string returned from this function is not guaranteed to + be orderable. On Linux, the version of + the distribution may jump unexpectedly, please refer to the distribution's + documentation for versioning practices. + + \sa kernelType(), kernelVersion(), productType(), prettyProductName() +*/ +QString QSysInfo::productVersion() +{ +#if defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN) + const auto version = QOperatingSystemVersion::current(); + return QString::asprintf("%d.%d", version.majorVersion(), version.minorVersion()); +#elif defined(Q_OS_WIN) + const char *version = osVer_helper(); + if (version) { + const QLatin1Char spaceChar(' '); + return QString::fromLatin1(version).remove(spaceChar).toLower() + winSp_helper().remove(spaceChar).toLower(); + } + // fall through + +#elif defined(USE_ETC_OS_RELEASE) // Q_OS_UNIX + QUnixOSVersion unixOsVersion; + findUnixOsVersion(unixOsVersion); + if (!unixOsVersion.productVersion.isEmpty()) + return unixOsVersion.productVersion; +#endif + + // fallback + return unknownText(); +} + +/*! + \since 5.4 + + Returns a prettier form of productType() and productVersion(), containing + other tokens like the operating system type, codenames and other + information. The result of this function is suitable for displaying to the + user, but not for long-term storage, as the string may change with updates + to Qt. + + If productType() is "unknown", this function will instead use the + kernelType() and kernelVersion() functions. + + \sa kernelType(), kernelVersion(), productType(), productVersion() +*/ +QString QSysInfo::prettyProductName() +{ +#if defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN) || defined(Q_OS_WIN) + const auto version = QOperatingSystemVersion::current(); + const int majorVersion = version.majorVersion(); + const QString versionString = QString::asprintf("%d.%d", majorVersion, version.minorVersion()); + QString result = version.name() + u' '; + const char *name = osVer_helper(version); + if (!name) + return result + versionString; + result += QLatin1StringView(name); +# if !defined(Q_OS_WIN) + return result + " ("_L1 + versionString + u')'; +# else + // (resembling winver.exe): Windows 10 "Windows 10 Version 1809" + const auto displayVersion = windowsDisplayVersion(); + if (!displayVersion.isEmpty()) + result += " Version "_L1 + displayVersion; + return result; +# endif // Windows +#elif defined(Q_OS_HAIKU) + return "Haiku "_L1 + productVersion(); +#elif defined(Q_OS_UNIX) +# ifdef USE_ETC_OS_RELEASE + QUnixOSVersion unixOsVersion; + findUnixOsVersion(unixOsVersion); + if (!unixOsVersion.prettyName.isEmpty()) + return unixOsVersion.prettyName; +# endif + struct utsname u; + if (uname(&u) == 0) + return QString::fromLatin1(u.sysname) + u' ' + QString::fromLatin1(u.release); +#endif + return unknownText(); +} + +#ifndef QT_BOOTSTRAPPED +/*! + \since 5.6 + + Returns this machine's host name, if one is configured. Note that hostnames + are not guaranteed to be globally unique, especially if they were + configured automatically. + + This function does not guarantee the returned host name is a Fully + Qualified Domain Name (FQDN). For that, use QHostInfo to resolve the + returned name to an FQDN. + + This function returns the same as QHostInfo::localHostName(). + + \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 + struct utsname u; + if (uname(&u) == 0) + return QString::fromLocal8Bit(u.nodename); + return QString(); +#else +# ifdef Q_OS_WIN + // Important: QtNetwork depends on machineHostName() initializing ws2_32.dll + winsockInit(); +# endif + + char hostName[512]; + if (gethostname(hostName, sizeof(hostName)) == -1) + return QString(); + hostName[sizeof(hostName) - 1] = '\0'; + return QString::fromLocal8Bit(hostName); +#endif +} +#endif // QT_BOOTSTRAPPED + +enum { + UuidStringLen = sizeof("00000000-0000-0000-0000-000000000000") - 1 +}; + +/*! + \since 5.11 + + 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(), bootUniqueId() +*/ +QByteArray QSysInfo::machineUniqueId() +{ +#if defined(Q_OS_DARWIN) && __has_include() + char uuid[UuidStringLen + 1]; + static const mach_port_t defaultPort = 0; // Effectively kIOMasterPortDefault/kIOMainPortDefault + io_service_t service = IOServiceGetMatchingService(defaultPort, IOServiceMatching("IOPlatformExpertDevice")); + QCFString stringRef = (CFStringRef)IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0); + CFStringGetCString(stringRef, uuid, sizeof(uuid), kCFStringEncodingMacRoman); + return QByteArray(uuid); +#elif defined(Q_OS_BSD4) && defined(KERN_HOSTUUID) + char uuid[UuidStringLen + 1]; + size_t uuidlen = sizeof(uuid); + 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 - 1); +#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) + // Let's poke at the registry + const QString machineGuid = QWinRegistryKey(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Cryptography)") + .stringValue(u"MachineGuid"_s); + if (!machineGuid.isEmpty()) + return machineGuid.toLatin1(); +#endif + return QByteArray(); +} + +/*! + \since 5.11 + + Returns a unique ID for this machine's boot, if one can be determined. If + no unique ID could be determined, this function returns an empty byte + array. This value is expected to change after every boot and can be + considered globally unique. + + This function is currently only implemented for Linux and Apple operating + systems. + + \sa machineUniqueId() +*/ +QByteArray QSysInfo::bootUniqueId() +{ +#ifdef Q_OS_LINUX + // use low-level API here for simplicity + int fd = qt_safe_open("/proc/sys/kernel/random/boot_id", O_RDONLY); + if (fd != -1) { + char uuid[UuidStringLen]; + qint64 len = qt_safe_read(fd, uuid, sizeof(uuid)); + qt_safe_close(fd); + if (len == UuidStringLen) + return QByteArray(uuid, UuidStringLen); + } +#elif defined(Q_OS_DARWIN) + // "kern.bootsessionuuid" is only available by name + char uuid[UuidStringLen + 1]; + size_t uuidlen = sizeof(uuid); + if (sysctlbyname("kern.bootsessionuuid", uuid, &uuidlen, nullptr, 0) == 0 + && uuidlen == sizeof(uuid)) + return QByteArray(uuid, uuidlen - 1); +#endif + return QByteArray(); +}; + +QT_END_NAMESPACE