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