Tizen 2.1 base
[platform/upstream/gcd.git] / kqueue-1.0.4 / src / linux / socket.c
1 /*
2  * Copyright (c) 2009 Mark Heily <mark@heily.com>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <linux/sockios.h>
20 #include <pthread.h>
21 #include <signal.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <sys/ioctl.h>
25 #include <sys/queue.h>
26 #include <sys/socket.h>
27 #include <sys/types.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include <sys/epoll.h>
32
33 #include "sys/event.h"
34 #include "private.h"
35
36
37 static char *
38 epoll_event_dump(struct epoll_event *evt)
39 {
40     static char __thread buf[128];
41
42     if (evt == NULL)
43         return "(null)";
44
45 #define EPEVT_DUMP(attrib) \
46     if (evt->events & attrib) \
47        strcat(&buf[0], #attrib" ");
48
49     snprintf(&buf[0], 128, " { data = %p, events = ", evt->data.ptr);
50     EPEVT_DUMP(EPOLLIN);
51     EPEVT_DUMP(EPOLLOUT);
52 #if defined(HAVE_EPOLLRDHUP)
53     EPEVT_DUMP(EPOLLRDHUP);
54 #endif
55     EPEVT_DUMP(EPOLLONESHOT);
56     EPEVT_DUMP(EPOLLET);
57     strcat(&buf[0], "}\n");
58
59     return (&buf[0]);
60 #undef EPEVT_DUMP
61 }
62
63 static int
64 epoll_update(int op, struct filter *filt, struct knote *kn, struct epoll_event *ev)
65 {
66     dbg_printf("op=%d fd=%d events=%s", op, (int)kn->kev.ident, 
67             epoll_event_dump(ev));
68     if (epoll_ctl(filt->kf_pfd, op, kn->kev.ident, ev) < 0) {
69         dbg_printf("epoll_ctl(2): %s", strerror(errno));
70         return (-1);
71     }
72
73     return (0);
74 }
75
76 static int 
77 socket_knote_delete(int epfd, int fd)
78 {
79     return epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
80 }
81
82 int
83 evfilt_socket_init(struct filter *filt)
84 {
85     filt->kf_pfd = epoll_create(1);
86     if (filt->kf_pfd < 0)
87         return (-1);
88
89     dbg_printf("socket epollfd = %d", filt->kf_pfd);
90     return (0);
91 }
92
93 void
94 evfilt_socket_destroy(struct filter *filt)
95 {
96     close(filt->kf_pfd);
97 }
98
99 int
100 evfilt_socket_copyout(struct filter *filt, 
101             struct kevent *dst, 
102             int nevents)
103 {
104     struct epoll_event epevt[MAX_KEVENT];
105     struct epoll_event *ev;
106     struct knote *kn;
107     int i, nret;
108
109     for (;;) {
110         nret = epoll_wait(filt->kf_pfd, &epevt[0], nevents, 0);
111         if (nret < 0) {
112             if (errno == EINTR)
113                 continue;
114             dbg_perror("epoll_wait");
115             return (-1);
116         } else {
117             break;
118         }
119     }
120
121     for (i = 0, nevents = 0; i < nret; i++) {
122         ev = &epevt[i];
123         epoll_event_dump(ev);
124         kn = knote_lookup(filt, ev->data.fd);
125         if (kn != NULL) {
126             memcpy(dst, &kn->kev, sizeof(*dst));
127 #if defined(HAVE_EPOLLRDHUP)
128             if (ev->events & EPOLLRDHUP || ev->events & EPOLLHUP)
129                 dst->flags |= EV_EOF;
130 #else
131             if (ev->events & EPOLLHUP)
132                 dst->flags |= EV_EOF;
133 #endif
134             if (ev->events & EPOLLERR)
135                 dst->fflags = 1; /* FIXME: Return the actual socket error */
136           
137             if (kn->flags & KNFL_PASSIVE_SOCKET) {
138                 /* On return, data contains the length of the 
139                    socket backlog. This is not available under Linux.
140                  */
141                 dst->data = 1;
142             } else {
143                 /* On return, data contains the number of bytes of protocol
144                    data available to read.
145                  */
146                 if (ioctl(dst->ident, 
147                             (dst->filter == EVFILT_READ) ? SIOCINQ : SIOCOUTQ, 
148                             &dst->data) < 0) {
149                     /* race condition with socket close, so ignore this error */
150                     dbg_puts("ioctl(2) of socket failed");
151                     dst->data = 0;
152                 }
153             }
154
155             if (kn->kev.flags & EV_DISPATCH) {
156                 socket_knote_delete(filt->kf_pfd, kn->kev.ident);
157                 KNOTE_DISABLE(kn);
158             } else if (kn->kev.flags & EV_ONESHOT) {
159                 socket_knote_delete(filt->kf_pfd, kn->kev.ident);
160                 knote_free(filt, kn);
161             }
162
163             nevents++;
164             dst++;
165         }
166     }
167
168     return (nevents);
169 }
170
171 int
172 evfilt_socket_knote_create(struct filter *filt, struct knote *kn)
173 {
174     struct epoll_event ev;
175
176     if (knote_get_socket_type(kn) < 0)
177         return (-1);
178
179     /* Convert the kevent into an epoll_event */
180     if (kn->kev.filter == EVFILT_READ)
181 #if defined(HAVE_EPOLLRDHUP)
182         kn->data.events = EPOLLIN | EPOLLRDHUP;
183 #else
184         kn->data.events = EPOLLIN;
185 #endif
186     else
187         kn->data.events = EPOLLOUT;
188     if (kn->kev.flags & EV_ONESHOT || kn->kev.flags & EV_DISPATCH)
189         kn->data.events |= EPOLLONESHOT;
190     if (kn->kev.flags & EV_CLEAR)
191         kn->data.events |= EPOLLET;
192
193     memset(&ev, 0, sizeof(ev));
194     ev.events = kn->data.events;
195     ev.data.fd = kn->kev.ident;
196
197     return epoll_update(EPOLL_CTL_ADD, filt, kn, &ev);
198 }
199
200 int
201 evfilt_socket_knote_modify(struct filter *filt, struct knote *kn, 
202         const struct kevent *kev)
203 {
204     return (-1); /* STUB */
205 }
206
207 int
208 evfilt_socket_knote_delete(struct filter *filt, struct knote *kn)
209 {
210     if (kn->kev.flags & EV_DISABLE)
211         return (0);
212     else
213         return epoll_update(EPOLL_CTL_DEL, filt, kn, NULL);
214 }
215
216 int
217 evfilt_socket_knote_enable(struct filter *filt, struct knote *kn)
218 {
219     struct epoll_event ev;
220
221     memset(&ev, 0, sizeof(ev));
222     ev.events = kn->data.events;
223     ev.data.fd = kn->kev.ident;
224
225     return epoll_update(EPOLL_CTL_ADD, filt, kn, &ev);
226 }
227
228 int
229 evfilt_socket_knote_disable(struct filter *filt, struct knote *kn)
230 {
231     return epoll_update(EPOLL_CTL_DEL, filt, kn, NULL);
232 }
233
234
235 const struct filter evfilt_read = {
236     EVFILT_READ,
237     evfilt_socket_init,
238     evfilt_socket_destroy,
239     evfilt_socket_copyout,
240     evfilt_socket_knote_create,
241     evfilt_socket_knote_modify,
242     evfilt_socket_knote_delete,
243     evfilt_socket_knote_enable,
244     evfilt_socket_knote_disable,         
245 };
246
247 const struct filter evfilt_write = {
248     EVFILT_WRITE,
249     evfilt_socket_init,
250     evfilt_socket_destroy,
251     evfilt_socket_copyout,
252     evfilt_socket_knote_create,
253     evfilt_socket_knote_modify,
254     evfilt_socket_knote_delete,
255     evfilt_socket_knote_enable,
256     evfilt_socket_knote_disable,         
257 };