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 switch (version.majorVersion()) {
95 case 10: {
96 switch (version.minorVersion()) {
97 case 9: return "Mavericks";
98 case 10: return "Yosemite";
99 case 11: return "El Capitan";
100 case 12: return "Sierra";
101 case 13: return "High Sierra";
102 case 14: return "Mojave";
103 case 15: return "Catalina";
104 case 16: return "Big Sur";
105 default:
106 Q_UNREACHABLE();
107 }
108 }
109 case 11: return "Big Sur";
110 case 12: return "Monterey";
111 case 13: return "Ventura";
112 case 14: return "Sonoma";
113 case 15: return "Sequoia";
114 default:
115 // Unknown, future version
116 break;
117 }
118#else
119 Q_UNUSED(version);
120#endif
121 return nullptr;
122}
123
124#elif defined(Q_OS_WIN) || defined(Q_OS_CYGWIN)
125
126# ifndef QT_BOOTSTRAPPED
127class QWindowsSockInit
128{
129public:
130 QWindowsSockInit();
131 ~QWindowsSockInit();
132 int version;
133};
134
135QWindowsSockInit::QWindowsSockInit()
136: version(0)
137{
138 //### should we try for 2.2 on all platforms ??
139 WSAData wsadata;
140
141 // IPv6 requires Winsock v2.0 or better.
142 if (WSAStartup(MAKEWORD(2, 0), &wsadata) != 0) {
143 qWarning("QTcpSocketAPI: WinSock v2.0 initialization failed.");
144 } else {
145 version = 0x20;
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(pos: 1).chopped(len: 1);
240 return QString::fromUtf8(utf8: str);
241}
242
243static QByteArray getEtcFileContent(const char *filename)
244{
245 // we're avoiding QFile here
246 int fd = qt_safe_open(pathname: filename, O_RDONLY);
247 if (fd == -1)
248 return QByteArray();
249
250 QT_STATBUF sbuf;
251 if (QT_FSTAT(fd: fd, buf: &sbuf) == -1) {
252 qt_safe_close(fd);
253 return QByteArray();
254 }
255
256 QByteArray buffer(sbuf.st_size, Qt::Uninitialized);
257 buffer.resize(size: qt_safe_read(fd, data: buffer.data(), maxlen: 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(s: ptr, c: '\n', n: end - ptr));
277 if (!eol)
278 eol = end - 1;
279 line.setRawData(a: ptr, n: eol - ptr);
280
281 if (line.startsWith(bv: idKey)) {
282 ptr += idKey.size();
283 v.productType = unquote(str: {ptr, eol});
284 continue;
285 }
286
287 if (line.startsWith(bv: prettyNameKey)) {
288 ptr += prettyNameKey.size();
289 v.prettyName = unquote(str: {ptr, eol});
290 continue;
291 }
292
293 if (line.startsWith(bv: versionKey)) {
294 ptr += versionKey.size();
295 v.productVersion = unquote(str: {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, filename: "/etc/os-release", idKey: id, versionKey: versionId, prettyNameKey: prettyName) ||
315 readEtcFile(v, filename: "/usr/lib/os-release", idKey: id, versionKey: versionId, prettyNameKey: prettyName);
316}
317
318static bool readEtcLsbRelease(QUnixOSVersion &v)
319{
320 bool ok = readEtcFile(v, filename: "/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(pathname: distrorelease, O_RDONLY);
329 if (fd != -1) {
330 QT_STATBUF sbuf;
331 if (QT_FSTAT(fd: fd, buf: &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(size: qt_safe_read(fd, data: buffer.data(), maxlen: sbuf.st_size));
335 v.prettyName = QString::fromLatin1(ba: 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: fileName);
352 if (buffer.isEmpty())
353 return QByteArray();
354
355 const char *ptr = buffer.constData();
356 return QByteArray(ptr, buffer.indexOf(bv: "\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(fileName: "/etc/redhat-release");
365 if (line.isEmpty())
366 return false;
367
368 v.prettyName = QString::fromLatin1(ba: line);
369
370 const char keyword[] = "release ";
371 const qsizetype releaseIndex = line.indexOf(bv: keyword);
372 v.productType = QString::fromLatin1(ba: line.mid(index: 0, len: releaseIndex)).remove(c: u' ');
373 const qsizetype spaceIndex = line.indexOf(c: ' ', from: releaseIndex + strlen(s: keyword));
374 v.productVersion = QString::fromLatin1(ba: line.mid(index: releaseIndex + strlen(s: keyword),
375 len: spaceIndex > -1 ? spaceIndex - releaseIndex - int(strlen(s: 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(fileName: "/etc/debian_version");
385 if (line.isEmpty())
386 return false;
387
388 v.productType = QStringLiteral("Debian");
389 v.productVersion = QString::fromLatin1(ba: 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(name: &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(s1: u.machine, s2: "aarch64") == 0)
588 return QStringLiteral("arm64");
589 if (strncmp(s1: u.machine, s2: "armv", n: 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(s1: u.machine, s2: "ppc", n: 3) == 0)
595 return "power"_L1 + QLatin1StringView(u.machine + 3);
596 if (strncmp(s1: u.machine, s2: "powerpc", n: 7) == 0)
597 return "power"_L1 + QLatin1StringView(u.machine + 7);
598 if (strcmp(s1: u.machine, s2: "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(s1: u.machine, s2: "sun4u") == 0 || strcmp(s1: u.machine, s2: "sparc64") == 0)
605 return QStringLiteral("sparcv9");
606 if (strcmp(s1: u.machine, s2: "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(s: u.machine) == 4 && u.machine[0] == 'i'
612 && u.machine[2] == '8' && u.machine[3] == '6')
613 return QStringLiteral("i386");
614 if (strcmp(s1: u.machine, s2: "amd64") == 0) // Solaris
615 return QStringLiteral("x86_64");
616# endif
617 return QString::fromLatin1(ba: 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(name: &u) == 0)
701 return QString::fromLatin1(ba: 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(name: &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(ba: 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(v&: 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) || defined(Q_OS_DARWIN)
857 const auto version = QOperatingSystemVersion::current();
858 return QString::asprintf("%d.%d", version.majorVersion(), version.minorVersion());
859#elif defined(Q_OS_WIN)
860 const char *version = osVer_helper();
861 if (version) {
862 const QLatin1Char spaceChar(' ');
863 return QString::fromLatin1(version).remove(spaceChar).toLower() + winSp_helper().remove(spaceChar).toLower();
864 }
865 // fall through
866
867#elif defined(Q_OS_VXWORKS)
868 utsname u;
869 if (uname(&u) == 0)
870 return QString::fromLatin1(u.releaseversion);
871 // fall through
872
873#elif defined(USE_ETC_OS_RELEASE) // Q_OS_UNIX
874 QUnixOSVersion unixOsVersion;
875 findUnixOsVersion(v&: unixOsVersion);
876 if (!unixOsVersion.productVersion.isEmpty())
877 return unixOsVersion.productVersion;
878#endif
879
880 // fallback
881 return unknownText();
882}
883
884/*!
885 \since 5.4
886
887 Returns a prettier form of productType() and productVersion(), containing
888 other tokens like the operating system type, codenames and other
889 information. The result of this function is suitable for displaying to the
890 user, but not for long-term storage, as the string may change with updates
891 to Qt.
892
893 If productType() is "unknown", this function will instead use the
894 kernelType() and kernelVersion() functions.
895
896 \sa kernelType(), kernelVersion(), productType(), productVersion()
897*/
898QString QSysInfo::prettyProductName()
899{
900#if defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN) || defined(Q_OS_WIN)
901 const auto version = QOperatingSystemVersion::current();
902 const int majorVersion = version.majorVersion();
903 const QString versionString = QString::asprintf("%d.%d", majorVersion, version.minorVersion());
904 QString result = version.name() + u' ';
905 const char *name = osVer_helper(version);
906 if (!name)
907 return result + versionString;
908 result += QLatin1StringView(name);
909# if !defined(Q_OS_WIN)
910 return result + " ("_L1 + versionString + u')';
911# else
912 // (resembling winver.exe): Windows 10 "Windows 10 Version 1809"
913 const auto displayVersion = windowsDisplayVersion();
914 if (!displayVersion.isEmpty())
915 result += " Version "_L1 + displayVersion;
916 return result;
917# endif // Windows
918#elif defined(Q_OS_HAIKU)
919 return "Haiku "_L1 + productVersion();
920#elif defined(Q_OS_UNIX)
921# ifdef USE_ETC_OS_RELEASE
922 QUnixOSVersion unixOsVersion;
923 findUnixOsVersion(v&: unixOsVersion);
924 if (!unixOsVersion.prettyName.isEmpty())
925 return unixOsVersion.prettyName;
926# endif
927 struct utsname u;
928 if (uname(name: &u) == 0)
929 return QString::fromLatin1(ba: u.sysname) + u' ' + QString::fromLatin1(ba: u.release);
930#endif
931 return unknownText();
932}
933
934#ifndef QT_BOOTSTRAPPED
935/*!
936 \since 5.6
937
938 Returns this machine's host name, if one is configured. Note that hostnames
939 are not guaranteed to be globally unique, especially if they were
940 configured automatically.
941
942 This function does not guarantee the returned host name is a Fully
943 Qualified Domain Name (FQDN). For that, use QHostInfo to resolve the
944 returned name to an FQDN.
945
946 This function returns the same as QHostInfo::localHostName().
947
948 \sa QHostInfo::localDomainName, machineUniqueId()
949*/
950QString QSysInfo::machineHostName()
951{
952 // the hostname can change, so we can't cache it
953#if defined(Q_OS_LINUX)
954 // gethostname(3) on Linux just calls uname(2), so do it ourselves
955 // and avoid a memcpy
956 struct utsname u;
957 if (uname(name: &u) == 0)
958 return QString::fromLocal8Bit(ba: u.nodename);
959 return QString();
960#else
961# ifdef Q_OS_WIN
962 // Important: QtNetwork depends on machineHostName() initializing ws2_32.dll
963 winsockInit();
964 QString hostName;
965 hostName.resize(512);
966 unsigned long len = hostName.size();
967 BOOL res = GetComputerNameEx(ComputerNameDnsHostname,
968 reinterpret_cast<wchar_t *>(const_cast<quint16 *>(hostName.utf16())), &len);
969 if (!res && len > 512) {
970 hostName.resize(len - 1);
971 GetComputerNameEx(ComputerNameDnsHostname,
972 reinterpret_cast<wchar_t *>(const_cast<quint16 *>(hostName.utf16())), &len);
973 }
974 hostName.truncate(len);
975 return hostName;
976# else // !Q_OS_WIN
977
978 char hostName[512];
979 if (gethostname(hostName, sizeof(hostName)) == -1)
980 return QString();
981 hostName[sizeof(hostName) - 1] = '\0';
982 return QString::fromLocal8Bit(hostName);
983# endif
984#endif
985}
986#endif // QT_BOOTSTRAPPED
987
988enum {
989 UuidStringLen = sizeof("00000000-0000-0000-0000-000000000000") - 1
990};
991
992/*!
993 \since 5.11
994
995 Returns a unique ID for this machine, if one can be determined. If no
996 unique ID could be determined, this function returns an empty byte array.
997 Unlike machineHostName(), the value returned by this function is likely
998 globally unique.
999
1000 A unique ID is useful in network operations to identify this machine for an
1001 extended period of time, when the IP address could change or if this
1002 machine could have more than one IP address. For example, the ID could be
1003 used when communicating with a server or when storing device-specific data
1004 in shared network storage.
1005
1006 Note that on some systems, this value will persist across reboots and on
1007 some it will not. Applications should not blindly depend on this fact
1008 without verifying the OS capabilities. In particular, on Linux systems,
1009 this ID is usually permanent and it matches the D-Bus machine ID, except
1010 for nodes without their own storage (replicated nodes).
1011
1012 \sa machineHostName(), bootUniqueId()
1013*/
1014QByteArray QSysInfo::machineUniqueId()
1015{
1016#if defined(Q_OS_DARWIN) && __has_include(<IOKit/IOKitLib.h>)
1017 char uuid[UuidStringLen + 1];
1018 io_service_t service = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
1019 QCFString stringRef = (CFStringRef)IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0);
1020 CFStringGetCString(stringRef, uuid, sizeof(uuid), kCFStringEncodingMacRoman);
1021 return QByteArray(uuid);
1022#elif defined(Q_OS_BSD4) && defined(KERN_HOSTUUID)
1023 char uuid[UuidStringLen + 1];
1024 size_t uuidlen = sizeof(uuid);
1025 int name[] = { CTL_KERN, KERN_HOSTUUID };
1026 if (sysctl(name, sizeof name / sizeof name[0], &uuid, &uuidlen, nullptr, 0) == 0
1027 && uuidlen == sizeof(uuid))
1028 return QByteArray(uuid, uuidlen - 1);
1029#elif defined(Q_OS_UNIX)
1030 // The modern name on Linux is /etc/machine-id, but that path is
1031 // unlikely to exist on non-Linux (non-systemd) systems. The old
1032 // path is more than enough.
1033 static const char fullfilename[] = "/usr/local/var/lib/dbus/machine-id";
1034 const char *firstfilename = fullfilename + sizeof("/usr/local") - 1;
1035 int fd = qt_safe_open(pathname: firstfilename, O_RDONLY);
1036 if (fd == -1 && errno == ENOENT)
1037 fd = qt_safe_open(pathname: fullfilename, O_RDONLY);
1038
1039 if (fd != -1) {
1040 char buffer[32]; // 128 bits, hex-encoded
1041 qint64 len = qt_safe_read(fd, data: buffer, maxlen: sizeof(buffer));
1042 qt_safe_close(fd);
1043
1044 if (len != -1)
1045 return QByteArray(buffer, len);
1046 }
1047#elif defined(Q_OS_WIN)
1048 // Let's poke at the registry
1049 const QString machineGuid = QWinRegistryKey(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Cryptography)")
1050 .stringValue(u"MachineGuid"_s);
1051 if (!machineGuid.isEmpty())
1052 return machineGuid.toLatin1();
1053#endif
1054 return QByteArray();
1055}
1056
1057/*!
1058 \since 5.11
1059
1060 Returns a unique ID for this machine's boot, if one can be determined. If
1061 no unique ID could be determined, this function returns an empty byte
1062 array. This value is expected to change after every boot and can be
1063 considered globally unique.
1064
1065 This function is currently only implemented for Linux and Apple operating
1066 systems.
1067
1068 \sa machineUniqueId()
1069*/
1070QByteArray QSysInfo::bootUniqueId()
1071{
1072#ifdef Q_OS_LINUX
1073 // use low-level API here for simplicity
1074 int fd = qt_safe_open(pathname: "/proc/sys/kernel/random/boot_id", O_RDONLY);
1075 if (fd != -1) {
1076 char uuid[UuidStringLen];
1077 qint64 len = qt_safe_read(fd, data: uuid, maxlen: sizeof(uuid));
1078 qt_safe_close(fd);
1079 if (len == UuidStringLen)
1080 return QByteArray(uuid, UuidStringLen);
1081 }
1082#elif defined(Q_OS_DARWIN)
1083 // "kern.bootsessionuuid" is only available by name
1084 char uuid[UuidStringLen + 1];
1085 size_t uuidlen = sizeof(uuid);
1086 if (sysctlbyname("kern.bootsessionuuid", uuid, &uuidlen, nullptr, 0) == 0
1087 && uuidlen == sizeof(uuid))
1088 return QByteArray(uuid, uuidlen - 1);
1089#endif
1090 return QByteArray();
1091};
1092
1093QT_END_NAMESPACE
1094

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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