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

source code of qtbase/src/corelib/global/qsysinfo.cpp