1/*
2 * Copyright © 2016 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#ifdef HAVE_DIX_CONFIG_H
24#include <dix-config.h>
25#endif
26
27#include <X11/X.h>
28#include <X11/Xproto.h>
29#include <stdlib.h>
30#include <unistd.h>
31#include "misc.h"               /* for typedef of pointer */
32#include "ospoll.h"
33#include "list.h"
34
35#if !HAVE_OSPOLL && defined(HAVE_POLLSET_CREATE)
36#include <sys/pollset.h>
37#define POLLSET         1
38#define HAVE_OSPOLL     1
39#endif
40
41#if !HAVE_OSPOLL && defined(HAVE_PORT_CREATE)
42#include <port.h>
43#include <poll.h>
44#define PORT            1
45#define HAVE_OSPOLL     1
46#endif
47
48#if !HAVE_OSPOLL && defined(HAVE_EPOLL_CREATE1)
49#include <sys/epoll.h>
50#define EPOLL           1
51#define HAVE_OSPOLL     1
52#endif
53
54#if !HAVE_OSPOLL
55#include "xserver_poll.h"
56#define POLL            1
57#define HAVE_OSPOLL     1
58#endif
59
60#if POLLSET
61
62// pollset-based implementation (as seen on AIX)
63struct ospollfd {
64    int                 fd;
65    int                 xevents;
66    short               revents;
67    enum ospoll_trigger trigger;
68    void                (*callback)(int fd, int xevents, void *data);
69    void                *data;
70};
71
72struct ospoll {
73    pollset_t           ps;
74    struct ospollfd     *fds;
75    int                 num;
76    int                 size;
77};
78
79#endif
80
81#if EPOLL || PORT
82
83/* epoll-based implementation */
84struct ospollfd {
85    int                 fd;
86    int                 xevents;
87    enum ospoll_trigger trigger;
88    void                (*callback)(int fd, int xevents, void *data);
89    void                *data;
90    struct xorg_list    deleted;
91};
92
93struct ospoll {
94    int                 epoll_fd;
95    struct ospollfd     **fds;
96    int                 num;
97    int                 size;
98    struct xorg_list    deleted;
99};
100
101#endif
102
103#if POLL
104
105/* poll-based implementation */
106struct ospollfd {
107    short               revents;
108    enum ospoll_trigger trigger;
109    void                (*callback)(int fd, int revents, void *data);
110    void                *data;
111};
112
113struct ospoll {
114    struct pollfd       *fds;
115    struct ospollfd     *osfds;
116    int                 num;
117    int                 size;
118    Bool                changed;
119};
120
121#endif
122
123/* Binary search for the specified file descriptor
124 *
125 * Returns position if found
126 * Returns -position - 1 if not found
127 */
128
129static int
130ospoll_find(struct ospoll *ospoll, int fd)
131{
132    int lo = 0;
133    int hi = ospoll->num - 1;
134
135    while (lo <= hi) {
136        int m = (lo + hi) >> 1;
137#if EPOLL || PORT
138        int t = ospoll->fds[m]->fd;
139#endif
140#if POLL || POLLSET
141        int t = ospoll->fds[m].fd;
142#endif
143
144        if (t < fd)
145            lo = m + 1;
146        else if (t > fd)
147            hi = m - 1;
148        else
149            return m;
150    }
151    return -(lo + 1);
152}
153
154#if EPOLL || PORT
155static void
156ospoll_clean_deleted(struct ospoll *ospoll)
157{
158    struct ospollfd     *osfd, *tmp;
159
160    xorg_list_for_each_entry_safe(osfd, tmp, &ospoll->deleted, deleted) {
161        xorg_list_del(&osfd->deleted);
162        free(osfd);
163    }
164}
165#endif
166
167/* Insert an element into an array
168 *
169 * base: base address of array
170 * num:  number of elements in the array before the insert
171 * size: size of each element
172 * pos:  position to insert at
173 */
174static inline void
175array_insert(void *base, size_t num, size_t size, size_t pos)
176{
177    char *b = base;
178
179    memmove(b + (pos+1) * size,
180            b + pos * size,
181            (num - pos) * size);
182}
183
184/* Delete an element from an array
185 *
186 * base: base address of array
187 * num:  number of elements in the array before the delete
188 * size: size of each element
189 * pos:  position to delete from
190 */
191static inline void
192array_delete(void *base, size_t num, size_t size, size_t pos)
193{
194    char *b = base;
195
196    memmove(b + pos * size, b + (pos + 1) * size,
197            (num - pos - 1) * size);
198}
199
200
201struct ospoll *
202ospoll_create(void)
203{
204#if POLLSET
205    struct ospoll *ospoll = calloc(1, sizeof (struct ospoll));
206
207    ospoll->ps = pollset_create(-1);
208    if (ospoll->ps < 0) {
209        free (ospoll);
210        return NULL;
211    }
212    return ospoll;
213#endif
214#if PORT
215    struct ospoll *ospoll = calloc(1, sizeof (struct ospoll));
216
217    ospoll->epoll_fd = port_create();
218    if (ospoll->epoll_fd < 0) {
219        free (ospoll);
220        return NULL;
221    }
222    xorg_list_init(&ospoll->deleted);
223    return ospoll;
224#endif
225#if EPOLL
226    struct ospoll       *ospoll = calloc(1, sizeof (struct ospoll));
227
228    ospoll->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
229    if (ospoll->epoll_fd < 0) {
230        free (ospoll);
231        return NULL;
232    }
233    xorg_list_init(&ospoll->deleted);
234    return ospoll;
235#endif
236#if POLL
237    return calloc(1, sizeof (struct ospoll));
238#endif
239}
240
241void
242ospoll_destroy(struct ospoll *ospoll)
243{
244#if POLLSET
245    if (ospoll) {
246        assert (ospoll->num == 0);
247        pollset_destroy(ospoll->ps);
248        free(ospoll->fds);
249        free(ospoll);
250    }
251#endif
252#if EPOLL || PORT
253    if (ospoll) {
254        assert (ospoll->num == 0);
255        close(ospoll->epoll_fd);
256        ospoll_clean_deleted(ospoll);
257        free(ospoll->fds);
258        free(ospoll);
259    }
260#endif
261#if POLL
262    if (ospoll) {
263        assert (ospoll->num == 0);
264        free (ospoll->fds);
265        free (ospoll->osfds);
266        free (ospoll);
267    }
268#endif
269}
270
271Bool
272ospoll_add(struct ospoll *ospoll, int fd,
273           enum ospoll_trigger trigger,
274           void (*callback)(int fd, int xevents, void *data),
275           void *data)
276{
277    int pos = ospoll_find(ospoll, fd);
278#if POLLSET
279    if (pos < 0) {
280        if (ospoll->num == ospoll->size) {
281            struct ospollfd *new_fds;
282            int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2;
283
284            new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0]));
285            if (!new_fds)
286                return FALSE;
287            ospoll->fds = new_fds;
288            ospoll->size = new_size;
289        }
290        pos = -pos - 1;
291        array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
292        ospoll->num++;
293
294        ospoll->fds[pos].fd = fd;
295        ospoll->fds[pos].xevents = 0;
296        ospoll->fds[pos].revents = 0;
297    }
298    ospoll->fds[pos].trigger = trigger;
299    ospoll->fds[pos].callback = callback;
300    ospoll->fds[pos].data = data;
301#endif
302#if PORT
303    struct ospollfd *osfd;
304
305    if (pos < 0) {
306        osfd = calloc(1, sizeof (struct ospollfd));
307        if (!osfd)
308            return FALSE;
309
310        if (ospoll->num >= ospoll->size) {
311            struct ospollfd **new_fds;
312            int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2;
313
314            new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0]));
315            if (!new_fds) {
316                free (osfd);
317                return FALSE;
318            }
319            ospoll->fds = new_fds;
320            ospoll->size = new_size;
321        }
322
323        osfd->fd = fd;
324        osfd->xevents = 0;
325
326        pos = -pos - 1;
327        array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
328        ospoll->fds[pos] = osfd;
329        ospoll->num++;
330    } else {
331        osfd = ospoll->fds[pos];
332    }
333    osfd->data = data;
334    osfd->callback = callback;
335    osfd->trigger = trigger;
336#endif
337#if EPOLL
338    struct ospollfd *osfd;
339
340    if (pos < 0) {
341
342        struct epoll_event ev;
343
344        osfd = calloc(1, sizeof (struct ospollfd));
345        if (!osfd)
346            return FALSE;
347
348        if (ospoll->num >= ospoll->size) {
349            struct ospollfd **new_fds;
350            int new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2;
351
352            new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0]));
353            if (!new_fds) {
354                free (osfd);
355                return FALSE;
356            }
357            ospoll->fds = new_fds;
358            ospoll->size = new_size;
359        }
360
361        ev.events = 0;
362        ev.data.ptr = osfd;
363        if (trigger == ospoll_trigger_edge)
364            ev.events |= EPOLLET;
365        if (epoll_ctl(ospoll->epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
366            free(osfd);
367            return FALSE;
368        }
369        osfd->fd = fd;
370        osfd->xevents = 0;
371
372        pos = -pos - 1;
373        array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
374        ospoll->fds[pos] = osfd;
375        ospoll->num++;
376    } else {
377        osfd = ospoll->fds[pos];
378    }
379    osfd->data = data;
380    osfd->callback = callback;
381    osfd->trigger = trigger;
382#endif
383#if POLL
384    if (pos < 0) {
385        if (ospoll->num == ospoll->size) {
386            struct pollfd   *new_fds;
387            struct ospollfd *new_osfds;
388            int             new_size = ospoll->size ? ospoll->size * 2 : MAXCLIENTS * 2;
389
390            new_fds = reallocarray(ospoll->fds, new_size, sizeof (ospoll->fds[0]));
391            if (!new_fds)
392                return FALSE;
393            ospoll->fds = new_fds;
394            new_osfds = reallocarray(ospoll->osfds, new_size, sizeof (ospoll->osfds[0]));
395            if (!new_osfds)
396                return FALSE;
397            ospoll->osfds = new_osfds;
398            ospoll->size = new_size;
399        }
400        pos = -pos - 1;
401        array_insert(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
402        array_insert(ospoll->osfds, ospoll->num, sizeof (ospoll->osfds[0]), pos);
403        ospoll->num++;
404        ospoll->changed = TRUE;
405
406        ospoll->fds[pos].fd = fd;
407        ospoll->fds[pos].events = 0;
408        ospoll->fds[pos].revents = 0;
409        ospoll->osfds[pos].revents = 0;
410    }
411    ospoll->osfds[pos].trigger = trigger;
412    ospoll->osfds[pos].callback = callback;
413    ospoll->osfds[pos].data = data;
414#endif
415    return TRUE;
416}
417
418void
419ospoll_remove(struct ospoll *ospoll, int fd)
420{
421    int pos = ospoll_find(ospoll, fd);
422
423    pos = ospoll_find(ospoll, fd);
424    if (pos >= 0) {
425#if POLLSET
426        struct ospollfd *osfd = &ospoll->fds[pos];
427        struct poll_ctl ctl = { .cmd = PS_DELETE, .fd = fd };
428        pollset_ctl(ospoll->ps, &ctl, 1);
429
430        array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
431        ospoll->num--;
432#endif
433#if PORT
434        struct ospollfd *osfd = ospoll->fds[pos];
435        port_dissociate(ospoll->epoll_fd, PORT_SOURCE_FD, fd);
436
437        array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
438        ospoll->num--;
439        osfd->callback = NULL;
440        osfd->data = NULL;
441        xorg_list_add(&osfd->deleted, &ospoll->deleted);
442#endif
443#if EPOLL
444        struct ospollfd *osfd = ospoll->fds[pos];
445        struct epoll_event ev;
446        ev.events = 0;
447        ev.data.ptr = osfd;
448        (void) epoll_ctl(ospoll->epoll_fd, EPOLL_CTL_DEL, fd, &ev);
449
450        array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
451        ospoll->num--;
452        osfd->callback = NULL;
453        osfd->data = NULL;
454        xorg_list_add(&osfd->deleted, &ospoll->deleted);
455#endif
456#if POLL
457        array_delete(ospoll->fds, ospoll->num, sizeof (ospoll->fds[0]), pos);
458        array_delete(ospoll->osfds, ospoll->num, sizeof (ospoll->osfds[0]), pos);
459        ospoll->num--;
460        ospoll->changed = TRUE;
461#endif
462    }
463}
464
465#if PORT
466static void
467epoll_mod(struct ospoll *ospoll, struct ospollfd *osfd)
468{
469    int events = 0;
470    if (osfd->xevents & X_NOTIFY_READ)
471        events |= POLLIN;
472    if (osfd->xevents & X_NOTIFY_WRITE)
473        events |= POLLOUT;
474    port_associate(ospoll->epoll_fd, PORT_SOURCE_FD, osfd->fd, events, osfd);
475}
476#endif
477
478#if EPOLL
479static void
480epoll_mod(struct ospoll *ospoll, struct ospollfd *osfd)
481{
482    struct epoll_event ev;
483    ev.events = 0;
484    if (osfd->xevents & X_NOTIFY_READ)
485        ev.events |= EPOLLIN;
486    if (osfd->xevents & X_NOTIFY_WRITE)
487        ev.events |= EPOLLOUT;
488    if (osfd->trigger == ospoll_trigger_edge)
489        ev.events |= EPOLLET;
490    ev.data.ptr = osfd;
491    (void) epoll_ctl(ospoll->epoll_fd, EPOLL_CTL_MOD, osfd->fd, &ev);
492}
493#endif
494
495void
496ospoll_listen(struct ospoll *ospoll, int fd, int xevents)
497{
498    int pos = ospoll_find(ospoll, fd);
499
500    if (pos >= 0) {
501#if POLLSET
502        struct poll_ctl ctl = { .cmd = PS_MOD, .fd = fd };
503        if (xevents & X_NOTIFY_READ) {
504            ctl.events |= POLLIN;
505            ospoll->fds[pos].revents &= ~POLLIN;
506        }
507        if (xevents & X_NOTIFY_WRITE) {
508            ctl.events |= POLLOUT;
509            ospoll->fds[pos].revents &= ~POLLOUT;
510        }
511        pollset_ctl(ospoll->ps, &ctl, 1);
512        ospoll->fds[pos].xevents |= xevents;
513#endif
514#if EPOLL || PORT
515        struct ospollfd *osfd = ospoll->fds[pos];
516        osfd->xevents |= xevents;
517        epoll_mod(ospoll, osfd);
518#endif
519#if POLL
520        if (xevents & X_NOTIFY_READ) {
521            ospoll->fds[pos].events |= POLLIN;
522            ospoll->osfds[pos].revents &= ~POLLIN;
523        }
524        if (xevents & X_NOTIFY_WRITE) {
525            ospoll->fds[pos].events |= POLLOUT;
526            ospoll->osfds[pos].revents &= ~POLLOUT;
527        }
528#endif
529    }
530}
531
532void
533ospoll_mute(struct ospoll *ospoll, int fd, int xevents)
534{
535    int pos = ospoll_find(ospoll, fd);
536
537    if (pos >= 0) {
538#if POLLSET
539        struct ospollfd *osfd = &ospoll->fds[pos];
540        osfd->xevents &= ~xevents;
541        struct poll_ctl ctl = { .cmd = PS_DELETE, .fd = fd };
542        pollset_ctl(ospoll->ps, &ctl, 1);
543        if (osfd->xevents) {
544            ctl.cmd = PS_ADD;
545            if (osfd->xevents & X_NOTIFY_READ) {
546                ctl.events |= POLLIN;
547            }
548            if (osfd->xevents & X_NOTIFY_WRITE) {
549                ctl.events |= POLLOUT;
550            }
551            pollset_ctl(ospoll->ps, &ctl, 1);
552        }
553#endif
554#if EPOLL || PORT
555        struct ospollfd *osfd = ospoll->fds[pos];
556        osfd->xevents &= ~xevents;
557        epoll_mod(ospoll, osfd);
558#endif
559#if POLL
560        if (xevents & X_NOTIFY_READ)
561            ospoll->fds[pos].events &= ~POLLIN;
562        if (xevents & X_NOTIFY_WRITE)
563            ospoll->fds[pos].events &= ~POLLOUT;
564#endif
565    }
566}
567
568
569int
570ospoll_wait(struct ospoll *ospoll, int timeout)
571{
572    int nready;
573#if POLLSET
574#define MAX_EVENTS      256
575    struct pollfd events[MAX_EVENTS];
576
577    nready = pollset_poll(ospoll->ps, events, MAX_EVENTS, timeout);
578    for (int i = 0; i < nready; i++) {
579        struct pollfd *ev = &events[i];
580        int pos = ospoll_find(ospoll, ev->fd);
581        struct ospollfd *osfd = &ospoll->fds[pos];
582        short revents = ev->revents;
583        short oldevents = osfd->revents;
584
585        osfd->revents = (revents & (POLLIN|POLLOUT));
586        if (osfd->trigger == ospoll_trigger_edge)
587            revents &= ~oldevents;
588        if (revents) {
589            int xevents = 0;
590            if (revents & POLLIN)
591                xevents |= X_NOTIFY_READ;
592            if (revents & POLLOUT)
593                xevents |= X_NOTIFY_WRITE;
594            if (revents & (~(POLLIN|POLLOUT)))
595                xevents |= X_NOTIFY_ERROR;
596            osfd->callback(osfd->fd, xevents, osfd->data);
597        }
598    }
599#endif
600#if PORT
601#define MAX_EVENTS      256
602    port_event_t events[MAX_EVENTS];
603    uint_t nget = 1;
604    timespec_t port_timeout = {
605        .tv_sec = timeout / 1000,
606        .tv_nsec = (timeout % 1000) * 1000000
607    };
608
609    nready = 0;
610    if (port_getn(ospoll->epoll_fd, events, MAX_EVENTS, &nget, &port_timeout)
611        == 0) {
612        nready = nget;
613    }
614    for (int i = 0; i < nready; i++) {
615        port_event_t *ev = &events[i];
616        struct ospollfd *osfd = ev->portev_user;
617        uint32_t revents = ev->portev_events;
618        int xevents = 0;
619
620        if (revents & POLLIN)
621            xevents |= X_NOTIFY_READ;
622        if (revents & POLLOUT)
623            xevents |= X_NOTIFY_WRITE;
624        if (revents & (~(POLLIN|POLLOUT)))
625            xevents |= X_NOTIFY_ERROR;
626
627        if (osfd->callback)
628            osfd->callback(osfd->fd, xevents, osfd->data);
629
630        if (osfd->trigger == ospoll_trigger_level &&
631            !xorg_list_is_empty(&osfd->deleted)) {
632            epoll_mod(ospoll, osfd);
633        }
634    }
635    ospoll_clean_deleted(ospoll);
636#endif
637#if EPOLL
638#define MAX_EVENTS      256
639    struct epoll_event events[MAX_EVENTS];
640    int i;
641
642    nready = epoll_wait(ospoll->epoll_fd, events, MAX_EVENTS, timeout);
643    for (i = 0; i < nready; i++) {
644        struct epoll_event *ev = &events[i];
645        struct ospollfd *osfd = ev->data.ptr;
646        uint32_t revents = ev->events;
647        int xevents = 0;
648
649        if (revents & EPOLLIN)
650            xevents |= X_NOTIFY_READ;
651        if (revents & EPOLLOUT)
652            xevents |= X_NOTIFY_WRITE;
653        if (revents & (~(EPOLLIN|EPOLLOUT)))
654            xevents |= X_NOTIFY_ERROR;
655
656        if (osfd->callback)
657            osfd->callback(osfd->fd, xevents, osfd->data);
658    }
659    ospoll_clean_deleted(ospoll);
660#endif
661#if POLL
662    nready = xserver_poll(ospoll->fds, ospoll->num, timeout);
663    ospoll->changed = FALSE;
664    if (nready > 0) {
665        int f;
666        for (f = 0; f < ospoll->num; f++) {
667            short revents = ospoll->fds[f].revents;
668            short oldevents = ospoll->osfds[f].revents;
669
670            ospoll->osfds[f].revents = (revents & (POLLIN|POLLOUT));
671            if (ospoll->osfds[f].trigger == ospoll_trigger_edge)
672                revents &= ~oldevents;
673            if (revents) {
674                int    xevents = 0;
675                if (revents & POLLIN)
676                    xevents |= X_NOTIFY_READ;
677                if (revents & POLLOUT)
678                    xevents |= X_NOTIFY_WRITE;
679                if (revents & (~(POLLIN|POLLOUT)))
680                    xevents |= X_NOTIFY_ERROR;
681                ospoll->osfds[f].callback(ospoll->fds[f].fd, xevents,
682                                          ospoll->osfds[f].data);
683
684                /* Check to see if the arrays have changed, and just go back
685                 * around again
686                 */
687                if (ospoll->changed)
688                    break;
689            }
690        }
691    }
692#endif
693    return nready;
694}
695
696void
697ospoll_reset_events(struct ospoll *ospoll, int fd)
698{
699#if POLLSET
700    int pos = ospoll_find(ospoll, fd);
701
702    if (pos < 0)
703        return;
704
705    ospoll->fds[pos].revents = 0;
706#endif
707#if PORT
708    int pos = ospoll_find(ospoll, fd);
709
710    if (pos < 0)
711        return;
712
713    epoll_mod(ospoll, ospoll->fds[pos]);
714#endif
715#if POLL
716    int pos = ospoll_find(ospoll, fd);
717
718    if (pos < 0)
719        return;
720
721    ospoll->osfds[pos].revents = 0;
722#endif
723}
724
725void *
726ospoll_data(struct ospoll *ospoll, int fd)
727{
728    int pos = ospoll_find(ospoll, fd);
729
730    if (pos < 0)
731        return NULL;
732#if POLLSET
733    return ospoll->fds[pos].data;
734#endif
735#if EPOLL || PORT
736    return ospoll->fds[pos]->data;
737#endif
738#if POLL
739    return ospoll->osfds[pos].data;
740#endif
741}
742