1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Copyright (C) 2016 Intel Corporation. |
5 | ** Contact: https://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the QtCore module of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:LGPL$ |
10 | ** Commercial License Usage |
11 | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | ** accordance with the commercial license agreement provided with the |
13 | ** Software or, alternatively, in accordance with the terms contained in |
14 | ** a written agreement between you and The Qt Company. For licensing terms |
15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at https://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU Lesser General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
22 | ** packaging of this file. Please review the following information to |
23 | ** ensure the GNU Lesser General Public License version 3 requirements |
24 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
25 | ** |
26 | ** GNU General Public License Usage |
27 | ** Alternatively, this file may be used under the terms of the GNU |
28 | ** General Public License version 2.0 or (at your option) the GNU General |
29 | ** Public license version 3 or any later version approved by the KDE Free |
30 | ** Qt Foundation. The licenses are as published by the Free Software |
31 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
32 | ** included in the packaging of this file. Please review the following |
33 | ** information to ensure the GNU General Public License requirements will |
34 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
35 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
36 | ** |
37 | ** $QT_END_LICENSE$ |
38 | ** |
39 | ****************************************************************************/ |
40 | |
41 | #include <QtCore/private/qglobal_p.h> |
42 | #include "qcore_unix_p.h" |
43 | #include "qelapsedtimer.h" |
44 | |
45 | #include <stdlib.h> |
46 | |
47 | #ifdef __GLIBC__ |
48 | # include <sys/syscall.h> |
49 | # include <pthread.h> |
50 | # include <unistd.h> |
51 | #endif |
52 | |
53 | #ifdef Q_OS_MAC |
54 | #include <mach/mach_time.h> |
55 | #endif |
56 | |
57 | QT_BEGIN_NAMESPACE |
58 | |
59 | QByteArray qt_readlink(const char *path) |
60 | { |
61 | #ifndef PATH_MAX |
62 | // suitably large value that won't consume too much memory |
63 | # define PATH_MAX 1024*1024 |
64 | #endif |
65 | |
66 | QByteArray buf(256, Qt::Uninitialized); |
67 | |
68 | ssize_t len = ::readlink(path: path, buf: buf.data(), len: buf.size()); |
69 | while (len == buf.size()) { |
70 | // readlink(2) will fill our buffer and not necessarily terminate with NUL; |
71 | if (buf.size() >= PATH_MAX) { |
72 | errno = ENAMETOOLONG; |
73 | return QByteArray(); |
74 | } |
75 | |
76 | // double the size and try again |
77 | buf.resize(size: buf.size() * 2); |
78 | len = ::readlink(path: path, buf: buf.data(), len: buf.size()); |
79 | } |
80 | |
81 | if (len == -1) |
82 | return QByteArray(); |
83 | |
84 | buf.resize(size: len); |
85 | return buf; |
86 | } |
87 | |
88 | #if defined(Q_PROCESSOR_X86_32) && defined(__GLIBC__) |
89 | # if !__GLIBC_PREREQ(2, 22) |
90 | // glibc prior to release 2.22 had a bug that suppresses the third argument to |
91 | // open() / open64() / openat(), causing file creation with O_TMPFILE to have |
92 | // the wrong permissions. So we bypass the glibc implementation and go straight |
93 | // for the syscall. See |
94 | // https://sourceware.org/git/?p=glibc.git;a=commit;h=65f6f938cd562a614a68e15d0581a34b177ec29d |
95 | int qt_open64(const char *pathname, int flags, mode_t mode) |
96 | { |
97 | return syscall(SYS_open, pathname, flags | O_LARGEFILE, mode); |
98 | } |
99 | # endif |
100 | #endif |
101 | |
102 | #ifndef QT_BOOTSTRAPPED |
103 | |
104 | #if QT_CONFIG(poll_pollts) |
105 | # define ppoll pollts |
106 | #endif |
107 | |
108 | static inline bool time_update(struct timespec *tv, const struct timespec &start, |
109 | const struct timespec &timeout) |
110 | { |
111 | // clock source is (hopefully) monotonic, so we can recalculate how much timeout is left; |
112 | // if it isn't monotonic, we'll simply hope that it hasn't jumped, because we have no alternative |
113 | struct timespec now = qt_gettime(); |
114 | *tv = timeout + start - now; |
115 | return tv->tv_sec >= 0; |
116 | } |
117 | |
118 | #if QT_CONFIG(poll_poll) |
119 | static inline int timespecToMillisecs(const struct timespec *ts) |
120 | { |
121 | return (ts == NULL) ? -1 : |
122 | (ts->tv_sec * 1000) + (ts->tv_nsec / 1000000); |
123 | } |
124 | #endif |
125 | |
126 | // defined in qpoll.cpp |
127 | int qt_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts); |
128 | |
129 | static inline int qt_ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts) |
130 | { |
131 | #if QT_CONFIG(poll_ppoll) || QT_CONFIG(poll_pollts) |
132 | return ::ppoll(fds: fds, nfds: nfds, timeout: timeout_ts, ss: nullptr); |
133 | #elif QT_CONFIG(poll_poll) |
134 | return ::poll(fds, nfds, timespecToMillisecs(timeout_ts)); |
135 | #elif QT_CONFIG(poll_select) |
136 | return qt_poll(fds, nfds, timeout_ts); |
137 | #else |
138 | // configure.json reports an error when everything is not available |
139 | #endif |
140 | } |
141 | |
142 | |
143 | /*! |
144 | \internal |
145 | |
146 | Behaves as close to POSIX poll(2) as practical but may be implemented |
147 | using select(2) where necessary. In that case, returns -1 and sets errno |
148 | to EINVAL if passed any descriptor greater than or equal to FD_SETSIZE. |
149 | */ |
150 | int qt_safe_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts) |
151 | { |
152 | if (!timeout_ts) { |
153 | // no timeout -> block forever |
154 | int ret; |
155 | EINTR_LOOP(ret, qt_ppoll(fds, nfds, nullptr)); |
156 | return ret; |
157 | } |
158 | |
159 | timespec start = qt_gettime(); |
160 | timespec timeout = *timeout_ts; |
161 | |
162 | // loop and recalculate the timeout as needed |
163 | forever { |
164 | const int ret = qt_ppoll(fds, nfds, timeout_ts: &timeout); |
165 | if (ret != -1 || errno != EINTR) |
166 | return ret; |
167 | |
168 | // recalculate the timeout |
169 | if (!time_update(tv: &timeout, start, timeout: *timeout_ts)) { |
170 | // timeout during update |
171 | // or clock reset, fake timeout error |
172 | return 0; |
173 | } |
174 | } |
175 | } |
176 | |
177 | #endif // QT_BOOTSTRAPPED |
178 | |
179 | QT_END_NAMESPACE |
180 | |