0
|
1 /* |
|
2 * sockets.cpp -- portable C++ socket wrappers |
|
3 * |
|
4 * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> |
|
5 * |
|
6 * Permission to use, copy, modify, and/or distribute this software for any |
|
7 * purpose with or without fee is hereby granted, provided that the above |
|
8 * copyright notice and this permission notice appear in all copies. |
|
9 * |
|
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
17 */ |
|
18 |
|
19 #define TIMEOUT_MSG "operation timeout" |
|
20 |
|
21 #include <algorithm> |
|
22 #include <atomic> |
|
23 #include <cstring> |
|
24 #include <mutex> |
|
25 |
|
26 #include "sockets.h" |
|
27 |
|
28 namespace irccd { |
|
29 |
|
30 namespace net { |
|
31 |
|
32 /* |
|
33 * Portable constants |
|
34 * ------------------------------------------------------------------ |
|
35 */ |
|
36 |
|
37 /* {{{ Constants */ |
|
38 |
|
39 #if defined(_WIN32) |
|
40 |
|
41 const Handle Invalid{INVALID_SOCKET}; |
|
42 const int Failure{SOCKET_ERROR}; |
|
43 |
|
44 #else |
|
45 |
|
46 const Handle Invalid{-1}; |
|
47 const int Failure{-1}; |
|
48 |
|
49 #endif |
|
50 |
|
51 /* }}} */ |
|
52 |
|
53 /* |
|
54 * Portable functions |
|
55 * ------------------------------------------------------------------ |
|
56 */ |
|
57 |
|
58 /* {{{ Functions */ |
|
59 |
|
60 #if defined(_WIN32) |
|
61 |
|
62 namespace { |
|
63 |
|
64 static std::mutex s_mutex; |
|
65 static std::atomic<bool> s_initialized{false}; |
|
66 |
|
67 } // !namespace |
|
68 |
|
69 #endif // !_WIN32 |
|
70 |
|
71 void init() noexcept |
|
72 { |
|
73 #if defined(_WIN32) |
|
74 std::lock_guard<std::mutex> lock(s_mutex); |
|
75 |
|
76 if (!s_initialized) { |
|
77 s_initialized = true; |
|
78 |
|
79 WSADATA wsa; |
|
80 WSAStartup(MAKEWORD(2, 2), &wsa); |
|
81 |
|
82 /* |
|
83 * If SOCKET_WSA_NO_INIT is not set then the user |
|
84 * must also call finish himself. |
|
85 */ |
|
86 #if !defined(SOCKET_NO_AUTO_INIT) |
|
87 atexit(finish); |
|
88 #endif |
|
89 } |
|
90 #endif |
|
91 } |
|
92 |
|
93 void finish() noexcept |
|
94 { |
|
95 #if defined(_WIN32) |
|
96 WSACleanup(); |
|
97 #endif |
|
98 } |
|
99 |
|
100 std::string error(int errn) |
|
101 { |
|
102 #if defined(_WIN32) |
|
103 LPSTR str = nullptr; |
|
104 std::string errmsg = "Unknown error"; |
|
105 |
|
106 FormatMessageA( |
|
107 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, |
|
108 NULL, |
|
109 errn, |
|
110 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
|
111 (LPSTR)&str, 0, NULL); |
|
112 |
|
113 |
|
114 if (str) { |
|
115 errmsg = std::string(str); |
|
116 LocalFree(str); |
|
117 } |
|
118 |
|
119 return errmsg; |
|
120 #else |
|
121 return strerror(errn); |
|
122 #endif |
|
123 } |
|
124 |
|
125 std::string error() |
|
126 { |
|
127 #if defined(_WIN32) |
|
128 return error(WSAGetLastError()); |
|
129 #else |
|
130 return error(errno); |
|
131 #endif |
|
132 } |
|
133 |
|
134 /* }}} */ |
|
135 |
|
136 /* |
|
137 * SSL stuff |
|
138 * ------------------------------------------------------------------ |
|
139 */ |
|
140 |
|
141 /* {{{ SSL initialization */ |
|
142 |
|
143 #if !defined(SOCKET_NO_SSL) |
|
144 |
|
145 namespace ssl { |
|
146 |
|
147 namespace { |
|
148 |
|
149 std::mutex mutex; |
|
150 std::atomic<bool> initialized{false}; |
|
151 |
|
152 } // !namespace |
|
153 |
|
154 void finish() noexcept |
|
155 { |
|
156 ERR_free_strings(); |
|
157 } |
|
158 |
|
159 void init() noexcept |
|
160 { |
|
161 std::lock_guard<std::mutex> lock{mutex}; |
|
162 |
|
163 if (!initialized) { |
|
164 initialized = true; |
|
165 |
|
166 SSL_library_init(); |
|
167 SSL_load_error_strings(); |
|
168 OpenSSL_add_all_algorithms(); |
|
169 |
|
170 #if !defined(SOCKET_NO_AUTO_SSL_INIT) |
|
171 atexit(finish); |
|
172 #endif // SOCKET_NO_AUTO_SSL_INIT |
|
173 } |
|
174 } |
|
175 |
|
176 } // !ssl |
|
177 |
|
178 #endif // SOCKET_NO_SSL |
|
179 |
|
180 /* }}} */ |
|
181 |
|
182 /* |
|
183 * Error class |
|
184 * ------------------------------------------------------------------ |
|
185 */ |
|
186 |
|
187 /* {{{ Error */ |
|
188 |
|
189 Error::Error(Code code, std::string function) |
|
190 : m_code{code} |
|
191 , m_function{std::move(function)} |
|
192 , m_error{error()} |
|
193 { |
|
194 } |
|
195 |
|
196 Error::Error(Code code, std::string function, int n) |
|
197 : m_code{code} |
|
198 , m_function{std::move(function)} |
|
199 , m_error{error(n)} |
|
200 { |
|
201 } |
|
202 |
|
203 Error::Error(Code code, std::string function, std::string error) |
|
204 : m_code{code} |
|
205 , m_function{std::move(function)} |
|
206 , m_error{std::move(error)} |
|
207 { |
|
208 } |
|
209 |
|
210 /* }}} */ |
|
211 |
|
212 /* |
|
213 * Predefine addressed to be used |
|
214 * ------------------------------------------------------------------ |
|
215 */ |
|
216 |
|
217 /* {{{ Addresses */ |
|
218 |
|
219 namespace address { |
|
220 |
|
221 /* Default domain */ |
|
222 int Ip::m_default{AF_INET}; |
|
223 |
|
224 Ip::Ip(Type domain) noexcept |
|
225 : m_domain(static_cast<int>(domain)) |
|
226 { |
|
227 assert(m_domain == AF_INET6 || m_domain == AF_INET); |
|
228 |
|
229 if (m_domain == AF_INET6) { |
|
230 std::memset(&m_sin6, 0, sizeof (sockaddr_in6)); |
|
231 } else { |
|
232 std::memset(&m_sin, 0, sizeof (sockaddr_in)); |
|
233 } |
|
234 } |
|
235 |
|
236 Ip::Ip(const std::string &host, int port, Type domain) |
|
237 : m_domain(static_cast<int>(domain)) |
|
238 { |
|
239 assert(m_domain == AF_INET6 || m_domain == AF_INET); |
|
240 |
|
241 if (host == "*") { |
|
242 if (m_domain == AF_INET6) { |
|
243 std::memset(&m_sin6, 0, sizeof (sockaddr_in6)); |
|
244 |
|
245 m_length = sizeof (sockaddr_in6); |
|
246 m_sin6.sin6_addr = in6addr_any; |
|
247 m_sin6.sin6_family = AF_INET6; |
|
248 m_sin6.sin6_port = htons(port); |
|
249 } else { |
|
250 std::memset(&m_sin, 0, sizeof (sockaddr_in)); |
|
251 |
|
252 m_length = sizeof (sockaddr_in); |
|
253 m_sin.sin_addr.s_addr = INADDR_ANY; |
|
254 m_sin.sin_family = AF_INET; |
|
255 m_sin.sin_port = htons(port); |
|
256 } |
|
257 } else { |
|
258 addrinfo hints, *res; |
|
259 |
|
260 std::memset(&hints, 0, sizeof (addrinfo)); |
|
261 hints.ai_family = domain; |
|
262 |
|
263 auto error = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res); |
|
264 if (error != 0) { |
|
265 throw Error{Error::System, "getaddrinfo", gai_strerror(error)}; |
|
266 } |
|
267 |
|
268 if (m_domain == AF_INET6) { |
|
269 std::memcpy(&m_sin6, res->ai_addr, res->ai_addrlen); |
|
270 } else { |
|
271 std::memcpy(&m_sin, res->ai_addr, res->ai_addrlen); |
|
272 } |
|
273 |
|
274 m_length = res->ai_addrlen; |
|
275 freeaddrinfo(res); |
|
276 } |
|
277 } |
|
278 |
|
279 Ip::Ip(const sockaddr_storage *ss, socklen_t length) noexcept |
|
280 : m_length{length} |
|
281 , m_domain{ss->ss_family} |
|
282 { |
|
283 assert(ss->ss_family == AF_INET6 || ss->ss_family == AF_INET); |
|
284 |
|
285 if (ss->ss_family == AF_INET6) { |
|
286 std::memcpy(&m_sin6, ss, length); |
|
287 } else if (ss->ss_family == AF_INET) { |
|
288 std::memcpy(&m_sin, ss, length); |
|
289 } |
|
290 } |
|
291 |
|
292 #if !defined(_WIN32) |
|
293 |
|
294 Local::Local() noexcept |
|
295 { |
|
296 std::memset(&m_sun, 0, sizeof (sockaddr_un)); |
|
297 } |
|
298 |
|
299 Local::Local(std::string path, bool rm) noexcept |
|
300 : m_path{std::move(path)} |
|
301 { |
|
302 /* Silently remove the file even if it fails */ |
|
303 if (rm) { |
|
304 ::remove(m_path.c_str()); |
|
305 } |
|
306 |
|
307 /* Copy the path */ |
|
308 std::memset(m_sun.sun_path, 0, sizeof (m_sun.sun_path)); |
|
309 std::strncpy(m_sun.sun_path, m_path.c_str(), sizeof (m_sun.sun_path) - 1); |
|
310 |
|
311 /* Set the parameters */ |
|
312 m_sun.sun_family = AF_LOCAL; |
|
313 } |
|
314 |
|
315 Local::Local(const sockaddr_storage *ss, socklen_t length) noexcept |
|
316 { |
|
317 assert(ss->ss_family == AF_LOCAL); |
|
318 |
|
319 if (ss->ss_family == AF_LOCAL) { |
|
320 std::memcpy(&m_sun, ss, length); |
|
321 m_path = reinterpret_cast<const sockaddr_un &>(m_sun).sun_path; |
|
322 } |
|
323 } |
|
324 |
|
325 #endif // !_WIN32 |
|
326 |
|
327 } // !address |
|
328 |
|
329 /* }}} */ |
|
330 |
|
331 /* |
|
332 * Select |
|
333 * ------------------------------------------------------------------ |
|
334 */ |
|
335 |
|
336 /* {{{ Select */ |
|
337 |
|
338 std::vector<ListenerStatus> Select::wait(const ListenerTable &table, int ms) |
|
339 { |
|
340 timeval maxwait, *towait; |
|
341 fd_set readset; |
|
342 fd_set writeset; |
|
343 |
|
344 FD_ZERO(&readset); |
|
345 FD_ZERO(&writeset); |
|
346 |
|
347 Handle max = 0; |
|
348 |
|
349 for (const auto &pair : table) { |
|
350 if ((pair.second & Condition::Readable) == Condition::Readable) { |
|
351 FD_SET(pair.first, &readset); |
|
352 } |
|
353 if ((pair.second & Condition::Writable) == Condition::Writable) { |
|
354 FD_SET(pair.first, &writeset); |
|
355 } |
|
356 |
|
357 if (pair.first > max) { |
|
358 max = pair.first; |
|
359 } |
|
360 } |
|
361 |
|
362 maxwait.tv_sec = 0; |
|
363 maxwait.tv_usec = ms * 1000; |
|
364 |
|
365 // Set to nullptr for infinite timeout. |
|
366 towait = (ms < 0) ? nullptr : &maxwait; |
|
367 |
|
368 auto error = ::select(max + 1, &readset, &writeset, nullptr, towait); |
|
369 if (error == Failure) { |
|
370 throw Error{Error::System, "select"}; |
|
371 } |
|
372 if (error == 0) { |
|
373 throw Error{Error::Timeout, "select", TIMEOUT_MSG}; |
|
374 } |
|
375 |
|
376 std::vector<ListenerStatus> sockets; |
|
377 |
|
378 for (const auto &pair : table) { |
|
379 if (FD_ISSET(pair.first, &readset)) { |
|
380 sockets.push_back(ListenerStatus{pair.first, Condition::Readable}); |
|
381 } |
|
382 if (FD_ISSET(pair.first, &writeset)) { |
|
383 sockets.push_back(ListenerStatus{pair.first, Condition::Writable}); |
|
384 } |
|
385 } |
|
386 |
|
387 return sockets; |
|
388 } |
|
389 |
|
390 /* }}} */ |
|
391 |
|
392 /* |
|
393 * Poll |
|
394 * ------------------------------------------------------------------ |
|
395 */ |
|
396 |
|
397 /* {{{ Poll */ |
|
398 |
|
399 /* |
|
400 * Poll implementation |
|
401 * ------------------------------------------------------------------ |
|
402 */ |
|
403 |
|
404 #if defined(SOCKET_HAVE_POLL) |
|
405 |
|
406 #if defined(_WIN32) |
|
407 # define poll WSAPoll |
|
408 #endif |
|
409 |
|
410 short Poll::toPoll(Condition condition) const noexcept |
|
411 { |
|
412 short result(0); |
|
413 |
|
414 if ((condition & Condition::Readable) == Condition::Readable) { |
|
415 result |= POLLIN; |
|
416 } |
|
417 if ((condition & Condition::Writable) == Condition::Writable) { |
|
418 result |= POLLOUT; |
|
419 } |
|
420 |
|
421 return result; |
|
422 } |
|
423 |
|
424 Condition Poll::toCondition(short &event) const noexcept |
|
425 { |
|
426 Condition condition{Condition::None}; |
|
427 |
|
428 /* |
|
429 * Poll implementations mark the socket differently regarding |
|
430 * the disconnection of a socket. |
|
431 * |
|
432 * At least, even if POLLHUP or POLLIN is set, recv() always |
|
433 * return 0 so we mark the socket as readable. |
|
434 */ |
|
435 if ((event & POLLIN) || (event & POLLHUP)) { |
|
436 condition |= Condition::Readable; |
|
437 } |
|
438 if (event & POLLOUT) { |
|
439 condition |= Condition::Writable; |
|
440 } |
|
441 |
|
442 /* Reset event for safety */ |
|
443 event = 0; |
|
444 |
|
445 return condition; |
|
446 } |
|
447 |
|
448 void Poll::set(const ListenerTable &, Handle h, Condition condition, bool add) |
|
449 { |
|
450 if (add) { |
|
451 m_fds.push_back(pollfd{h, toPoll(condition), 0}); |
|
452 } else { |
|
453 auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) { |
|
454 return pfd.fd == h; |
|
455 }); |
|
456 |
|
457 it->events |= toPoll(condition); |
|
458 } |
|
459 } |
|
460 |
|
461 void Poll::unset(const ListenerTable &, Handle h, Condition condition, bool remove) |
|
462 { |
|
463 auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) { |
|
464 return pfd.fd == h; |
|
465 }); |
|
466 |
|
467 if (remove) { |
|
468 m_fds.erase(it); |
|
469 } else { |
|
470 it->events &= ~(toPoll(condition)); |
|
471 } |
|
472 } |
|
473 |
|
474 std::vector<ListenerStatus> Poll::wait(const ListenerTable &, int ms) |
|
475 { |
|
476 auto result = poll(m_fds.data(), m_fds.size(), ms); |
|
477 if (result == 0) { |
|
478 throw Error{Error::Timeout, "select", TIMEOUT_MSG}; |
|
479 } |
|
480 if (result < 0) { |
|
481 throw Error{Error::System, "poll"}; |
|
482 } |
|
483 |
|
484 std::vector<ListenerStatus> sockets; |
|
485 for (auto &fd : m_fds) { |
|
486 if (fd.revents != 0) { |
|
487 sockets.push_back(ListenerStatus{fd.fd, toCondition(fd.revents)}); |
|
488 } |
|
489 } |
|
490 |
|
491 return sockets; |
|
492 } |
|
493 |
|
494 #endif // !SOCKET_HAVE_POLL |
|
495 |
|
496 /* }}} */ |
|
497 |
|
498 /* |
|
499 * Epoll implementation |
|
500 * ------------------------------------------------------------------ |
|
501 */ |
|
502 |
|
503 /* {{{ Epoll */ |
|
504 |
|
505 #if defined(SOCKET_HAVE_EPOLL) |
|
506 |
|
507 uint32_t Epoll::toEpoll(Condition condition) const noexcept |
|
508 { |
|
509 uint32_t events = 0; |
|
510 |
|
511 if ((condition & Condition::Readable) == Condition::Readable) { |
|
512 events |= EPOLLIN; |
|
513 } |
|
514 if ((condition & Condition::Writable) == Condition::Writable) { |
|
515 events |= EPOLLOUT; |
|
516 } |
|
517 |
|
518 return events; |
|
519 } |
|
520 |
|
521 Condition Epoll::toCondition(uint32_t events) const noexcept |
|
522 { |
|
523 Condition condition{Condition::None}; |
|
524 |
|
525 if ((events & EPOLLIN) || (events & EPOLLHUP)) { |
|
526 condition |= Condition::Readable; |
|
527 } |
|
528 if (events & EPOLLOUT) { |
|
529 condition |= Condition::Writable; |
|
530 } |
|
531 |
|
532 return condition; |
|
533 } |
|
534 |
|
535 void Epoll::update(Handle h, int op, int eflags) |
|
536 { |
|
537 epoll_event ev; |
|
538 |
|
539 std::memset(&ev, 0, sizeof (epoll_event)); |
|
540 |
|
541 ev.events = eflags; |
|
542 ev.data.fd = h; |
|
543 |
|
544 if (epoll_ctl(m_handle, op, h, &ev) < 0) { |
|
545 throw Error{Error::System, "epoll_ctl"}; |
|
546 } |
|
547 } |
|
548 |
|
549 Epoll::Epoll() |
|
550 : m_handle{epoll_create1(0)} |
|
551 { |
|
552 if (m_handle < 0) { |
|
553 throw Error{Error::System, "epoll_create"}; |
|
554 } |
|
555 } |
|
556 |
|
557 Epoll::~Epoll() |
|
558 { |
|
559 close(m_handle); |
|
560 } |
|
561 |
|
562 /* |
|
563 * For set and unset, we need to apply the whole flags required, so if the socket |
|
564 * was set to Connection::Readable and user add Connection::Writable, we must |
|
565 * place both. |
|
566 */ |
|
567 void Epoll::set(const ListenerTable &table, Handle sc, Condition condition, bool add) |
|
568 { |
|
569 if (add) { |
|
570 update(sc, EPOLL_CTL_ADD, toEpoll(condition)); |
|
571 m_events.resize(m_events.size() + 1); |
|
572 } else { |
|
573 update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) | condition)); |
|
574 } |
|
575 } |
|
576 |
|
577 /* |
|
578 * Unset is a bit complicated case because Listener tells us which |
|
579 * flag to remove but to update epoll descriptor we need to pass |
|
580 * the effective flags that we want to be applied. |
|
581 * |
|
582 * So we put the same flags that are currently effective and remove the |
|
583 * requested one. |
|
584 */ |
|
585 void Epoll::unset(const ListenerTable &table, Handle sc, Condition condition, bool remove) |
|
586 { |
|
587 if (remove) { |
|
588 update(sc, EPOLL_CTL_DEL, 0); |
|
589 m_events.resize(m_events.size() - 1); |
|
590 } else { |
|
591 update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) & ~(condition))); |
|
592 } |
|
593 } |
|
594 |
|
595 std::vector<ListenerStatus> Epoll::wait(const ListenerTable &, int ms) |
|
596 { |
|
597 int ret = epoll_wait(m_handle, m_events.data(), m_events.size(), ms); |
|
598 std::vector<ListenerStatus> result; |
|
599 |
|
600 if (ret == 0) { |
|
601 throw Error{Error::Timeout, "epoll_wait", TIMEOUT_MSG}; |
|
602 } |
|
603 if (ret < 0) { |
|
604 throw Error{Error::System, "epoll_wait"}; |
|
605 } |
|
606 |
|
607 for (int i = 0; i < ret; ++i) { |
|
608 result.push_back(ListenerStatus{m_events[i].data.fd, toCondition(m_events[i].events)}); |
|
609 } |
|
610 |
|
611 return result; |
|
612 } |
|
613 |
|
614 #endif // !SOCKET_HAVE_EPOLL |
|
615 |
|
616 /* }}} */ |
|
617 |
|
618 /* |
|
619 * Kqueue implementation |
|
620 * ------------------------------------------------------------------ |
|
621 */ |
|
622 |
|
623 /* {{{ Kqueue */ |
|
624 |
|
625 #if defined(SOCKET_HAVE_KQUEUE) |
|
626 |
|
627 Kqueue::Kqueue() |
|
628 : m_handle(kqueue()) |
|
629 { |
|
630 if (m_handle < 0) { |
|
631 throw Error{Error::System, "kqueue"}; |
|
632 } |
|
633 } |
|
634 |
|
635 Kqueue::~Kqueue() |
|
636 { |
|
637 close(m_handle); |
|
638 } |
|
639 |
|
640 void Kqueue::update(Handle h, int filter, int kflags) |
|
641 { |
|
642 struct kevent ev; |
|
643 |
|
644 EV_SET(&ev, h, filter, kflags, 0, 0, nullptr); |
|
645 |
|
646 if (kevent(m_handle, &ev, 1, nullptr, 0, nullptr) < 0) { |
|
647 throw Error{Error::System, "kevent"}; |
|
648 } |
|
649 } |
|
650 |
|
651 void Kqueue::set(const ListenerTable &, Handle h, Condition condition, bool add) |
|
652 { |
|
653 if ((condition & Condition::Readable) == Condition::Readable) { |
|
654 update(h, EVFILT_READ, EV_ADD | EV_ENABLE); |
|
655 } |
|
656 if ((condition & Condition::Writable) == Condition::Writable) { |
|
657 update(h, EVFILT_WRITE, EV_ADD | EV_ENABLE); |
|
658 } |
|
659 |
|
660 if (add) { |
|
661 m_result.resize(m_result.size() + 1); |
|
662 } |
|
663 } |
|
664 |
|
665 void Kqueue::unset(const ListenerTable &, Handle h, Condition condition, bool remove) |
|
666 { |
|
667 if ((condition & Condition::Readable) == Condition::Readable) { |
|
668 update(h, EVFILT_READ, EV_DELETE); |
|
669 } |
|
670 if ((condition & Condition::Writable) == Condition::Writable) { |
|
671 update(h, EVFILT_WRITE, EV_DELETE); |
|
672 } |
|
673 |
|
674 if (remove) { |
|
675 m_result.resize(m_result.size() - 1); |
|
676 } |
|
677 } |
|
678 |
|
679 std::vector<ListenerStatus> Kqueue::wait(const ListenerTable &, int ms) |
|
680 { |
|
681 std::vector<ListenerStatus> sockets; |
|
682 timespec ts = { 0, 0 }; |
|
683 timespec *pts = (ms <= 0) ? nullptr : &ts; |
|
684 |
|
685 ts.tv_sec = ms / 1000; |
|
686 ts.tv_nsec = (ms % 1000) * 1000000; |
|
687 |
|
688 int nevents = kevent(m_handle, nullptr, 0, &m_result[0], m_result.capacity(), pts); |
|
689 |
|
690 if (nevents == 0) { |
|
691 throw Error{Error::Timeout, "kevent", TIMEOUT_MSG}; |
|
692 } |
|
693 if (nevents < 0) { |
|
694 throw Error{Error::System, "kevent"}; |
|
695 } |
|
696 |
|
697 for (int i = 0; i < nevents; ++i) { |
|
698 sockets.push_back(ListenerStatus{ |
|
699 static_cast<Handle>(m_result[i].ident), |
|
700 m_result[i].filter == EVFILT_READ ? Condition::Readable : Condition::Writable |
|
701 }); |
|
702 } |
|
703 |
|
704 return sockets; |
|
705 } |
|
706 |
|
707 #endif // !SOCKET_HAVE_KQUEUE |
|
708 |
|
709 /* }}} */ |
|
710 |
|
711 } // !net |
|
712 |
|
713 } // !irccd |