Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsysinfo.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// Copyright (C) 2022 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
6#include "qsysinfo.h"
7
8#include <QtCore/qbytearray.h>
9#include <QtCore/qoperatingsystemversion.h>
10#include <QtCore/qstring.h>
11
12#include <private/qoperatingsystemversion_p.h>
13
14#ifdef Q_OS_UNIX
15# include <sys/utsname.h>
16# include <private/qcore_unix_p.h>
17#endif
18
19#ifdef Q_OS_ANDROID
20#include <QtCore/private/qjnihelpers_p.h>
21#include <qjniobject.h>
22#endif
23
24#if defined(Q_OS_SOLARIS)
25# include <sys/systeminfo.h>
26#endif
27
28#if defined(Q_OS_DARWIN)
29# include "qnamespace.h"
30# include <private/qcore_mac_p.h>
31# if __has_include(<IOKit/IOKitLib.h>)
32# include <IOKit/IOKitLib.h>
33# endif
34#endif
35
36#ifdef Q_OS_BSD4
37# include <sys/sysctl.h>
38#endif
39
40#ifdef Q_OS_WIN
41# include "qoperatingsystemversion_win_p.h"
42# include "private/qwinregistry_p.h"
43# include "qt_windows.h"
44#endif // Q_OS_WIN
45
46#include "archdetect.cpp"
47
49
50using namespace Qt::StringLiterals;
51
52/*!
53 \class QSysInfo
54 \inmodule QtCore
55 \brief The QSysInfo class provides information about the system.
56
57 \list
58 \li \l WordSize specifies the size of a pointer for the platform
59 on which the application is compiled.
60 \li \l ByteOrder specifies whether the platform is big-endian or
61 little-endian.
62 \endlist
63
64 Some constants are defined only on certain platforms. You can use
65 the preprocessor symbols Q_OS_WIN and Q_OS_MACOS to test that
66 the application is compiled under Windows or \macos.
67
68 \sa QLibraryInfo
69*/
70
71/*!
72 \enum QSysInfo::Sizes
73
74 This enum provides platform-specific information about the sizes of data
75 structures used by the underlying architecture.
76
77 \value WordSize The size in bits of a pointer for the platform on which
78 the application is compiled (32 or 64).
79*/
80
81/*!
82 \enum QSysInfo::Endian
83
84 \value BigEndian Big-endian byte order (also called Network byte order)
85 \value LittleEndian Little-endian byte order
86 \value ByteOrder Equals BigEndian or LittleEndian, depending on
87 the platform's byte order.
88*/
89
90#if defined(Q_OS_DARWIN)
91
92static const char *osVer_helper(QOperatingSystemVersion version = QOperatingSystemVersion::current())
93{
94#ifdef Q_OS_MACOS
95 switch (version.majorVersion()) {
96 case 10: {
97 switch (version.minorVersion()) {
98 case 9: return "Mavericks";
99 case 10: return "Yosemite";
100 case 11: return "El Capitan";
101 case 12: return "Sierra";
102 case 13: return "High Sierra";
103 case 14: return "Mojave";
104 case 15: return "Catalina";
105 case 16: return "Big Sur";
106 default:
107 Q_UNREACHABLE();
108 }
109 }
110 case 11: return "Big Sur";
111 case 12: return "Monterey";
112 case 13: return "Ventura";
113 case 14: return "Sonoma";
114 case 15: return "Sequoia";
115 case 26: return "Tahoe";
116 default:
117 // Unknown, future version
118 break;
119 }
120#else
121 Q_UNUSED(version);
122#endif
123 return nullptr;
124}
125
126#elif defined(Q_OS_WIN)
127
128# ifndef QT_BOOTSTRAPPED
129class QWindowsSockInit
130{
131public:
132 QWindowsSockInit();
133 ~QWindowsSockInit();
134 int version;
135};
136
137QWindowsSockInit::QWindowsSockInit()
138: version(0)
139{
140 WSAData wsadata;
141
142 if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) {
143 qWarning("QTcpSocketAPI: WinSock v2.2 initialization failed.");
144 } else {
145 version = 0x22;
146 }
147}
148
149QWindowsSockInit::~QWindowsSockInit()
150{
151 WSACleanup();
152}
153Q_GLOBAL_STATIC(QWindowsSockInit, winsockInit)
154# endif // QT_BOOTSTRAPPED
155
156static QString readVersionRegistryString(const wchar_t *subKey)
157{
158 return QWinRegistryKey(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)")
159 .stringValue(subKey);
160}
161
162static inline QString windowsDisplayVersion()
163{
164 // https://tickets.puppetlabs.com/browse/FACT-3058
165 // The "ReleaseId" key stopped updating since Windows 10 20H2.
166 if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10_20H2)
167 return readVersionRegistryString(L"DisplayVersion");
168 else
169 return readVersionRegistryString(L"ReleaseId");
170}
171
172static QString winSp_helper()
173{
174 const auto osv = qWindowsVersionInfo();
175 const qint16 major = osv.wServicePackMajor;
176 if (major) {
177 QString sp = QStringLiteral("SP ") + QString::number(major);
178 const qint16 minor = osv.wServicePackMinor;
179 if (minor)
180 sp += u'.' + QString::number(minor);
181
182 return sp;
183 }
184 return QString();
185}
186
187static const char *osVer_helper(QOperatingSystemVersion version = QOperatingSystemVersion::current())
188{
189 Q_UNUSED(version);
190 const OSVERSIONINFOEX osver = qWindowsVersionInfo();
191 const bool workstation = osver.wProductType == VER_NT_WORKSTATION;
192
193#define Q_WINVER(major, minor) (major << 8 | minor)
194 switch (Q_WINVER(osver.dwMajorVersion, osver.dwMinorVersion)) {
195 case Q_WINVER(10, 0):
196 if (workstation) {
197 if (osver.dwBuildNumber >= 22000)
198 return "11";
199 return "10";
200 }
201 // else: Server
202 if (osver.dwBuildNumber >= 26100)
203 return "Server 2025";
204 if (osver.dwBuildNumber >= 20348)
205 return "Server 2022";
206 if (osver.dwBuildNumber >= 17763)
207 return "Server 2019";
208 return "Server 2016";
209 }
210#undef Q_WINVER
211 // unknown, future version
212 return nullptr;
213}
214
215#endif
216#if defined(Q_OS_UNIX)
217# if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_FREEBSD)
218# define USE_ETC_OS_RELEASE
219struct QUnixOSVersion
220{
221 // from /etc/os-release older /etc/lsb-release // redhat /etc/redhat-release // debian /etc/debian_version
222 QString productType; // $ID $DISTRIB_ID // single line file containing: // Debian
223 QString productVersion; // $VERSION_ID $DISTRIB_RELEASE // <Vendor_ID release Version_ID> // single line file <Release_ID/sid>
224 QString prettyName; // $PRETTY_NAME $DISTRIB_DESCRIPTION
225};
226
227static QString unquote(QByteArrayView str)
228{
229 // man os-release says:
230 // Variable assignment values must be enclosed in double
231 // or single quotes if they include spaces, semicolons or
232 // other special characters outside of A–Z, a–z, 0–9. Shell
233 // special characters ("$", quotes, backslash, backtick)
234 // must be escaped with backslashes, following shell style.
235 // All strings should be in UTF-8 format, and non-printable
236 // characters should not be used. It is not supported to
237 // concatenate multiple individually quoted strings.
238 if (str.size() >= 2 && str.front() == '"' && str.back() == '"')
239 str = str.sliced(1).chopped(1);
240 return QString::fromUtf8(str);
241}
242
243static QByteArray getEtcFileContent(const char *filename)
244{
245 // we're avoiding QFile here
246 int fd = qt_safe_open(filename, O_RDONLY);
247 if (fd == -1)
248 return QByteArray();
249
250 QT_STATBUF sbuf;
251 if (QT_FSTAT(fd, &sbuf) == -1) {
252 qt_safe_close(fd);
253 return QByteArray();
254 }
255
256 QByteArray buffer(sbuf.st_size, Qt::Uninitialized);
257 buffer.resize(qt_safe_read(fd, buffer.data(), sbuf.st_size));
258 qt_safe_close(fd);
259 return buffer;
260}
261
262static bool readEtcFile(QUnixOSVersion &v, const char *filename,
263 const QByteArray &idKey, const QByteArray &versionKey, const QByteArray &prettyNameKey)
264{
265
266 QByteArray buffer = getEtcFileContent(filename);
267 if (buffer.isEmpty())
268 return false;
269
270 const char *ptr = buffer.constData();
271 const char *end = buffer.constEnd();
272 const char *eol;
273 QByteArray line;
274 for (; ptr != end; ptr = eol + 1) {
275 // find the end of the line after ptr
276 eol = static_cast<const char *>(memchr(ptr, '\n', end - ptr));
277 if (!eol)
278 eol = end - 1;
279 line.setRawData(ptr, eol - ptr);
280
281 if (line.startsWith(idKey)) {
282 ptr += idKey.size();
283 v.productType = unquote({ptr, eol});
284 continue;
285 }
286
287 if (line.startsWith(prettyNameKey)) {
288 ptr += prettyNameKey.size();
289 v.prettyName = unquote({ptr, eol});
290 continue;
291 }
292
293 if (line.startsWith(versionKey)) {
294 ptr += versionKey.size();
295 v.productVersion = unquote({ptr, eol});
296 continue;
297 }
298 }
299
300 return true;
301}
302
303static bool readOsRelease(QUnixOSVersion &v)
304{
305 QByteArray id = QByteArrayLiteral("ID=");
306 QByteArray versionId = QByteArrayLiteral("VERSION_ID=");
307 QByteArray prettyName = QByteArrayLiteral("PRETTY_NAME=");
308
309 // man os-release(5) says:
310 // The file /etc/os-release takes precedence over /usr/lib/os-release.
311 // Applications should check for the former, and exclusively use its data
312 // if it exists, and only fall back to /usr/lib/os-release if it is
313 // missing.
314 return readEtcFile(v, "/etc/os-release", id, versionId, prettyName) ||
315 readEtcFile(v, "/usr/lib/os-release", id, versionId, prettyName);
316}
317
318static bool readEtcLsbRelease(QUnixOSVersion &v)
319{
320 bool ok = readEtcFile(v, "/etc/lsb-release", QByteArrayLiteral("DISTRIB_ID="),
321 QByteArrayLiteral("DISTRIB_RELEASE="), QByteArrayLiteral("DISTRIB_DESCRIPTION="));
322 if (ok && (v.prettyName.isEmpty() || v.prettyName == v.productType)) {
323 // some distributions have redundant information for the pretty name,
324 // so try /etc/<lowercasename>-release
325
326 // we're still avoiding QFile here
327 QByteArray distrorelease = "/etc/" + v.productType.toLatin1().toLower() + "-release";
328 int fd = qt_safe_open(distrorelease, O_RDONLY);
329 if (fd != -1) {
330 QT_STATBUF sbuf;
331 if (QT_FSTAT(fd, &sbuf) != -1 && sbuf.st_size > v.prettyName.size()) {
332 // file apparently contains interesting information
333 QByteArray buffer(sbuf.st_size, Qt::Uninitialized);
334 buffer.resize(qt_safe_read(fd, buffer.data(), sbuf.st_size));
335 v.prettyName = QString::fromLatin1(buffer.trimmed());
336 }
337 qt_safe_close(fd);
338 }
339 }
340
341 // some distributions have a /etc/lsb-release file that does not provide the values
342 // we are looking for, i.e. DISTRIB_ID, DISTRIB_RELEASE and DISTRIB_DESCRIPTION.
343 // Assuming that neither DISTRIB_ID nor DISTRIB_RELEASE were found, or contained valid values,
344 // returning false for readEtcLsbRelease will allow further /etc/<lowercasename>-release parsing.
345 return ok && !(v.productType.isEmpty() && v.productVersion.isEmpty());
346}
347
348#if defined(Q_OS_LINUX)
349static QByteArray getEtcFileFirstLine(const char *fileName)
350{
351 QByteArray buffer = getEtcFileContent(fileName);
352 if (buffer.isEmpty())
353 return QByteArray();
354
355 const char *ptr = buffer.constData();
356 return QByteArray(ptr, buffer.indexOf("\n")).trimmed();
357}
358
359static bool readEtcRedHatRelease(QUnixOSVersion &v)
360{
361 // /etc/redhat-release analysed should be a one line file
362 // the format of its content is <Vendor_ID release Version>
363 // i.e. "Red Hat Enterprise Linux Workstation release 6.5 (Santiago)"
364 QByteArray line = getEtcFileFirstLine("/etc/redhat-release");
365 if (line.isEmpty())
366 return false;
367
368 v.prettyName = QString::fromLatin1(line);
369
370 const char keyword[] = "release ";
371 const qsizetype releaseIndex = line.indexOf(keyword);
372 v.productType = QString::fromLatin1(line.mid(0, releaseIndex)).remove(u' ');
373 const qsizetype spaceIndex = line.indexOf(' ', releaseIndex + strlen(keyword));
374 v.productVersion = QString::fromLatin1(line.mid(releaseIndex + strlen(keyword),
375 spaceIndex > -1 ? spaceIndex - releaseIndex - int(strlen(keyword)) : -1));
376 return true;
377}
378
379static bool readEtcDebianVersion(QUnixOSVersion &v)
380{
381 // /etc/debian_version analysed should be a one line file
382 // the format of its content is <Release_ID/sid>
383 // i.e. "jessie/sid"
384 QByteArray line = getEtcFileFirstLine("/etc/debian_version");
385 if (line.isEmpty())
386 return false;
387
388 v.productType = QStringLiteral("Debian");
389 v.productVersion = QString::fromLatin1(line);
390 return true;
391}
392#endif
393
394static bool findUnixOsVersion(QUnixOSVersion &v)
395{
396 if (readOsRelease(v))
397 return true;
398 if (readEtcLsbRelease(v))
399 return true;
400#if defined(Q_OS_LINUX)
401 if (readEtcRedHatRelease(v))
402 return true;
403 if (readEtcDebianVersion(v))
404 return true;
405#endif
406 return false;
407}
408# endif // USE_ETC_OS_RELEASE
409#endif // Q_OS_UNIX
410
411#ifdef Q_OS_ANDROID
412static const char *osVer_helper(QOperatingSystemVersion)
413{
414 // https://source.android.com/source/build-numbers.html
415 // https://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels
416 const int sdk_int = QtAndroidPrivate::androidSdkVersion();
417 switch (sdk_int) {
418 case 3:
419 return "Cupcake";
420 case 4:
421 return "Donut";
422 case 5:
423 case 6:
424 case 7:
425 return "Eclair";
426 case 8:
427 return "Froyo";
428 case 9:
429 case 10:
430 return "Gingerbread";
431 case 11:
432 case 12:
433 case 13:
434 return "Honeycomb";
435 case 14:
436 case 15:
437 return "Ice Cream Sandwich";
438 case 16:
439 case 17:
440 case 18:
441 return "Jelly Bean";
442 case 19:
443 case 20:
444 return "KitKat";
445 case 21:
446 case 22:
447 return "Lollipop";
448 case 23:
449 return "Marshmallow";
450 case 24:
451 case 25:
452 return "Nougat";
453 case 26:
454 case 27:
455 return "Oreo";
456 case 28:
457 return "Pie";
458 case 29:
459 return "10";
460 case 30:
461 return "11";
462 case 31:
463 return "12";
464 case 32:
465 return "12L";
466 case 33:
467 return "13";
468 default:
469 break;
470 }
471
472 return "";
473}
474#endif
475
476/*!
477 \since 5.4
478
479 Returns the architecture of the CPU that Qt was compiled for, in text
480 format. Note that this may not match the actual CPU that the application is
481 running on if there's an emulation layer or if the CPU supports multiple
482 architectures (like x86-64 processors supporting i386 applications). To
483 detect that, use currentCpuArchitecture().
484
485 Values returned by this function are stable and will not change over time,
486 so applications can rely on the returned value as an identifier, except
487 that new CPU types may be added over time.
488
489 Typical returned values are (note: list not exhaustive):
490 \list
491 \li "arm"
492 \li "arm64"
493 \li "i386"
494 \li "ia64"
495 \li "mips"
496 \li "mips64"
497 \li "power"
498 \li "power64"
499 \li "sparc"
500 \li "sparcv9"
501 \li "x86_64"
502 \endlist
503
504 \sa QSysInfo::buildAbi(), QSysInfo::currentCpuArchitecture()
505*/
506QString QSysInfo::buildCpuArchitecture()
507{
508 return QStringLiteral(ARCH_PROCESSOR);
509}
510
511/*!
512 \since 5.4
513
514 Returns the architecture of the CPU that the application is running on, in
515 text format. Note that this function depends on what the OS will report and
516 may not detect the actual CPU architecture if the OS hides that information
517 or is unable to provide it. For example, a 32-bit OS running on a 64-bit
518 CPU is usually unable to determine the CPU is actually capable of running
519 64-bit programs.
520
521 Values returned by this function are mostly stable: an attempt will be made
522 to ensure that they stay constant over time and match the values returned
523 by buildCpuArchitecture(). However, due to the nature of the
524 operating system functions being used, there may be discrepancies.
525
526 Typical returned values are (note: list not exhaustive):
527 \list
528 \li "arm"
529 \li "arm64"
530 \li "i386"
531 \li "ia64"
532 \li "mips"
533 \li "mips64"
534 \li "power"
535 \li "power64"
536 \li "sparc"
537 \li "sparcv9"
538 \li "x86_64"
539 \endlist
540
541 \sa QSysInfo::buildAbi(), QSysInfo::buildCpuArchitecture()
542*/
543QString QSysInfo::currentCpuArchitecture()
544{
545#if defined(Q_OS_WIN)
546 // We don't need to catch all the CPU architectures in this function;
547 // only those where the host CPU might be different than the build target
548 // (usually, 64-bit platforms).
549 SYSTEM_INFO info;
550 GetNativeSystemInfo(&info);
551 switch (info.wProcessorArchitecture) {
552# ifdef PROCESSOR_ARCHITECTURE_AMD64
553 case PROCESSOR_ARCHITECTURE_AMD64:
554 return QStringLiteral("x86_64");
555# endif
556# ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
557 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
558# endif
559 case PROCESSOR_ARCHITECTURE_IA64:
560 return QStringLiteral("ia64");
561 }
562#elif defined(Q_OS_DARWIN) && !defined(Q_OS_MACOS)
563 // iOS-based OSes do not return the architecture on uname(2)'s result.
564 return buildCpuArchitecture();
565#elif defined(Q_OS_UNIX)
566 long ret = -1;
567 struct utsname u;
568
569# if defined(Q_OS_SOLARIS)
570 // We need a special call for Solaris because uname(2) on x86 returns "i86pc" for
571 // both 32- and 64-bit CPUs. Reference:
572 // http://docs.oracle.com/cd/E18752_01/html/816-5167/sysinfo-2.html#REFMAN2sysinfo-2
573 // http://fxr.watson.org/fxr/source/common/syscall/systeminfo.c?v=OPENSOLARIS
574 // http://fxr.watson.org/fxr/source/common/conf/param.c?v=OPENSOLARIS;im=10#L530
575 if (ret == -1)
576 ret = sysinfo(SI_ARCHITECTURE_64, u.machine, sizeof u.machine);
577# endif
578
579 if (ret == -1)
580 ret = uname(&u);
581
582 // we could use detectUnixVersion() above, but we only need a field no other function does
583 if (ret != -1) {
584 // the use of QT_BUILD_INTERNAL here is simply to ensure all branches build
585 // as we don't often build on some of the less common platforms
586# if defined(Q_PROCESSOR_ARM) || defined(QT_BUILD_INTERNAL)
587 if (strcmp(u.machine, "aarch64") == 0)
588 return QStringLiteral("arm64");
589 if (strncmp(u.machine, "armv", 4) == 0)
590 return QStringLiteral("arm");
591# endif
592# if defined(Q_PROCESSOR_POWER) || defined(QT_BUILD_INTERNAL)
593 // harmonize "powerpc" and "ppc" to "power"
594 if (strncmp(u.machine, "ppc", 3) == 0)
595 return "power"_L1 + QLatin1StringView(u.machine + 3);
596 if (strncmp(u.machine, "powerpc", 7) == 0)
597 return "power"_L1 + QLatin1StringView(u.machine + 7);
598 if (strcmp(u.machine, "Power Macintosh") == 0)
599 return "power"_L1;
600# endif
601# if defined(Q_PROCESSOR_SPARC) || defined(QT_BUILD_INTERNAL)
602 // Solaris sysinfo(2) (above) uses "sparcv9", but uname -m says "sun4u";
603 // Linux says "sparc64"
604 if (strcmp(u.machine, "sun4u") == 0 || strcmp(u.machine, "sparc64") == 0)
605 return QStringLiteral("sparcv9");
606 if (strcmp(u.machine, "sparc32") == 0)
607 return QStringLiteral("sparc");
608# endif
609# if defined(Q_PROCESSOR_X86) || defined(QT_BUILD_INTERNAL)
610 // harmonize all "i?86" to "i386"
611 if (strlen(u.machine) == 4 && u.machine[0] == 'i'
612 && u.machine[2] == '8' && u.machine[3] == '6')
613 return QStringLiteral("i386");
614 if (strcmp(u.machine, "amd64") == 0) // Solaris
615 return QStringLiteral("x86_64");
616# endif
617 return QString::fromLatin1(u.machine);
618 }
619#endif
620 return buildCpuArchitecture();
621}
622
623/*!
624 \since 5.4
625
626 Returns the full architecture string that Qt was compiled for. This string
627 is useful for identifying different, incompatible builds. For example, it
628 can be used as an identifier to request an upgrade package from a server.
629
630 The values returned from this function are kept stable as follows: the
631 mandatory components of the result will not change in future versions of
632 Qt, but optional suffixes may be added.
633
634 The returned value is composed of three or more parts, separated by dashes
635 ("-"). They are:
636
637 \table
638 \header \li Component \li Value
639 \row \li CPU Architecture \li The same as QSysInfo::buildCpuArchitecture(), such as "arm", "i386", "mips" or "x86_64"
640 \row \li Endianness \li "little_endian" or "big_endian"
641 \row \li Word size \li Whether it's a 32- or 64-bit application. Possible values are:
642 "llp64" (Windows 64-bit), "lp64" (Unix 64-bit), "ilp32" (32-bit)
643 \row \li (Optional) ABI \li Zero or more components identifying different ABIs possible in this architecture.
644 Currently, Qt has optional ABI components for ARM and MIPS processors: one
645 component is the main ABI (such as "eabi", "o32", "n32", "o64"); another is
646 whether the calling convention is using hardware floating point registers ("hardfloat"
647 is present).
648
649 Additionally, if Qt was configured with \c{-qreal float}, the ABI option tag "qreal_float"
650 will be present. If Qt was configured with another type as qreal, that type is present after
651 "qreal_", with all characters other than letters and digits escaped by an underscore, followed
652 by two hex digits. For example, \c{-qreal long double} becomes "qreal_long_20double".
653 \endtable
654
655 \sa QSysInfo::buildCpuArchitecture()
656*/
657QString QSysInfo::buildAbi()
658{
659 // ARCH_FULL is a concatenation of strings (incl. ARCH_PROCESSOR), which breaks
660 // QStringLiteral on MSVC. Since the concatenation behavior we want is specified
661 // the same C++11 paper as the Unicode strings, we'll use that macro and hope
662 // that Microsoft implements the new behavior when they add support for Unicode strings.
663 return QStringLiteral(ARCH_FULL);
664}
665
666static QString unknownText()
667{
668 return QStringLiteral("unknown");
669}
670
671/*!
672 \since 5.4
673
674 Returns the type of the operating system kernel Qt was compiled for. It's
675 also the kernel the application is running on, unless the host operating
676 system is running a form of compatibility or virtualization layer.
677
678 Values returned by this function are stable and will not change over time,
679 so applications can rely on the returned value as an identifier, except
680 that new OS kernel types may be added over time.
681
682 On Windows, this function returns the type of Windows kernel, like "winnt".
683 On Unix systems, it returns the same as the output of \c{uname
684 -s} (lowercased).
685
686 \note This function may return surprising values: it returns "linux"
687 for all operating systems running Linux (including Android), "qnx" for all
688 operating systems running QNX, "freebsd" for
689 Debian/kFreeBSD, and "darwin" for \macos and iOS. For information on the type
690 of product the application is running on, see productType().
691
692 \sa QFileSelector, kernelVersion(), productType(), productVersion(), prettyProductName()
693*/
694QString QSysInfo::kernelType()
695{
696#if defined(Q_OS_WIN)
697 return QStringLiteral("winnt");
698#elif defined(Q_OS_UNIX)
699 struct utsname u;
700 if (uname(&u) == 0)
701 return QString::fromLatin1(u.sysname).toLower();
702#endif
703 return unknownText();
704}
705
706/*!
707 \since 5.4
708
709 Returns the release version of the operating system kernel. On Windows, it
710 returns the version of the NT kernel. On Unix systems, including
711 Android and \macos, it returns the same as the \c{uname -r}
712 command would return. On VxWorks, it returns the numeric part of the string
713 reported by kernelVersion().
714
715 If the version could not be determined, this function may return an empty
716 string.
717
718 \sa kernelType(), productType(), productVersion(), prettyProductName()
719*/
720QString QSysInfo::kernelVersion()
721{
722#ifdef Q_OS_WIN
723 const auto osver = QOperatingSystemVersion::current();
724 return QString::asprintf("%d.%d.%d",
725 osver.majorVersion(), osver.minorVersion(), osver.microVersion());
726#else
727 struct utsname u;
728 if (uname(&u) == 0) {
729# ifdef Q_OS_VXWORKS
730 // The string follows the pattern "Core Kernel version: w.x.y.z"
731 auto versionStr = QByteArrayView(u.kernelversion);
732 if (auto lastSpace = versionStr.lastIndexOf(' '); lastSpace != -1) {
733 return QString::fromLatin1(versionStr.sliced(lastSpace + 1));
734 }
735# else
736 return QString::fromLatin1(u.release);
737# endif
738 }
739 return QString();
740#endif
741}
742
743
744/*!
745 \since 5.4
746
747 Returns the product name of the operating system this application is
748 running in. If the application is running on some sort of emulation or
749 virtualization layer (such as WINE on a Unix system), this function will
750 inspect the emulation / virtualization layer.
751
752 Values returned by this function are stable and will not change over time,
753 so applications can rely on the returned value as an identifier, except
754 that new OS types may be added over time.
755
756 \b{Linux and Android note}: this function returns "android" for Linux
757 systems running Android userspace, notably when using the Bionic library.
758 For all other Linux systems, regardless of C library being used, it tries
759 to determine the distribution name and returns that. If determining the
760 distribution name failed, it returns "unknown".
761
762 \b{\macos note}: this function returns "macos" for all \macos systems,
763 regardless of Apple naming convention. Previously, in Qt 5, it returned
764 "osx", again regardless of Apple naming conventions.
765
766 \b{Darwin, iOS, tvOS, and watchOS note}: this function returns "ios" for
767 iOS systems, "tvos" for tvOS systems, "watchos" for watchOS systems, and
768 "darwin" in case the system could not be determined.
769
770 \b{FreeBSD note}: this function returns "debian" for Debian/kFreeBSD and
771 "unknown" otherwise.
772
773 \b{Windows note}: this function return "windows"
774
775 \b{VxWorks note}: this function return "vxworks"
776
777 For other Unix-type systems, this function usually returns "unknown".
778
779 \sa QFileSelector, kernelType(), kernelVersion(), productVersion(), prettyProductName()
780*/
781QString QSysInfo::productType()
782{
783 // similar, but not identical to QFileSelectorPrivate::platformSelectors
784#if defined(Q_OS_WIN)
785 return QStringLiteral("windows");
786
787#elif defined(Q_OS_QNX)
788 return QStringLiteral("qnx");
789
790#elif defined(Q_OS_ANDROID)
791 return QStringLiteral("android");
792
793#elif defined(Q_OS_IOS)
794 return QStringLiteral("ios");
795#elif defined(Q_OS_TVOS)
796 return QStringLiteral("tvos");
797#elif defined(Q_OS_WATCHOS)
798 return QStringLiteral("watchos");
799#elif defined(Q_OS_VISIONOS)
800 return QStringLiteral("visionos");
801#elif defined(Q_OS_MACOS)
802 return QStringLiteral("macos");
803#elif defined(Q_OS_DARWIN)
804 return QStringLiteral("darwin");
805#elif defined(Q_OS_WASM)
806 return QStringLiteral("wasm");
807#elif defined(Q_OS_VXWORKS)
808 return QStringLiteral("vxworks");
809
810#elif defined(USE_ETC_OS_RELEASE) // Q_OS_UNIX
811 QUnixOSVersion unixOsVersion;
812 findUnixOsVersion(unixOsVersion);
813 if (!unixOsVersion.productType.isEmpty())
814 return unixOsVersion.productType;
815#endif
816 return unknownText();
817}
818
819/*!
820 \since 5.4
821
822 Returns the product version of the operating system in string form. If the
823 version could not be determined, this function returns "unknown".
824
825 It will return the Android, iOS, \macos, VxWorks, Windows full-product
826 versions on those systems.
827
828 Typical returned values are (note: list not exhaustive):
829 \list
830 \li "12" (Android 12)
831 \li "36" (Fedora 36)
832 \li "15.5" (iOS 15.5)
833 \li "12.4" (macOS Monterey)
834 \li "22.04" (Ubuntu 22.04)
835 \li "8.6" (watchOS 8.6)
836 \li "11" (Windows 11)
837 \li "Server 2022" (Windows Server 2022)
838 \li "24.03" (VxWorks 7 - 24.03)
839 \endlist
840
841 On Linux systems, it will try to determine the distribution version and will
842 return that. This is also done on Debian/kFreeBSD, so this function will
843 return Debian version in that case.
844
845 In all other Unix-type systems, this function always returns "unknown".
846
847 \note The version string returned from this function is not guaranteed to
848 be orderable. On Linux, the version of
849 the distribution may jump unexpectedly, please refer to the distribution's
850 documentation for versioning practices.
851
852 \sa kernelType(), kernelVersion(), productType(), prettyProductName()
853*/
854QString QSysInfo::productVersion()
855{
856#if defined(Q_OS_ANDROID)
857 const auto version = QOperatingSystemVersion::current();
858 return QString::asprintf("%d.%d", version.majorVersion(), version.minorVersion());
859#elif defined(Q_OS_DARWIN)
860 const auto version = QOperatingSystemVersion::current();
861 return QString::asprintf("%d.%d.%d", version.majorVersion(),
862 version.minorVersion(),
863 version.microVersion());
864#elif defined(Q_OS_WIN)
865 const char *version = osVer_helper();
866 if (version) {
867 const QLatin1Char spaceChar(' ');
868 return QString::fromLatin1(version).remove(spaceChar).toLower() + winSp_helper().remove(spaceChar).toLower();
869 }
870 // fall through
871
872#elif defined(Q_OS_VXWORKS)
873 utsname u;
874 if (uname(&u) == 0)
875 return QString::fromLatin1(u.releaseversion);
876 // fall through
877
878#elif defined(USE_ETC_OS_RELEASE) // Q_OS_UNIX
879 QUnixOSVersion unixOsVersion;
880 findUnixOsVersion(unixOsVersion);
881 if (!unixOsVersion.productVersion.isEmpty())
882 return unixOsVersion.productVersion;
883#endif
884
885 // fallback
886 return unknownText();
887}
888
889/*!
890 \since 5.4
891
892 Returns a prettier form of productType() and productVersion(), containing
893 other tokens like the operating system type, codenames and other
894 information. The result of this function is suitable for displaying to the
895 user, but not for long-term storage, as the string may change with updates
896 to Qt.
897
898 If productType() is "unknown", this function will instead use the
899 kernelType() and kernelVersion() functions.
900
901 \sa kernelType(), kernelVersion(), productType(), productVersion()
902*/
903QString QSysInfo::prettyProductName()
904{
905#if defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN) || defined(Q_OS_WIN)
906 const auto version = QOperatingSystemVersion::current();
907 QString versionString;
908# if defined(Q_OS_DARWIN)
909 if (const int microVersion = version.microVersion(); microVersion > 0)
910 versionString = QString::asprintf("%d.%d.%d", version.majorVersion(),
911 version.minorVersion(),
912 microVersion);
913 else
914# endif // Darwin
915 versionString = QString::asprintf("%d.%d", version.majorVersion(),
916 version.minorVersion());
917 QString result = version.name() + u' ';
918 const char *name = osVer_helper(version);
919 if (!name)
920 return result + versionString;
921 result += QLatin1StringView(name);
922# if !defined(Q_OS_WIN)
923 return result + " ("_L1 + versionString + u')';
924# else
925 // (resembling winver.exe): Windows 10 "Windows 10 Version 1809"
926 const auto displayVersion = windowsDisplayVersion();
927 if (!displayVersion.isEmpty())
928 result += " Version "_L1 + displayVersion;
929 return result;
930# endif // Windows
931#elif defined(Q_OS_HAIKU)
932 return "Haiku "_L1 + productVersion();
933#elif defined(Q_OS_UNIX)
934# ifdef USE_ETC_OS_RELEASE
935 QUnixOSVersion unixOsVersion;
936 findUnixOsVersion(unixOsVersion);
937 if (!unixOsVersion.prettyName.isEmpty())
938 return unixOsVersion.prettyName;
939# endif
940 struct utsname u;
941 if (uname(&u) == 0)
942 return QString::fromLatin1(u.sysname) + u' ' + QString::fromLatin1(u.release);
943#endif
944 return unknownText();
945}
946
947#ifndef QT_BOOTSTRAPPED
948/*!
949 \since 5.6
950
951 Returns this machine's host name, if one is configured. Note that hostnames
952 are not guaranteed to be globally unique, especially if they were
953 configured automatically.
954
955 This function does not guarantee the returned host name is a Fully
956 Qualified Domain Name (FQDN). For that, use QHostInfo to resolve the
957 returned name to an FQDN.
958
959 This function returns the same as QHostInfo::localHostName().
960
961 \sa QHostInfo::localDomainName, machineUniqueId()
962*/
963QString QSysInfo::machineHostName()
964{
965 // the hostname can change, so we can't cache it
966#if defined(Q_OS_LINUX)
967 // gethostname(3) on Linux just calls uname(2), so do it ourselves
968 // and avoid a memcpy
969 struct utsname u;
970 if (uname(&u) == 0)
971 return QString::fromLocal8Bit(u.nodename);
972 return QString();
973#else
974# ifdef Q_OS_WIN
975 // Important: QtNetwork depends on machineHostName() initializing ws2_32.dll
976 winsockInit();
977 QString hostName;
978 hostName.resize(512);
979 unsigned long len = hostName.size();
980 BOOL res = GetComputerNameEx(ComputerNameDnsHostname,
981 reinterpret_cast<wchar_t *>(hostName.data()), &len);
982 if (!res && len > 512) {
983 hostName.resize(len - 1);
984 GetComputerNameEx(ComputerNameDnsHostname, reinterpret_cast<wchar_t *>(hostName.data()),
985 &len);
986 }
987 hostName.truncate(len);
988 return hostName;
989# else // !Q_OS_WIN
990
991 char hostName[512];
992 if (gethostname(hostName, sizeof(hostName)) == -1)
993 return QString();
994 hostName[sizeof(hostName) - 1] = '\0';
995 return QString::fromLocal8Bit(hostName);
996# endif
997#endif
998}
999#endif // QT_BOOTSTRAPPED
1000
1001enum {
1002 UuidStringLen = sizeof("00000000-0000-0000-0000-000000000000") - 1
1003};
1004
1005/*!
1006 \since 5.11
1007
1008 Returns a unique ID for this machine, if one can be determined. If no
1009 unique ID could be determined, this function returns an empty byte array.
1010 Unlike machineHostName(), the value returned by this function is likely
1011 globally unique.
1012
1013 A unique ID is useful in network operations to identify this machine for an
1014 extended period of time, when the IP address could change or if this
1015 machine could have more than one IP address. For example, the ID could be
1016 used when communicating with a server or when storing device-specific data
1017 in shared network storage.
1018
1019 Note that on some systems, this value will persist across reboots and on
1020 some it will not. Applications should not blindly depend on this fact
1021 without verifying the OS capabilities. In particular, on Linux systems,
1022 this ID is usually permanent and it matches the D-Bus machine ID, except
1023 for nodes without their own storage (replicated nodes).
1024
1025 \sa machineHostName(), bootUniqueId()
1026*/
1027QByteArray QSysInfo::machineUniqueId()
1028{
1029#if defined(Q_OS_DARWIN) && __has_include(<IOKit/IOKitLib.h>)
1030 char uuid[UuidStringLen + 1];
1031 io_service_t service = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
1032 QCFString stringRef = (CFStringRef)IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0);
1033 CFStringGetCString(stringRef, uuid, sizeof(uuid), kCFStringEncodingMacRoman);
1034 return QByteArray(uuid);
1035#elif defined(Q_OS_BSD4) && defined(KERN_HOSTUUID)
1036 char uuid[UuidStringLen + 1];
1037 size_t uuidlen = sizeof(uuid);
1038 int name[] = { CTL_KERN, KERN_HOSTUUID };
1039 if (sysctl(name, sizeof name / sizeof name[0], &uuid, &uuidlen, nullptr, 0) == 0
1040 && uuidlen == sizeof(uuid))
1041 return QByteArray(uuid, uuidlen - 1);
1042#elif defined(Q_OS_UNIX)
1043 // The modern name on Linux is /etc/machine-id, but that path is
1044 // unlikely to exist on non-Linux (non-systemd) systems. The old
1045 // path is more than enough.
1046 static const char fullfilename[] = "/usr/local/var/lib/dbus/machine-id";
1047 const char *firstfilename = fullfilename + sizeof("/usr/local") - 1;
1048 int fd = qt_safe_open(firstfilename, O_RDONLY);
1049 if (fd == -1 && errno == ENOENT)
1050 fd = qt_safe_open(fullfilename, O_RDONLY);
1051
1052 if (fd != -1) {
1053 char buffer[32]; // 128 bits, hex-encoded
1054 qint64 len = qt_safe_read(fd, buffer, sizeof(buffer));
1055 qt_safe_close(fd);
1056
1057 if (len != -1)
1058 return QByteArray(buffer, len);
1059 }
1060#elif defined(Q_OS_WIN)
1061 // Let's poke at the registry
1062 const QString machineGuid = QWinRegistryKey(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Cryptography)")
1063 .stringValue(L"MachineGuid");
1064 if (!machineGuid.isEmpty())
1065 return machineGuid.toLatin1();
1066#endif
1067 return QByteArray();
1068}
1069
1070/*!
1071 \since 5.11
1072
1073 Returns a unique ID for this machine's boot, if one can be determined. If
1074 no unique ID could be determined, this function returns an empty byte
1075 array. This value is expected to change after every boot and can be
1076 considered globally unique.
1077
1078 This function is currently only implemented for Linux and Apple operating
1079 systems.
1080
1081 \sa machineUniqueId()
1082*/
1083QByteArray QSysInfo::bootUniqueId()
1084{
1085#ifdef Q_OS_LINUX
1086 // use low-level API here for simplicity
1087 int fd = qt_safe_open("/proc/sys/kernel/random/boot_id", O_RDONLY);
1088 if (fd != -1) {
1089 char uuid[UuidStringLen];
1090 qint64 len = qt_safe_read(fd, uuid, sizeof(uuid));
1091 qt_safe_close(fd);
1092 if (len == UuidStringLen)
1093 return QByteArray(uuid, UuidStringLen);
1094 }
1095#elif defined(Q_OS_DARWIN)
1096 // "kern.bootsessionuuid" is only available by name
1097 char uuid[UuidStringLen + 1];
1098 size_t uuidlen = sizeof(uuid);
1099 if (sysctlbyname("kern.bootsessionuuid", uuid, &uuidlen, nullptr, 0) == 0
1100 && uuidlen == sizeof(uuid))
1101 return QByteArray(uuid, uuidlen - 1);
1102#endif
1103 return QByteArray();
1104};
1105
1106QT_END_NAMESPACE
#define ARCH_PROCESSOR
#define ARCH_FULL
Combined button and popup list for selecting options.
#define __has_include(x)
@ UuidStringLen
static QString unknownText()
Definition qsysinfo.cpp:666