kernel: Use hashdestroy() to free hash tables allocated with hashinit().
[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  */
34
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/time.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <signal.h>
42 #include <stdio.h>
43 #include <unistd.h>
44
45 #include "doscmd.h"
46 #include "AsyncIO.h"
47
48 #define FD_ISZERO(p)    ((p)->fds_bits[0] == 0)
49
50 /*
51  * Set or Clear the Async nature of an FD
52  */
53
54 #define SETASYNC(fd)    fcntl(fd, F_SETFL, handlers[fd].flag | FASYNC)
55 #define CLRASYNC(fd)    fcntl(fd, F_SETFL, handlers[fd].flag & ~FASYNC)
56
57 /*
58  * Request that ``func'' be called everytime data is available on ``fd''
59  */
60
61 static  fd_set  fdset;          /* File Descriptors to select on */
62
63 typedef struct {
64         void    (*func)(int, int, void *, regcontext_t *);
65                                         /* Function to call on data arrival */
66         void    (*failure)(void *);     /* Function to call on failure */
67         void    *arg;                   /* Argument to above functions */
68         int     lockcnt;                /* Nested level of lock */
69         fd_set  members;                /* Set of FD's to disable on SIGIO */
70         int     flag;                   /* The flag from F_GETFL (we own it) */
71 } Async;
72
73 static Async    handlers[OPEN_MAX];
74
75 static void     CleanIO(void);
76 static void     HandleIO(struct sigframe *sf);
77
78 void
79 _RegisterIO(int fd, void (*func)(int, int, void *, regcontext_t *),
80     void *arg, void (*failure)(void *))
81 {
82         static int firsttime = 1;
83         Async *as;
84
85         if (fd < 0 || fd > OPEN_MAX) {
86 printf("%d: Invalid FD\n", fd);
87                 return;
88         }
89
90         as = &handlers[fd];
91
92         if ((as->flag = fcntl(fd, F_GETFL, 0)) == -1) {
93                 if (func) {
94 /*@*/                   perror("get fcntl");
95 /*@*/                   abort();
96                         return;
97                 }
98         }
99
100         if (firsttime) {
101                 firsttime = 0;
102                 setsignal(SIGIO, HandleIO);
103         }
104
105         if ((handlers[fd].func = func) != NULL) {
106                 as->lockcnt = 0;
107                 as->arg = arg;
108                 as->failure = failure;
109
110                 FD_SET(fd, &fdset);
111                 FD_ZERO(&handlers[fd].members);
112                 FD_SET(fd, &handlers[fd].members);
113                 if (fcntl(fd, F_SETOWN, getpid()) < 0) {
114 /*@*/                   perror("SETOWN");
115                 }
116                 SETASYNC(fd);
117         } else {
118                 as->arg = NULL;
119                 as->failure = NULL;
120                 as->lockcnt = 0;
121
122                 CLRASYNC(fd);
123                 FD_CLR(fd, &fdset);
124         }
125 }
126
127 static void
128 CleanIO(void)
129 {
130         int x;
131         static struct timeval tv;
132
133         /*
134          * For every file des in fd_set, we check to see if it
135          * causes a fault on select().  If so, we unregister it
136          * for the user.
137          */
138         for (x = 0; x < OPEN_MAX; ++x) {
139                 fd_set set;
140
141                 if (!FD_ISSET(x, &fdset))
142                         continue;
143
144                 FD_ZERO(&set);
145                 FD_SET(x, &set);
146                 errno = 0;
147                 if (select(FD_SETSIZE, &set, 0, 0, &tv) < 0 &&
148                     errno == EBADF) {
149                         void (*f)(void *);
150                         void *a;
151 printf("Closed file descriptor %d\n", x);
152
153                         f = handlers[x].failure;
154                         a = handlers[x].arg;
155                         handlers[x].failure = NULL;
156                         handlers[x].func = NULL;
157                         handlers[x].arg = NULL;
158                         handlers[x].lockcnt = 0;
159                         FD_CLR(x, &fdset);
160                         if (f)
161                                 (*f)(a);
162                 }
163         }
164 }
165
166 static void
167 HandleIO(struct sigframe *sf)
168 {
169         static struct timeval tv;
170         fd_set readset, writeset;
171         int x, fd;
172
173 again:
174         readset = writeset = fdset;
175         if ((x = select(FD_SETSIZE, &readset, &writeset, 0, &tv)) < 0) {
176                 /*
177                  * If we failed because of a BADFiledes, go find
178                  * which one(s), fail them out and then try a
179                  * new select to see if any of the good ones are
180                  * okay.
181                  */
182                 if (errno == EBADF) {
183                         CleanIO();
184                         if (FD_ISZERO(&fdset))
185                                 return;
186                         goto again;
187                 }
188                 perror("select");
189                 return;
190         }
191
192         /*
193          * If we run out of fds to look at, break out of the loop
194          * and exit the handler.
195          */
196         if (x == 0)
197                 return;
198
199         /*
200          * If there is at least 1 fd saying it has something for
201          * us, then loop through the sets looking for those
202          * bits, stopping when we have handleed the number it has
203          * asked for.
204          */
205         for (fd = 0; x && fd < OPEN_MAX; fd ++) {
206                 Async *as;
207                 int cond;                                    
208                 
209                 cond = 0;
210                 
211                 if (FD_ISSET(fd, &readset)) {
212                         cond |= AS_RD;
213                         x --;
214                 }
215                 if (FD_ISSET(fd, &writeset)) {
216                         cond |= AS_WR;
217                         x --;
218                 }
219
220                 if (cond == 0)
221                         continue;
222
223                 /*
224                  * Is suppose it is possible that one of the previous
225                  * I/O requests changed the fdset.
226                  * We do know that SIGIO is turned off right now,
227                  * so it is safe to checkit.
228                  */
229                 if (!FD_ISSET(fd, &fdset)) {
230                         continue;
231                 }
232                 as = &handlers[fd];
233                 
234                 /*
235                  * as in above, maybe someone locked us...
236                  * we are in dangerous water now if we are
237                  * multi-tasked
238                  */
239                 if (as->lockcnt) {
240                         fprintf(stderr, "Selected IO on locked %d\n",fd);
241                         continue;
242                 }
243                 /*
244                  * Okay, now if there exists a handler, we should
245                  * call it.  We must turn back on SIGIO if there
246                  * are possibly other people waiting for it.
247                  */
248                 if (as->func) {
249                         (*handlers[fd].func)(fd, cond, handlers[fd].arg, 
250                             (regcontext_t *)&sf->sf_uc.uc_mcontext);
251                 } else {
252                         /*
253                          * Otherwise deregister this guy.
254                          */
255                         _RegisterIO(fd, 0, 0, 0);
256                 }
257         }
258         /*
259          * If we did not process all the fd's, then we should
260          * break out of the probable infinite loop.
261          */
262 }