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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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