1 #include "fdevent.h"
2 #include "buffer.h"
3 #include "log.h"
4
5 #include <sys/types.h>
6
7 #include <unistd.h>
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <errno.h>
12 #include <signal.h>
13 #include <fcntl.h>
14
15 #ifdef USE_FREEBSD_KQUEUE
16 # include <sys/event.h>
17 # include <sys/time.h>
18
fdevent_freebsd_kqueue_free(fdevents * ev)19 static void fdevent_freebsd_kqueue_free(fdevents *ev) {
20 close(ev->kq_fd);
21 free(ev->kq_results);
22 }
23
fdevent_freebsd_kqueue_event_del(fdevents * ev,int fde_ndx,int fd)24 static int fdevent_freebsd_kqueue_event_del(fdevents *ev, int fde_ndx, int fd) {
25 int ret, n = 0;
26 struct kevent kev[2];
27 struct timespec ts;
28 int oevents;
29
30 if (fde_ndx < 0) return -1;
31
32 oevents = ev->fdarray[fd]->events;
33
34 if (oevents & FDEVENT_IN) {
35 EV_SET(&kev[n], fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
36 n++;
37 }
38 if (oevents & FDEVENT_OUT) {
39 EV_SET(&kev[n], fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
40 n++;
41 }
42
43 if (0 == n) return -1;
44
45 ts.tv_sec = 0;
46 ts.tv_nsec = 0;
47
48 ret = kevent(ev->kq_fd,
49 &kev, n,
50 NULL, 0,
51 &ts);
52
53 if (ret == -1) {
54 log_error_write(ev->srv, __FILE__, __LINE__, "SS",
55 "kqueue event delete failed: ", strerror(errno));
56
57 return -1;
58 }
59
60 return -1;
61 }
62
fdevent_freebsd_kqueue_event_set(fdevents * ev,int fde_ndx,int fd,int events)63 static int fdevent_freebsd_kqueue_event_set(fdevents *ev, int fde_ndx, int fd, int events) {
64 int ret, n = 0;
65 struct kevent kev[2];
66 struct timespec ts;
67 int oevents = ev->fdarray[fd]->events;
68 int addevents = events & ~oevents;
69 int delevents = ~events & oevents;
70
71 UNUSED(fde_ndx);
72
73 if (events == oevents) return fd;
74
75 if (addevents & FDEVENT_IN) {
76 EV_SET(&kev[n], fd, EVFILT_READ, EV_ADD|EV_CLEAR, 0, 0, NULL);
77 n++;
78 } else if (delevents & FDEVENT_IN) {
79 EV_SET(&kev[n], fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
80 n++;
81 }
82 if (addevents & FDEVENT_OUT) {
83 EV_SET(&kev[n], fd, EVFILT_WRITE, EV_ADD|EV_CLEAR, 0, 0, NULL);
84 n++;
85 } else if (delevents & FDEVENT_OUT) {
86 EV_SET(&kev[n], fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
87 n++;
88 }
89
90 if (0 == n) return fd;
91
92 ts.tv_sec = 0;
93 ts.tv_nsec = 0;
94
95 ret = kevent(ev->kq_fd,
96 kev, n,
97 NULL, 0,
98 &ts);
99
100 if (ret == -1) {
101 log_error_write(ev->srv, __FILE__, __LINE__, "SS",
102 "kqueue event set failed: ", strerror(errno));
103
104 return -1;
105 }
106
107 return fd;
108 }
109
fdevent_freebsd_kqueue_poll(fdevents * ev,int timeout_ms)110 static int fdevent_freebsd_kqueue_poll(fdevents *ev, int timeout_ms) {
111 int ret;
112 struct timespec ts;
113
114 ts.tv_sec = timeout_ms / 1000;
115 ts.tv_nsec = (timeout_ms % 1000) * 1000000;
116
117 ret = kevent(ev->kq_fd,
118 NULL, 0,
119 ev->kq_results, ev->maxfds,
120 &ts);
121
122 if (ret == -1) {
123 switch(errno) {
124 case EINTR:
125 /* we got interrupted, perhaps just a SIGCHLD of a CGI script */
126 return 0;
127 default:
128 log_error_write(ev->srv, __FILE__, __LINE__, "SS",
129 "kqueue failed polling: ", strerror(errno));
130 break;
131 }
132 }
133
134 return ret;
135 }
136
fdevent_freebsd_kqueue_event_get_revent(fdevents * ev,size_t ndx)137 static int fdevent_freebsd_kqueue_event_get_revent(fdevents *ev, size_t ndx) {
138 int events = 0, e;
139
140 e = ev->kq_results[ndx].filter;
141
142 if (e == EVFILT_READ) {
143 events |= FDEVENT_IN;
144 } else if (e == EVFILT_WRITE) {
145 events |= FDEVENT_OUT;
146 }
147
148 e = ev->kq_results[ndx].flags;
149
150 if (e & EV_EOF) {
151 events |= FDEVENT_HUP;
152 }
153
154 if (e & EV_ERROR) {
155 events |= FDEVENT_ERR;
156 }
157
158 return events;
159 }
160
fdevent_freebsd_kqueue_event_get_fd(fdevents * ev,size_t ndx)161 static int fdevent_freebsd_kqueue_event_get_fd(fdevents *ev, size_t ndx) {
162 return ev->kq_results[ndx].ident;
163 }
164
fdevent_freebsd_kqueue_event_next_fdndx(fdevents * ev,int ndx)165 static int fdevent_freebsd_kqueue_event_next_fdndx(fdevents *ev, int ndx) {
166 UNUSED(ev);
167
168 return (ndx < 0) ? 0 : ndx + 1;
169 }
170
fdevent_freebsd_kqueue_reset(fdevents * ev)171 static int fdevent_freebsd_kqueue_reset(fdevents *ev) {
172 if (-1 == (ev->kq_fd = kqueue())) {
173 log_error_write(ev->srv, __FILE__, __LINE__, "SSS",
174 "kqueue failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\"");
175
176 return -1;
177 }
178
179 return 0;
180 }
181
182
fdevent_freebsd_kqueue_init(fdevents * ev)183 int fdevent_freebsd_kqueue_init(fdevents *ev) {
184 ev->type = FDEVENT_HANDLER_FREEBSD_KQUEUE;
185 #define SET(x) \
186 ev->x = fdevent_freebsd_kqueue_##x;
187
188 SET(free);
189 SET(poll);
190 SET(reset);
191
192 SET(event_del);
193 SET(event_set);
194
195 SET(event_next_fdndx);
196 SET(event_get_fd);
197 SET(event_get_revent);
198
199 ev->kq_fd = -1;
200
201 ev->kq_results = calloc(ev->maxfds, sizeof(*ev->kq_results));
202
203 /* check that kqueue works */
204
205 if (-1 == (ev->kq_fd = kqueue())) {
206 log_error_write(ev->srv, __FILE__, __LINE__, "SSS",
207 "kqueue failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\"");
208
209 return -1;
210 }
211
212 close(ev->kq_fd);
213 ev->kq_fd = -1;
214
215 return 0;
216 }
217 #else
fdevent_freebsd_kqueue_init(fdevents * ev)218 int fdevent_freebsd_kqueue_init(fdevents *ev) {
219 UNUSED(ev);
220
221 log_error_write(ev->srv, __FILE__, __LINE__, "S",
222 "kqueue not available, try to set server.event-handler = \"poll\" or \"select\"");
223
224 return -1;
225 }
226 #endif
227