1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifdef WIN32
18/* POSIX defines 1024 for the FD_SETSIZE */
19#define FD_SETSIZE 1024
20#endif
21
22#include "apr.h"
23#include "apr_poll.h"
24#include "apr_time.h"
25#include "apr_portable.h"
26#include "apr_arch_file_io.h"
27#include "apr_arch_networkio.h"
28#include "apr_arch_poll_private.h"
29#include "apr_arch_inherit.h"
30
31static apr_pollset_method_e pollset_default_method = POLLSET_DEFAULT_METHOD;
32
33#if !APR_FILES_AS_SOCKETS
34#if defined (WIN32)
35
36/* Create a dummy wakeup socket pipe for interrupting the poller
37 */
38static apr_status_t create_wakeup_pipe(apr_pollset_t *pollset)
39{
40    apr_status_t rv;
41
42    if ((rv = apr_file_socket_pipe_create(&pollset->wakeup_pipe[0],
43                                          &pollset->wakeup_pipe[1],
44                                          pollset->pool)) != APR_SUCCESS)
45        return rv;
46
47    pollset->wakeup_pfd.p = pollset->pool;
48    pollset->wakeup_pfd.reqevents = APR_POLLIN;
49    pollset->wakeup_pfd.desc_type = APR_POLL_FILE;
50    pollset->wakeup_pfd.desc.f = pollset->wakeup_pipe[0];
51
52    return apr_pollset_add(pollset, &pollset->wakeup_pfd);
53}
54
55#else  /* !WIN32 */
56static apr_status_t create_wakeup_pipe(apr_pollset_t *pollset)
57{
58    return APR_ENOTIMPL;
59}
60
61static apr_status_t apr_file_socket_pipe_close(apr_file_t *file)
62{
63    return APR_ENOTIMPL;
64}
65
66#endif /* WIN32 */
67#else  /* APR_FILES_AS_SOCKETS */
68
69/* Create a dummy wakeup pipe for interrupting the poller
70 */
71static apr_status_t create_wakeup_pipe(apr_pollset_t *pollset)
72{
73    apr_status_t rv;
74
75    if ((rv = apr_file_pipe_create(&pollset->wakeup_pipe[0],
76                                   &pollset->wakeup_pipe[1],
77                                   pollset->pool)) != APR_SUCCESS)
78        return rv;
79
80    pollset->wakeup_pfd.p = pollset->pool;
81    pollset->wakeup_pfd.reqevents = APR_POLLIN;
82    pollset->wakeup_pfd.desc_type = APR_POLL_FILE;
83    pollset->wakeup_pfd.desc.f = pollset->wakeup_pipe[0];
84
85    {
86        int flags;
87
88        if ((flags = fcntl(pollset->wakeup_pipe[0]->filedes, F_GETFD)) == -1)
89            return errno;
90
91        flags |= FD_CLOEXEC;
92        if (fcntl(pollset->wakeup_pipe[0]->filedes, F_SETFD, flags) == -1)
93            return errno;
94    }
95    {
96        int flags;
97
98        if ((flags = fcntl(pollset->wakeup_pipe[1]->filedes, F_GETFD)) == -1)
99            return errno;
100
101        flags |= FD_CLOEXEC;
102        if (fcntl(pollset->wakeup_pipe[1]->filedes, F_SETFD, flags) == -1)
103            return errno;
104    }
105
106    return apr_pollset_add(pollset, &pollset->wakeup_pfd);
107}
108#endif /* !APR_FILES_AS_SOCKETS */
109
110/* Read and discard what's ever in the wakeup pipe.
111 */
112void apr_pollset_drain_wakeup_pipe(apr_pollset_t *pollset)
113{
114    char rb[512];
115    apr_size_t nr = sizeof(rb);
116
117    while (apr_file_read(pollset->wakeup_pipe[0], rb, &nr) == APR_SUCCESS) {
118        /* Although we write just one byte to the other end of the pipe
119         * during wakeup, multiple threads could call the wakeup.
120         * So simply drain out from the input side of the pipe all
121         * the data.
122         */
123        if (nr != sizeof(rb))
124            break;
125    }
126}
127
128static apr_status_t pollset_cleanup(void *p)
129{
130    apr_pollset_t *pollset = (apr_pollset_t *) p;
131    if (pollset->provider->cleanup) {
132        (*pollset->provider->cleanup)(pollset);
133    }
134    if (pollset->flags & APR_POLLSET_WAKEABLE) {
135        /* Close both sides of the wakeup pipe */
136        if (pollset->wakeup_pipe[0]) {
137#if APR_FILES_AS_SOCKETS
138            apr_file_close(pollset->wakeup_pipe[0]);
139#else
140            apr_file_socket_pipe_close(pollset->wakeup_pipe[0]);
141#endif
142            pollset->wakeup_pipe[0] = NULL;
143        }
144        if (pollset->wakeup_pipe[1]) {
145#if APR_FILES_AS_SOCKETS
146            apr_file_close(pollset->wakeup_pipe[1]);
147#else
148            apr_file_socket_pipe_close(pollset->wakeup_pipe[1]);
149#endif
150            pollset->wakeup_pipe[1] = NULL;
151        }
152    }
153
154    return APR_SUCCESS;
155}
156
157#if defined(HAVE_KQUEUE)
158extern apr_pollset_provider_t *apr_pollset_provider_kqueue;
159#endif
160#if defined(HAVE_PORT_CREATE)
161extern apr_pollset_provider_t *apr_pollset_provider_port;
162#endif
163#if defined(HAVE_EPOLL)
164extern apr_pollset_provider_t *apr_pollset_provider_epoll;
165#endif
166#if defined(HAVE_POLL)
167extern apr_pollset_provider_t *apr_pollset_provider_poll;
168#endif
169extern apr_pollset_provider_t *apr_pollset_provider_select;
170
171static apr_pollset_provider_t *pollset_provider(apr_pollset_method_e method)
172{
173    apr_pollset_provider_t *provider = NULL;
174    switch (method) {
175        case APR_POLLSET_KQUEUE:
176#if defined(HAVE_KQUEUE)
177            provider = apr_pollset_provider_kqueue;
178#endif
179        break;
180        case APR_POLLSET_PORT:
181#if defined(HAVE_PORT_CREATE)
182            provider = apr_pollset_provider_port;
183#endif
184        break;
185        case APR_POLLSET_EPOLL:
186#if defined(HAVE_EPOLL)
187            provider = apr_pollset_provider_epoll;
188#endif
189        break;
190        case APR_POLLSET_POLL:
191#if defined(HAVE_POLL)
192            provider = apr_pollset_provider_poll;
193#endif
194        break;
195        case APR_POLLSET_SELECT:
196            provider = apr_pollset_provider_select;
197        break;
198        case APR_POLLSET_DEFAULT:
199        break;
200    }
201    return provider;
202}
203
204APR_DECLARE(apr_status_t) apr_pollset_create_ex(apr_pollset_t **ret_pollset,
205                                                apr_uint32_t size,
206                                                apr_pool_t *p,
207                                                apr_uint32_t flags,
208                                                apr_pollset_method_e method)
209{
210    apr_status_t rv;
211    apr_pollset_t *pollset;
212    apr_pollset_provider_t *provider = NULL;
213
214    *ret_pollset = NULL;
215
216 #ifdef WIN32
217    /* Favor WSAPoll if supported.
218     * This will work only if ws2_32.dll has WSAPoll funtion.
219     * In other cases it will fall back to select() method unless
220     * the APR_POLLSET_NODEFAULT is added to the flags.
221     */
222    if (method == APR_POLLSET_DEFAULT) {
223        method = APR_POLLSET_POLL;
224    }
225 #endif
226
227    if (method == APR_POLLSET_DEFAULT)
228        method = pollset_default_method;
229    while (provider == NULL) {
230        provider = pollset_provider(method);
231        if (!provider) {
232            if ((flags & APR_POLLSET_NODEFAULT) == APR_POLLSET_NODEFAULT)
233                return APR_ENOTIMPL;
234            if (method == pollset_default_method)
235                return APR_ENOTIMPL;
236            method = pollset_default_method;
237        }
238    }
239    if (flags & APR_POLLSET_WAKEABLE) {
240        /* Add room for wakeup descriptor */
241        size++;
242    }
243
244    pollset = apr_palloc(p, sizeof(*pollset));
245    pollset->nelts = 0;
246    pollset->nalloc = size;
247    pollset->pool = p;
248    pollset->flags = flags;
249    pollset->provider = provider;
250
251    rv = (*provider->create)(pollset, size, p, flags);
252    if (rv == APR_ENOTIMPL) {
253        if (method == pollset_default_method) {
254            return rv;
255        }
256        provider = pollset_provider(pollset_default_method);
257        if (!provider) {
258            return APR_ENOTIMPL;
259        }
260        rv = (*provider->create)(pollset, size, p, flags);
261        if (rv != APR_SUCCESS) {
262            return rv;
263        }
264        pollset->provider = provider;
265    }
266    else if (rv != APR_SUCCESS) {
267        return rv;
268    }
269    if (flags & APR_POLLSET_WAKEABLE) {
270        /* Create wakeup pipe */
271        if ((rv = create_wakeup_pipe(pollset)) != APR_SUCCESS) {
272            return rv;
273        }
274    }
275    if ((flags & APR_POLLSET_WAKEABLE) || provider->cleanup)
276        apr_pool_cleanup_register(p, pollset, pollset_cleanup,
277                                  apr_pool_cleanup_null);
278
279    *ret_pollset = pollset;
280    return APR_SUCCESS;
281}
282
283APR_DECLARE(const char *) apr_pollset_method_name(apr_pollset_t *pollset)
284{
285    return pollset->provider->name;
286}
287
288APR_DECLARE(const char *) apr_poll_method_defname()
289{
290    apr_pollset_provider_t *provider = NULL;
291
292    provider = pollset_provider(pollset_default_method);
293    if (provider)
294        return provider->name;
295    else
296        return "unknown";
297}
298
299APR_DECLARE(apr_status_t) apr_pollset_create(apr_pollset_t **pollset,
300                                             apr_uint32_t size,
301                                             apr_pool_t *p,
302                                             apr_uint32_t flags)
303{
304    apr_pollset_method_e method = APR_POLLSET_DEFAULT;
305    return apr_pollset_create_ex(pollset, size, p, flags, method);
306}
307
308APR_DECLARE(apr_status_t) apr_pollset_destroy(apr_pollset_t * pollset)
309{
310    if (pollset->flags & APR_POLLSET_WAKEABLE ||
311        pollset->provider->cleanup)
312        return apr_pool_cleanup_run(pollset->pool, pollset,
313                                    pollset_cleanup);
314    else
315        return APR_SUCCESS;
316}
317
318APR_DECLARE(apr_status_t) apr_pollset_wakeup(apr_pollset_t *pollset)
319{
320    if (pollset->flags & APR_POLLSET_WAKEABLE)
321        return apr_file_putc(1, pollset->wakeup_pipe[1]);
322    else
323        return APR_EINIT;
324}
325
326APR_DECLARE(apr_status_t) apr_pollset_add(apr_pollset_t *pollset,
327                                          const apr_pollfd_t *descriptor)
328{
329    return (*pollset->provider->add)(pollset, descriptor);
330}
331
332APR_DECLARE(apr_status_t) apr_pollset_remove(apr_pollset_t *pollset,
333                                             const apr_pollfd_t *descriptor)
334{
335    return (*pollset->provider->remove)(pollset, descriptor);
336}
337
338APR_DECLARE(apr_status_t) apr_pollset_poll(apr_pollset_t *pollset,
339                                           apr_interval_time_t timeout,
340                                           apr_int32_t *num,
341                                           const apr_pollfd_t **descriptors)
342{
343    return (*pollset->provider->poll)(pollset, timeout, num, descriptors);
344}
345