1/* sigio.c -- Support for SIGIO handler installation and removal
2 * Created: Thu Jun  3 15:39:18 1999 by faith@precisioninsight.com
3 *
4 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the next
15 * paragraph) shall be included in all copies or substantial portions of the
16 * Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
21 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
22 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 *
26 * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
27 */
28/*
29 * Copyright (c) 2002 by The XFree86 Project, Inc.
30 *
31 * Permission is hereby granted, free of charge, to any person obtaining a
32 * copy of this software and associated documentation files (the "Software"),
33 * to deal in the Software without restriction, including without limitation
34 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
35 * and/or sell copies of the Software, and to permit persons to whom the
36 * Software is furnished to do so, subject to the following conditions:
37 *
38 * The above copyright notice and this permission notice shall be included in
39 * all copies or substantial portions of the Software.
40 *
41 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
44 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
45 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
46 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
47 * OTHER DEALINGS IN THE SOFTWARE.
48 *
49 * Except as contained in this notice, the name of the copyright holder(s)
50 * and author(s) shall not be used in advertising or otherwise to promote
51 * the sale, use or other dealings in this Software without prior written
52 * authorization from the copyright holder(s) and author(s).
53 */
54
55#ifdef HAVE_XORG_CONFIG_H
56#include <xorg-config.h>
57#endif
58
59#include <X11/X.h>
60#include <xserver_poll.h>
61#include "xf86.h"
62#include "xf86Priv.h"
63#include "xf86_OSlib.h"
64#include "inputstr.h"
65
66#ifdef HAVE_STROPTS_H
67#include <stropts.h>
68#endif
69
70#ifdef MAXDEVICES
71/* MAXDEVICES represents the maximum number of input devices usable
72 * at the same time plus one entry for DRM support.
73 */
74#define MAX_FUNCS   (MAXDEVICES + 1)
75#else
76#define MAX_FUNCS 16
77#endif
78
79typedef struct _xf86SigIOFunc {
80    void (*f) (int, void *);
81    int fd;
82    void *closure;
83} Xf86SigIOFunc;
84
85static Xf86SigIOFunc xf86SigIOFuncs[MAX_FUNCS];
86static int xf86SigIOMax;
87static struct pollfd *xf86SigIOFds;
88static int xf86SigIONum;
89
90static Bool
91xf86SigIOAdd(int fd)
92{
93    struct pollfd *n;
94
95    n = realloc(xf86SigIOFds, (xf86SigIONum + 1) * sizeof (struct pollfd));
96    if (!n)
97        return FALSE;
98
99    n[xf86SigIONum].fd = fd;
100    n[xf86SigIONum].events = POLLIN;
101    xf86SigIONum++;
102    xf86SigIOFds = n;
103    return TRUE;
104}
105
106static void
107xf86SigIORemove(int fd)
108{
109    int i;
110    for (i = 0; i < xf86SigIONum; i++)
111        if (xf86SigIOFds[i].fd == fd) {
112            memmove(&xf86SigIOFds[i], &xf86SigIOFds[i+1], (xf86SigIONum - i - 1) * sizeof (struct pollfd));
113            xf86SigIONum--;
114            break;
115        }
116}
117
118/*
119 * SIGIO gives no way of discovering which fd signalled, select
120 * to discover
121 */
122static void
123xf86SIGIO(int sig)
124{
125    int i, f;
126    int save_errno = errno;     /* do not clobber the global errno */
127    int r;
128
129    inSignalContext = TRUE;
130
131    SYSCALL(r = xserver_poll(xf86SigIOFds, xf86SigIONum, 0));
132    for (f = 0; r > 0 && f < xf86SigIONum; f++) {
133        if (xf86SigIOFds[f].revents & POLLIN) {
134            for (i = 0; i < xf86SigIOMax; i++)
135                if (xf86SigIOFuncs[i].f && xf86SigIOFuncs[i].fd == xf86SigIOFds[f].fd)
136                    (*xf86SigIOFuncs[i].f) (xf86SigIOFuncs[i].fd,
137                                            xf86SigIOFuncs[i].closure);
138            r--;
139        }
140    }
141    if (r > 0) {
142        xf86Msg(X_ERROR, "SIGIO %d descriptors not handled\n", r);
143    }
144    /* restore global errno */
145    errno = save_errno;
146
147    inSignalContext = FALSE;
148}
149
150static int
151xf86IsPipe(int fd)
152{
153    struct stat buf;
154
155    if (fstat(fd, &buf) < 0)
156        return 0;
157    return S_ISFIFO(buf.st_mode);
158}
159
160static void
161block_sigio(void)
162{
163    sigset_t set;
164
165    sigemptyset(&set);
166    sigaddset(&set, SIGIO);
167    xthread_sigmask(SIG_BLOCK, &set, NULL);
168}
169
170static void
171release_sigio(void)
172{
173    sigset_t set;
174
175    sigemptyset(&set);
176    sigaddset(&set, SIGIO);
177    xthread_sigmask(SIG_UNBLOCK, &set, NULL);
178}
179
180int
181xf86InstallSIGIOHandler(int fd, void (*f) (int, void *), void *closure)
182{
183    struct sigaction sa;
184    struct sigaction osa;
185    int i;
186    int installed = FALSE;
187
188    for (i = 0; i < MAX_FUNCS; i++) {
189        if (!xf86SigIOFuncs[i].f) {
190            if (xf86IsPipe(fd))
191                return 0;
192            block_sigio();
193#ifdef O_ASYNC
194            if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_ASYNC) == -1) {
195                xf86Msg(X_WARNING, "fcntl(%d, O_ASYNC): %s\n",
196                        fd, strerror(errno));
197            }
198            else {
199                if (fcntl(fd, F_SETOWN, getpid()) == -1) {
200                    xf86Msg(X_WARNING, "fcntl(%d, F_SETOWN): %s\n",
201                            fd, strerror(errno));
202                }
203                else {
204                    installed = TRUE;
205                }
206            }
207#endif
208#if defined(I_SETSIG) && defined(HAVE_ISASTREAM)
209            /* System V Streams - used on Solaris for input devices */
210            if (!installed && isastream(fd)) {
211                if (ioctl(fd, I_SETSIG, S_INPUT | S_ERROR | S_HANGUP) == -1) {
212                    xf86Msg(X_WARNING, "fcntl(%d, I_SETSIG): %s\n",
213                            fd, strerror(errno));
214                }
215                else {
216                    installed = TRUE;
217                }
218            }
219#endif
220            if (!installed) {
221                release_sigio();
222                return 0;
223            }
224            sigemptyset(&sa.sa_mask);
225            sigaddset(&sa.sa_mask, SIGIO);
226            sa.sa_flags = SA_RESTART;
227            sa.sa_handler = xf86SIGIO;
228            sigaction(SIGIO, &sa, &osa);
229            xf86SigIOFuncs[i].fd = fd;
230            xf86SigIOFuncs[i].closure = closure;
231            xf86SigIOFuncs[i].f = f;
232            if (i >= xf86SigIOMax)
233                xf86SigIOMax = i + 1;
234            xf86SigIOAdd(fd);
235            release_sigio();
236            return 1;
237        }
238        /* Allow overwriting of the closure and callback */
239        else if (xf86SigIOFuncs[i].fd == fd) {
240            xf86SigIOFuncs[i].closure = closure;
241            xf86SigIOFuncs[i].f = f;
242            return 1;
243        }
244    }
245    return 0;
246}
247
248int
249xf86RemoveSIGIOHandler(int fd)
250{
251    struct sigaction sa;
252    struct sigaction osa;
253    int i;
254    int max;
255    int ret;
256
257    max = 0;
258    ret = 0;
259    for (i = 0; i < MAX_FUNCS; i++) {
260        if (xf86SigIOFuncs[i].f) {
261            if (xf86SigIOFuncs[i].fd == fd) {
262                xf86SigIOFuncs[i].f = 0;
263                xf86SigIOFuncs[i].fd = 0;
264                xf86SigIOFuncs[i].closure = 0;
265                xf86SigIORemove(fd);
266                ret = 1;
267            }
268            else {
269                max = i + 1;
270            }
271        }
272    }
273    if (ret) {
274#ifdef O_ASYNC
275        fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_ASYNC);
276#endif
277#if defined(I_SETSIG) && defined(HAVE_ISASTREAM)
278        if (isastream(fd)) {
279            if (ioctl(fd, I_SETSIG, 0) == -1) {
280                xf86Msg(X_WARNING, "fcntl(%d, I_SETSIG, 0): %s\n",
281                        fd, strerror(errno));
282            }
283        }
284#endif
285        xf86SigIOMax = max;
286        if (!max) {
287            sigemptyset(&sa.sa_mask);
288            sigaddset(&sa.sa_mask, SIGIO);
289            sa.sa_flags = 0;
290            sa.sa_handler = SIG_IGN;
291            sigaction(SIGIO, &sa, &osa);
292        }
293    }
294    return ret;
295}
296