Merge branch 'vendor/AWK'
[dragonfly.git] / usr.bin / doscmd / AsyncIO.c
1 /*
2  * Copyright (c) 1992, 1993, 1996
3  *      Berkeley Software Design, Inc.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Berkeley Software
16  *      Design, Inc.
17  *
18  * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  *      BSDI AsyncIO.c,v 2.2 1996/04/08 19:32:10 bostic Exp
31  *
32  * $FreeBSD: src/usr.bin/doscmd/AsyncIO.c,v 1.3.2.3 2002/05/21 11:49:47 tg Exp $
33  * $DragonFly: src/usr.bin/doscmd/AsyncIO.c,v 1.3 2008/10/16 01:52:32 swildner Exp $
34  */
35
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/time.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <limits.h>
42 #include <signal.h>
43 #include <stdio.h>
44 #include <unistd.h>
45
46 #include "doscmd.h"
47 #include "AsyncIO.h"
48
49 #define FD_ISZERO(p)    ((p)->fds_bits[0] == 0)
50
51 /*
52  * Set or Clear the Async nature of an FD
53  */
54
55 #define SETASYNC(fd)    fcntl(fd, F_SETFL, handlers[fd].flag | FASYNC)
56 #define CLRASYNC(fd)    fcntl(fd, F_SETFL, handlers[fd].flag & ~FASYNC)
57
58 /*
59  * Request that ``func'' be called everytime data is available on ``fd''
60  */
61
62 static  fd_set  fdset;          /* File Descriptors to select on */
63
64 typedef struct {
65         void    (*func)(int, int, void *, regcontext_t *);
66                                         /* Function to call on data arrival */
67         void    (*failure)(void *);     /* Function to call on failure */
68         void    *arg;                   /* Argument to above functions */
69         int     lockcnt;                /* Nested level of lock */
70         fd_set  members;                /* Set of FD's to disable on SIGIO */
71         int     flag;                   /* The flag from F_GETFL (we own it) */
72 } Async;
73
74 static Async    handlers[OPEN_MAX];
75
76 static void     CleanIO(void);
77 static void     HandleIO(struct sigframe *sf);
78
79 void
80 _RegisterIO(int fd, void (*func)(int, int, void *, regcontext_t *),
81     void *arg, void (*failure)(void *))
82 {
83         static int firsttime = 1;
84         Async *as;
85
86         if (fd < 0 || fd > OPEN_MAX) {
87 printf("%d: Invalid FD\n", fd);
88                 return;
89         }
90
91         as = &handlers[fd];
92
93         if ((as->flag = fcntl(fd, F_GETFL, 0)) == -1) {
94                 if (func) {
95 /*@*/                   perror("get fcntl");
96 /*@*/                   abort();
97                         return;
98                 }
99         }
100
101         if (firsttime) {
102                 firsttime = 0;
103                 setsignal(SIGIO, HandleIO);
104         }
105
106         if ((handlers[fd].func = func) != 0) {
107                 as->lockcnt = 0;
108                 as->arg = arg;
109                 as->failure = failure;
110
111                 FD_SET(fd, &fdset);
112                 FD_ZERO(&handlers[fd].members);
113                 FD_SET(fd, &handlers[fd].members);
114                 if (fcntl(fd, F_SETOWN, getpid()) < 0) {
115 /*@*/                   perror("SETOWN");
116                 }
117                 SETASYNC(fd);
118         } else {
119                 as->arg = 0;
120                 as->failure = 0;
121                 as->lockcnt = 0;
122
123                 CLRASYNC(fd);
124                 FD_CLR(fd, &fdset);
125         }
126 }
127
128 static void
129 CleanIO(void)
130 {
131         int x;
132         static struct timeval tv;
133
134         /*
135          * For every file des in fd_set, we check to see if it
136          * causes a fault on select().  If so, we unregister it
137          * for the user.
138          */
139         for (x = 0; x < OPEN_MAX; ++x) {
140                 fd_set set;
141
142                 if (!FD_ISSET(x, &fdset))
143                         continue;
144
145                 FD_ZERO(&set);
146                 FD_SET(x, &set);
147                 errno = 0;
148                 if (select(FD_SETSIZE, &set, 0, 0, &tv) < 0 &&
149                     errno == EBADF) {
150                         void (*f)(void *);
151                         void *a;
152 printf("Closed file descriptor %d\n", x);
153
154                         f = handlers[x].failure;
155                         a = handlers[x].arg;
156                         handlers[x].failure = 0;
157                         handlers[x].func = 0;
158                         handlers[x].arg = 0;
159                         handlers[x].lockcnt = 0;
160                         FD_CLR(x, &fdset);
161                         if (f)
162                                 (*f)(a);
163                 }
164         }
165 }
166
167 static void
168 HandleIO(struct sigframe *sf)
169 {
170         static struct timeval tv;
171         fd_set readset, writeset;
172         int x, fd;
173
174 again:
175         readset = writeset = fdset;
176         if ((x = select(FD_SETSIZE, &readset, &writeset, 0, &tv)) < 0) {
177                 /*
178                  * If we failed because of a BADFiledes, go find
179                  * which one(s), fail them out and then try a
180                  * new select to see if any of the good ones are
181                  * okay.
182                  */
183                 if (errno == EBADF) {
184                         CleanIO();
185                         if (FD_ISZERO(&fdset))
186                                 return;
187                         goto again;
188                 }
189                 perror("select");
190                 return;
191         }
192
193         /*
194          * If we run out of fds to look at, break out of the loop
195          * and exit the handler.
196          */
197         if (x == 0)
198                 return;
199
200         /*
201          * If there is at least 1 fd saying it has something for
202          * us, then loop through the sets looking for those
203          * bits, stopping when we have handleed the number it has
204          * asked for.
205          */
206         for (fd = 0; x && fd < OPEN_MAX; fd ++) {
207                 Async *as;
208                 int cond;                                    
209                 
210                 cond = 0;
211                 
212                 if (FD_ISSET(fd, &readset)) {
213                         cond |= AS_RD;
214                         x --;
215                 }
216                 if (FD_ISSET(fd, &writeset)) {
217                         cond |= AS_WR;
218                         x --;
219                 }
220
221                 if (cond == 0)
222                         continue;
223
224                 /*
225                  * Is suppose it is possible that one of the previous
226                  * I/O requests changed the fdset.
227                  * We do know that SIGIO is turned off right now,
228                  * so it is safe to checkit.
229                  */
230                 if (!FD_ISSET(fd, &fdset)) {
231                         continue;
232                 }
233                 as = &handlers[fd];
234                 
235                 /*
236                  * as in above, maybe someone locked us...
237                  * we are in dangerous water now if we are
238                  * multi-tasked
239                  */
240                 if (as->lockcnt) {
241                         fprintf(stderr, "Selected IO on locked %d\n",fd);
242                         continue;
243                 }
244                 /*
245                  * Okay, now if there exists a handler, we should
246                  * call it.  We must turn back on SIGIO if there
247                  * are possibly other people waiting for it.
248                  */
249                 if (as->func) {
250                         (*handlers[fd].func)(fd, cond, handlers[fd].arg, 
251                             (regcontext_t *)&sf->sf_uc.uc_mcontext);
252                 } else {
253                         /*
254                          * Otherwise deregister this guy.
255                          */
256                         _RegisterIO(fd, 0, 0, 0);
257                 }
258         }
259         /*
260          * If we did not process all the fd's, then we should
261          * break out of the probable infinite loop.
262          */
263 }