1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 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 <QtCore/private/qglobal_p.h>
6#include "qcore_unix_p.h"
7#include "qelapsedtimer.h"
8
9#include <stdlib.h>
10
11#ifdef __GLIBC__
12# include <sys/syscall.h>
13# include <pthread.h>
14# include <unistd.h>
15#endif
16
17#ifdef Q_OS_DARWIN
18#include <mach/mach_time.h>
19#endif
20
21QT_BEGIN_NAMESPACE
22
23QByteArray qt_readlink(const char *path)
24{
25#ifndef PATH_MAX
26 // suitably large value that won't consume too much memory
27# define PATH_MAX 1024*1024
28#endif
29
30 QByteArray buf(256, Qt::Uninitialized);
31
32 ssize_t len = ::readlink(path: path, buf: buf.data(), len: buf.size());
33 while (len == buf.size()) {
34 // readlink(2) will fill our buffer and not necessarily terminate with NUL;
35 if (buf.size() >= PATH_MAX) {
36 errno = ENAMETOOLONG;
37 return QByteArray();
38 }
39
40 // double the size and try again
41 buf.resize(size: buf.size() * 2);
42 len = ::readlink(path: path, buf: buf.data(), len: buf.size());
43 }
44
45 if (len == -1)
46 return QByteArray();
47
48 buf.resize(size: len);
49 return buf;
50}
51
52#if defined(Q_PROCESSOR_X86_32) && defined(__GLIBC__)
53# if !__GLIBC_PREREQ(2, 22)
54// glibc prior to release 2.22 had a bug that suppresses the third argument to
55// open() / open64() / openat(), causing file creation with O_TMPFILE to have
56// the wrong permissions. So we bypass the glibc implementation and go straight
57// for the syscall. See
58// https://sourceware.org/git/?p=glibc.git;a=commit;h=65f6f938cd562a614a68e15d0581a34b177ec29d
59int qt_open64(const char *pathname, int flags, mode_t mode)
60{
61 return syscall(SYS_open, pathname, flags | O_LARGEFILE, mode);
62}
63# endif
64#endif
65
66#ifndef QT_BOOTSTRAPPED
67
68static inline void do_gettime(qint64 *sec, qint64 *frac)
69{
70 timespec ts;
71 clockid_t clk = CLOCK_REALTIME;
72#if defined(CLOCK_MONOTONIC_RAW)
73 clk = CLOCK_MONOTONIC_RAW;
74#elif defined(CLOCK_MONOTONIC)
75 clk = CLOCK_MONOTONIC;
76#endif
77
78 clock_gettime(clock_id: clk, tp: &ts);
79 *sec = ts.tv_sec;
80 *frac = ts.tv_nsec;
81}
82
83// also used in qeventdispatcher_unix.cpp
84struct timespec qt_gettime() noexcept
85{
86 qint64 sec, frac;
87 do_gettime(sec: &sec, frac: &frac);
88
89 timespec tv;
90 tv.tv_sec = sec;
91 tv.tv_nsec = frac;
92
93 return tv;
94}
95
96#if QT_CONFIG(poll_pollts)
97# define ppoll pollts
98#endif
99
100static inline bool time_update(struct timespec *tv, const struct timespec &start,
101 const struct timespec &timeout)
102{
103 // clock source is (hopefully) monotonic, so we can recalculate how much timeout is left;
104 // if it isn't monotonic, we'll simply hope that it hasn't jumped, because we have no alternative
105 struct timespec now = qt_gettime();
106 *tv = timeout + start - now;
107 return tv->tv_sec >= 0;
108}
109
110#if QT_CONFIG(poll_poll)
111static inline int timespecToMillisecs(const struct timespec *ts)
112{
113 return (ts == NULL) ? -1 :
114 (ts->tv_sec * 1000) + (ts->tv_nsec / 1000000);
115}
116#endif
117
118// defined in qpoll.cpp
119int qt_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts);
120
121static inline int qt_ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts)
122{
123#if QT_CONFIG(poll_ppoll) || QT_CONFIG(poll_pollts)
124 return ::ppoll(fds: fds, nfds: nfds, timeout: timeout_ts, ss: nullptr);
125#elif QT_CONFIG(poll_poll)
126 return ::poll(fds, nfds, timespecToMillisecs(timeout_ts));
127#elif QT_CONFIG(poll_select)
128 return qt_poll(fds, nfds, timeout_ts);
129#else
130 // configure.json reports an error when everything is not available
131#endif
132}
133
134
135/*!
136 \internal
137
138 Behaves as close to POSIX poll(2) as practical but may be implemented
139 using select(2) where necessary. In that case, returns -1 and sets errno
140 to EINVAL if passed any descriptor greater than or equal to FD_SETSIZE.
141*/
142int qt_safe_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts)
143{
144 if (!timeout_ts) {
145 // no timeout -> block forever
146 int ret;
147 EINTR_LOOP(ret, qt_ppoll(fds, nfds, nullptr));
148 return ret;
149 }
150
151 timespec start = qt_gettime();
152 timespec timeout = *timeout_ts;
153
154 // loop and recalculate the timeout as needed
155 forever {
156 const int ret = qt_ppoll(fds, nfds, timeout_ts: &timeout);
157 if (ret != -1 || errno != EINTR)
158 return ret;
159
160 // recalculate the timeout
161 if (!time_update(tv: &timeout, start, timeout: *timeout_ts)) {
162 // timeout during update
163 // or clock reset, fake timeout error
164 return 0;
165 }
166 }
167}
168
169#endif // QT_BOOTSTRAPPED
170
171QT_END_NAMESPACE
172

source code of qtbase/src/corelib/kernel/qcore_unix.cpp