1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qcore_unix_p.h"
41 
42 #ifdef Q_OS_RTEMS
43 #include <rtems/rtems_bsdnet_internal.h>
44 #endif
45 
46 QT_BEGIN_NAMESPACE
47 
48 #define QT_POLL_READ_MASK   (POLLIN | POLLRDNORM)
49 #define QT_POLL_WRITE_MASK  (POLLOUT | POLLWRNORM | POLLWRBAND)
50 #define QT_POLL_EXCEPT_MASK (POLLPRI | POLLRDBAND)
51 #define QT_POLL_ERROR_MASK  (POLLERR | POLLNVAL)
52 #define QT_POLL_EVENTS_MASK (QT_POLL_READ_MASK | QT_POLL_WRITE_MASK | QT_POLL_EXCEPT_MASK)
53 
qt_poll_prepare(struct pollfd * fds,nfds_t nfds,fd_set * read_fds,fd_set * write_fds,fd_set * except_fds)54 static inline int qt_poll_prepare(struct pollfd *fds, nfds_t nfds,
55                                   fd_set *read_fds, fd_set *write_fds, fd_set *except_fds)
56 {
57     int max_fd = -1;
58 
59     FD_ZERO(read_fds);
60     FD_ZERO(write_fds);
61     FD_ZERO(except_fds);
62 
63     for (nfds_t i = 0; i < nfds; i++) {
64         if (fds[i].fd >= FD_SETSIZE) {
65             errno = EINVAL;
66             return -1;
67         }
68 
69         if ((fds[i].fd < 0) || (fds[i].revents & QT_POLL_ERROR_MASK))
70             continue;
71 
72         if (fds[i].events & QT_POLL_READ_MASK)
73             FD_SET(fds[i].fd, read_fds);
74 
75         if (fds[i].events & QT_POLL_WRITE_MASK)
76             FD_SET(fds[i].fd, write_fds);
77 
78         if (fds[i].events & QT_POLL_EXCEPT_MASK)
79             FD_SET(fds[i].fd, except_fds);
80 
81         if (fds[i].events & QT_POLL_EVENTS_MASK)
82             max_fd = qMax(max_fd, fds[i].fd);
83     }
84 
85     return max_fd + 1;
86 }
87 
qt_poll_examine_ready_read(struct pollfd & pfd)88 static inline void qt_poll_examine_ready_read(struct pollfd &pfd)
89 {
90     int res;
91     char data;
92 
93     EINTR_LOOP(res, ::recv(pfd.fd, &data, sizeof(data), MSG_PEEK));
94     const int error = (res < 0) ? errno : 0;
95 
96     if (res == 0) {
97         pfd.revents |= POLLHUP;
98     } else if (res > 0 || error == ENOTSOCK || error == ENOTCONN) {
99         pfd.revents |= QT_POLL_READ_MASK & pfd.events;
100     } else {
101         switch (error) {
102         case ESHUTDOWN:
103         case ECONNRESET:
104         case ECONNABORTED:
105         case ENETRESET:
106             pfd.revents |= POLLHUP;
107             break;
108         default:
109             pfd.revents |= POLLERR;
110             break;
111         }
112     }
113 }
114 
qt_poll_sweep(struct pollfd * fds,nfds_t nfds,fd_set * read_fds,fd_set * write_fds,fd_set * except_fds)115 static inline int qt_poll_sweep(struct pollfd *fds, nfds_t nfds,
116                                 fd_set *read_fds, fd_set *write_fds, fd_set *except_fds)
117 {
118     int result = 0;
119 
120     for (nfds_t i = 0; i < nfds; i++) {
121         if (fds[i].fd < 0)
122             continue;
123 
124         if (FD_ISSET(fds[i].fd, read_fds))
125             qt_poll_examine_ready_read(fds[i]);
126 
127         if (FD_ISSET(fds[i].fd, write_fds))
128             fds[i].revents |= QT_POLL_WRITE_MASK & fds[i].events;
129 
130         if (FD_ISSET(fds[i].fd, except_fds))
131             fds[i].revents |= QT_POLL_EXCEPT_MASK & fds[i].events;
132 
133         if (fds[i].revents != 0)
134             result++;
135     }
136 
137     return result;
138 }
139 
qt_poll_is_bad_fd(int fd)140 static inline bool qt_poll_is_bad_fd(int fd)
141 {
142 #ifdef Q_OS_RTEMS
143     if (!rtems_bsdnet_fdToSocket(fd))
144         return true;
145 #endif
146 
147     int ret;
148     EINTR_LOOP(ret, fcntl(fd, F_GETFD));
149     return (ret == -1 && errno == EBADF);
150 }
151 
qt_poll_mark_bad_fds(struct pollfd * fds,const nfds_t nfds)152 static inline int qt_poll_mark_bad_fds(struct pollfd *fds, const nfds_t nfds)
153 {
154     int n_marked = 0;
155 
156     for (nfds_t i = 0; i < nfds; i++) {
157         if (fds[i].fd < 0)
158             continue;
159 
160         if (fds[i].revents & QT_POLL_ERROR_MASK)
161             continue;
162 
163         if (qt_poll_is_bad_fd(fds[i].fd)) {
164             fds[i].revents |= POLLNVAL;
165             n_marked++;
166         }
167    }
168 
169    return n_marked;
170 }
171 
qt_poll(struct pollfd * fds,nfds_t nfds,const struct timespec * timeout_ts)172 int qt_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts)
173 {
174     if (!fds && nfds) {
175         errno = EFAULT;
176         return -1;
177     }
178 
179     fd_set read_fds, write_fds, except_fds;
180     struct timeval tv, *ptv = 0;
181 
182     if (timeout_ts) {
183         tv = timespecToTimeval(*timeout_ts);
184         ptv = &tv;
185     }
186 
187     int n_bad_fds = 0;
188 
189     for (nfds_t i = 0; i < nfds; i++) {
190         fds[i].revents = 0;
191 
192         if (fds[i].fd < 0)
193             continue;
194 
195         if (fds[i].events & QT_POLL_EVENTS_MASK)
196             continue;
197 
198         if (qt_poll_is_bad_fd(fds[i].fd)) {
199             // Mark bad file descriptors that have no event flags set
200             // here, as we won't be passing them to select below and therefore
201             // need to do the check ourselves
202             fds[i].revents = POLLNVAL;
203             n_bad_fds++;
204         }
205     }
206 
207     forever {
208         const int max_fd = qt_poll_prepare(fds, nfds, &read_fds, &write_fds, &except_fds);
209 
210         if (max_fd < 0)
211             return max_fd;
212 
213         if (n_bad_fds > 0) {
214             tv.tv_sec = 0;
215             tv.tv_usec = 0;
216             ptv = &tv;
217         }
218 
219         const int ret = ::select(max_fd, &read_fds, &write_fds, &except_fds, ptv);
220 
221         if (ret == 0)
222             return n_bad_fds;
223 
224         if (ret > 0)
225             return qt_poll_sweep(fds, nfds, &read_fds, &write_fds, &except_fds);
226 
227         if (errno != EBADF)
228             return -1;
229 
230         // We have at least one bad file descriptor that we waited on, find out which and try again
231         n_bad_fds += qt_poll_mark_bad_fds(fds, nfds);
232     }
233 }
234 
235 QT_END_NAMESPACE
236