1 /*--------------------------------------------------------------------------
2  Copyright 1999,2000, Dan Kegel http://www.kegel.com/
3  See the file COPYING
4  (Also freely licensed to Disappearing, Inc. under a separate license
5  which allows them to do absolutely anything they want with it, without
6  regard to the GPL.)
7 
8  This module is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12 
13  This module is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 --------------------------------------------------------------------------*/
22 #if HAVE_DEVPOLL
23 
24 #include "dprint.h"
25 #include <string.h>
26 #include <sys/ioctl.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <sys/poll.h>
30 #include <sys/devpoll.h>
31 #include "Poller_devpoll.h"
32 
33 #include <errno.h>
34 #include <assert.h>
35 #include <stdlib.h>
36 #include <fcntl.h>
37 
38 #ifndef POLLREMOVE
39 #define POLLREMOVE 0x1000
40 #endif
41 
init()42 int Poller_devpoll::init()
43 {
44 	DPRINT(("init()\n"));
45 
46 	m_rfds = 0;
47 
48 	// Allocate things indexed by file descriptor.
49 	m_clivent_alloc = 16;
50 	m_clivents = (struct clivent *)malloc(sizeof(struct clivent) * m_clivent_alloc);
51 	if (!m_clivents)
52 		return ENOMEM;
53 	memset(m_clivents, 0, m_clivent_alloc * sizeof(struct clivent));
54 
55 	// Allocate array of pollfds
56 	m_pfds_used = 0;
57 	m_pfds_alloc = 16;
58 	m_pfds = (struct pollfd *)malloc(sizeof(struct pollfd) * m_pfds_alloc);
59 	if (!m_pfds)
60 		return ENOMEM;
61 
62 	// Open /dev/poll driver
63 	if ((m_dpfd = open("/dev/poll", O_RDWR)) < 0)
64 		return ENOENT;
65 
66 	Poller::init();
67 	return 0;
68 }
69 
shutdown()70 void Poller_devpoll::shutdown()
71 {
72 	if (m_clivents) {
73 		close(m_dpfd);
74 		free(m_clivents);
75 		m_clivents = NULL;
76 		free(m_pfds);
77 		m_pfds = NULL;
78 	}
79 	Poller::shutdown();
80 }
81 
add(int fd,Client * client,short eventmask)82 int Poller_devpoll::add(int fd, Client *client, short eventmask)
83 {
84 	if (fd < 0) {
85 		LOG_ERROR(("add(fd %d): fd out of range\n", fd));
86 		return EINVAL;
87 	}
88 	if ((fd < m_clivent_alloc) && m_clivents[fd].client) {
89 		LOG_ERROR(("add(fd %d, %p,): already monitoring that fd!\n", fd, m_clivents[fd].client));
90 		return EINVAL;
91 	}
92 
93 	int i, n;
94 
95 	// Resize arrays indexed by fd if fd is beyond what we've seen.
96 	if (fd >= m_clivent_alloc) {
97 		n = m_clivent_alloc * 2;
98 		if (n < fd + 1)
99 			n = fd + 1;
100 
101 		struct clivent *pcv = (struct clivent *) realloc(m_clivents, n * sizeof(struct clivent));
102 		if (!pcv)
103 			return ENOMEM;
104 		// Clear new elements
105 		for (i=m_clivent_alloc; i<n; i++)
106 			pcv[i].client = NULL;
107 		m_clivents = pcv;
108 
109 		m_clivent_alloc = n;
110 	}
111 
112 
113 	// Resize things indexed by client number if we've run out of spots.
114 	if (m_pfds_used == m_pfds_alloc) {
115 		n = m_pfds_alloc * 2;
116 
117 		struct pollfd *pfds = (struct pollfd *) realloc(m_pfds, n * sizeof(struct pollfd));
118 		if (!pfds)
119 			return ENOMEM;
120 		m_pfds = pfds;
121 
122 		m_pfds_alloc = n;
123 	}
124 
125 	// Update things indexed by file descriptor.
126 	m_clivents[fd].client = client;
127 	m_clivents[fd].events = eventmask;
128 
129 	// Prepare a pollfd to write to /dev/poll
130 	struct pollfd tmp_pfd;
131 
132 	tmp_pfd.fd = fd;
133 	tmp_pfd.events = eventmask;
134 
135 	// Write pollfd to /dev/poll
136 	if (write(m_dpfd, &tmp_pfd, sizeof(struct pollfd)) != sizeof(struct pollfd)) {
137 		LOG_ERROR(("add(fd %d): could not write fd to dev/poll", fd));
138 		return EINVAL;
139 	}
140 
141 	// Update limits.
142 	m_pfds_used++;
143 
144 	DPRINT(("add(%d, %p, %x) m_pfds_used %d\n",
145 		fd, client, eventmask, m_pfds_used));
146 	return 0;
147 }
148 
del(int fd)149 int Poller_devpoll::del(int fd)
150 {
151 	// Sanity checks
152 	if (fd < 0 || fd >= m_clivent_alloc) {
153 		LOG_ERROR(("del(%d): fd out of range\n", fd));
154 		return EINVAL;
155 	}
156 	if (!m_clivents[fd].client) {
157 		LOG_ERROR(("del(fd %d): not monitoring that fd!\n", fd));
158 		return EINVAL;
159 	}
160 	assert(m_pfds_used > 0);
161 
162 	DPRINT(("del(%d): m_pfds_used %d on entry\n", fd, m_pfds_used));
163 
164 	// Remove from set of pollfds monitored by /dev/poll
165 	struct pollfd tmp_pfd;
166 
167 	tmp_pfd.fd = fd;
168 	tmp_pfd.events = POLLREMOVE;
169 
170 	// Write pollfd to /dev/poll
171 	if (write(m_dpfd, &tmp_pfd, sizeof(struct pollfd)) != sizeof(struct pollfd)) {
172 		LOG_ERROR(("add(fd %d): could not write fd to dev/poll", fd));
173 		return EINVAL;
174 	}
175 
176 	// Remove from arrays indexed by fd.
177 	m_clivents[fd].client = NULL;
178 
179 	// Update limits
180 	m_pfds_used--;
181 
182 	return 0;
183 }
184 
setMask(int fd,short eventmask)185 int Poller_devpoll::setMask(int fd, short eventmask)
186 {
187 	// Sanity checks
188 	if ((fd < 0) || (fd >= m_clivent_alloc)) {
189 		LOG_ERROR(("setMask(fd %d): fd out of range\n", fd));
190 		return EINVAL;
191 	}
192 	if (!m_clivents[fd].client) {
193 		LOG_ERROR(("setMask(fd %d): not monitoring that fd!\n", fd));
194 		return EINVAL;
195 	}
196 
197 	m_clivents[fd].events = eventmask;
198 
199 	// Prepare a pollfd to write to /dev/poll
200 #ifdef SOLARIS
201 
202 	struct pollfd tmp_pfd[2];
203 
204 	tmp_pfd[0].fd = fd;
205 	tmp_pfd[0].events = POLLREMOVE;
206 	tmp_pfd[1].fd = fd;
207 	tmp_pfd[1].events = m_clivents[fd].events;
208 
209 #else
210 	struct pollfd tmp_pfd[1];
211 
212 	tmp_pfd[0].fd = fd;
213 	tmp_pfd[0].events = m_clivents[fd].events;
214 
215 #endif
216 	// Write pollfd to /dev/poll
217 	if (write(m_dpfd, tmp_pfd, sizeof(tmp_pfd)) != sizeof(tmp_pfd)) {
218 		LOG_ERROR(("setMask(fd %d): could not write fd to dev/poll", fd));
219 		return EINVAL;
220 	}
221 
222 	DPRINT(("setMask(%d, %x): new mask %x\n", fd, eventmask, tmp_pfd.events));
223 
224 	return 0;
225 }
226 
orMask(int fd,short eventmask)227 int Poller_devpoll::orMask(int fd, short eventmask)
228 {
229 	// Sanity checks
230 	if ((fd < 0) || (fd >= m_clivent_alloc)) {
231 		LOG_ERROR(("orMask(fd %d): fd out of range\n", fd));
232 		return EINVAL;
233 	}
234 	if (!m_clivents[fd].client) {
235 		LOG_ERROR(("orMask(fd %d): not monitoring that fd!\n", fd));
236 		return EINVAL;
237 	}
238 
239 	m_clivents[fd].events |= eventmask;
240 
241 	// Prepare a pollfd to write to /dev/poll
242 	struct pollfd tmp_pfd;
243 
244 	tmp_pfd.fd = fd;
245 	tmp_pfd.events = m_clivents[fd].events;
246 
247 	// Write pollfd to /dev/poll
248 	if (write(m_dpfd, &tmp_pfd, sizeof(struct pollfd)) != sizeof(struct pollfd)) {
249 		LOG_ERROR(("orMask(fd %d): could not write fd to dev/poll", fd));
250 		return EINVAL;
251 	}
252 
253 	DPRINT(("orMask(%d, %x): new mask %x\n", fd, eventmask, tmp_pfd.events));
254 
255 	return 0;
256 }
257 
andMask(int fd,short eventmask)258 int Poller_devpoll::andMask(int fd, short eventmask)
259 {
260 	// Sanity checks
261 	if ((fd < 0) || (fd >= m_clivent_alloc)) {
262 		LOG_ERROR(("andMask(fd %d): fd out of range\n", fd));
263 		return EINVAL;
264 	}
265 	if (!m_clivents[fd].client) {
266 		LOG_ERROR(("andMask(fd %d): not monitoring that fd!\n", fd));
267 		return EINVAL;
268 	}
269 
270 	m_clivents[fd].events &= eventmask;
271 
272 	// Prepare a pollfd to write to /dev/poll
273 #ifdef SOLARIS
274 
275 	struct pollfd tmp_pfd[2];
276 
277 	tmp_pfd[0].fd = fd;
278 	tmp_pfd[0].events = POLLREMOVE;
279 	tmp_pfd[1].fd = fd;
280 	tmp_pfd[1].events = m_clivents[fd].events;
281 
282 #else
283 	struct pollfd tmp_pfd[1];
284 
285 	tmp_pfd[0].fd = fd;
286 	tmp_pfd[0].events = m_clivents[fd].events;
287 
288 #endif
289 	// Write pollfd to /dev/poll
290 	if (write(m_dpfd, tmp_pfd, sizeof(tmp_pfd)) != sizeof(tmp_pfd)) {
291 		LOG_ERROR(("andMask(fd %d): could not write fd to dev/poll", fd));
292 		return EINVAL;
293 	}
294 
295 	DPRINT(("andMask(%d, %x): new mask %x\n", fd, eventmask, tmp_pfd.events));
296 
297 	return 0;
298 }
299 
300 /**
301  Sleep at most timeout_millisec waiting for an I/O readiness event
302  on the file descriptors we're watching.  Fills internal array
303  of readiness events.  Call getNextEvent() repeatedly to read its
304  contents.
305  @return 0 on success, EWOULDBLOCK if no events ready
306  */
waitForEvents(int timeout_millisec)307 int Poller_devpoll::waitForEvents(int timeout_millisec)
308 {
309 	int err;
310 	struct dvpoll dopoll;
311 
312 	dopoll.dp_timeout = timeout_millisec;
313 	dopoll.dp_nfds = m_pfds_used;
314 	dopoll.dp_fds = m_pfds;
315 
316 	// Wait for I/O events the clients are interested in.
317 	m_rfds = ioctl(m_dpfd, DP_POLL, &dopoll);
318 	if (m_rfds == -1) {
319 		err = errno;
320 		DPRINT(("waitForEvents: poll() returned -1, errno %d\n", err));
321 		return err;
322 	}
323 
324 	LOG_TRACE(("waitForEvents: got %d events\n", m_rfds));
325 
326 	return m_rfds ? 0 : EWOULDBLOCK;
327 }
328 
329 /**
330  Get the next event that was found by waitForEvents.
331  @return 0 on success, EWOULDBLOCK if no more events
332  */
getNextEvent(PollEvent * e)333 int Poller_devpoll::getNextEvent(PollEvent *e)
334 {
335 	while (m_rfds >= 1) {
336 		m_rfds--;
337 		assert(m_pfds[m_rfds].revents > 0);
338 
339 		int fd = m_pfds[m_rfds].fd;
340 		assert((0 <= fd) && (fd < m_clivent_alloc));
341 
342 		// Check to make sure that del() hasnt been called on this fd
343 		if (!m_clivents[fd].client)
344 			continue;
345 
346 		e->fd = fd;
347 		e->revents = m_pfds[m_rfds].revents;
348 		e->client = m_clivents[fd].client;
349 
350 		LOG_TRACE(("getNextEvent: fd %d revents %x m_rfds %d\n", e->fd, e->revents,  m_rfds));
351 
352 		return 0;
353 	}
354 	return EWOULDBLOCK;
355 }
356 
357 
waitAndDispatchEvents(int timeout_millisec)358 int Poller_devpoll::waitAndDispatchEvents(int timeout_millisec)
359 {
360 	int err;
361 	PollEvent event;
362 
363 	err = waitForEvents(timeout_millisec);
364 	if (err)
365 		return err;
366 
367 	// Pump any network traffic into the appropriate Clients
368 	while (m_rfds > 0) {
369 		err = getNextEvent(&event);
370 		if (err) {
371 			if (err != EWOULDBLOCK)
372 				DPRINT(("waitAndDispatchEvents: getNextEvent() returned %d\n", err));
373 			break;
374 		}
375 		err = event.client->notifyPollEvent(&event);
376 		if (err) {
377 			DPRINT(("waitAndDispatchEvents: %p->notifyPollEvent(fd %d) returned %d, deleting\n",
378 				event.client, event.fd, err));
379 			del(event.fd);
380 		}
381 	}
382 
383 	return 0;
384 }
385 
386 #endif
387