Fix build on OpenBSD 6.4 (armv7)
[netspeed.git] / netspeed.c
blob378ab8f57710b6de1ea647d3e6e7abd39811e0fd
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * netspeed: a program to generate network traffic
4 * Copyright (C) 2013 Free Software Foundation, Inc
6 * Author: Nedko Arnaudov
8 * This program 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 3 of the License, or
11 * (at your option) any later version.
13 * This program 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.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 /* C99 */
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdbool.h>
26 #include <errno.h>
27 #include <assert.h>
28 #include <ctype.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include <stdint.h>
32 #include <limits.h>
34 /* some platforms (i.e. OpenBSD 6.4) don't define SIZE_MAX, but have SIZE_T_MAX */
35 #if !defined(SIZE_MAX) && defined(SIZE_T_MAX)
36 #define SIZE_MAX SIZE_T_MAX
37 #endif
39 /* POSIX */
40 #include <poll.h>
41 #include <sys/mman.h>
42 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <netinet/tcp.h>
46 #include <unistd.h>
47 #include <fcntl.h>
48 #include <netdb.h>
49 #include <signal.h>
50 #if !defined(NO_SCHED_FIFO)
51 #include <sched.h>
52 #endif
54 #define DEFAULT_WORKERS 4
56 #define DEFAULT_DL_RESOURCE "/speedtest/random4000x4000.jpg"
57 //#define DL_RESOURCE "/speedtest/random500x500.jpg"
58 #define DEFAULT_UL_RESOURCE "/speedtest/upload.php"
59 #define DEFAULT_UL_SIZE ((size_t)(3 * 1024 * 1024))
61 #define ABOUT "Generate network traffic. Written by Nedko Arnaudov."
63 /* OpenBSD */
64 #if !defined(MSG_NOSIGNAL)
65 #define MSG_NOSIGNAL 0
66 #endif
68 typedef int (* work_fn)(void * ctx, short revents, int * fd_ptr, short * events_ptr);
69 typedef void (* cleanup_fn)(void * ctx);
71 #define STATE_NOT_CONNECTED 0
72 #define STATE_CONNECTING 1
73 #define STATE_SENDING_REQUEST_HEADER 2
74 #define STATE_SENDING_REQUEST_BODY 3
75 #define STATE_READING_REPLY_HEADER 4
76 #define STATE_READING_REPLY_BODY 5
77 #define STATE_ERROR -1
79 struct connection
81 int no;
82 bool upload;
83 const char * host;
84 int state;
85 int socket;
86 uint32_t ip; /* network byte order */
87 size_t offset;
88 size_t size;
89 char buffer[1024 * 1024];
92 #define LOGLVL_NO -1
93 #define LOGLVL_FORCE 0
94 #define LOGLVL_ERROR 1
95 #define LOGLVL_WARNING 2
96 #define LOGLVL_INFO 3
97 #define LOGLVL_DEBUG1 4
98 #define LOGLVL_DEBUG2 5
100 #define LOGLVL_DEFAULT_MAX LOGLVL_WARNING
102 static int g_log_max = LOGLVL_DEFAULT_MAX;
103 static int g_progress = 0;
104 static size_t g_workers = DEFAULT_WORKERS;
105 static const char * g_dl_resource = DEFAULT_DL_RESOURCE;
106 static const char * g_ul_resource = DEFAULT_UL_RESOURCE;
107 static size_t g_ul_size = DEFAULT_UL_SIZE;
109 void log_msg(int level, const char * format, ...) __attribute__((format(printf, 2, 3)));
110 void log_msg(int level, const char * format, ...)
112 va_list ap;
114 if (level > g_log_max) return;
116 va_start(ap, format);
117 vfprintf(level > 0 ? stdout : stderr, format, ap);
118 va_end(ap);
121 #define connection_ptr ((struct connection *)ctx)
123 #define LOG_MSG_(level, format, ...) \
124 log_msg(level, format "\n", ##__VA_ARGS__)
125 #define LOG_WORKER(level, no, format, ...) \
126 log_msg(level, "[%d] " format "\n", (int)(no), ##__VA_ARGS__)
127 #define LOG_WORKER_(level, format, ...) \
128 LOG_WORKER(level, connection_ptr->no, format, ##__VA_ARGS__)
130 #define LFRC( format, ...) LOG_MSG(LOGLVL_FORCE, format, ##__VA_ARGS__)
131 #define LERR( format, ...) LOG_MSG(LOGLVL_ERROR, format, ##__VA_ARGS__)
132 #define LWRN( format, ...) LOG_MSG(LOGLVL_WARNING, format, ##__VA_ARGS__)
133 #define LINF( format, ...) LOG_MSG(LOGLVL_INFO, format, ##__VA_ARGS__)
134 #define LDBG1(format, ...) LOG_MSG(LOGLVL_DEBUG1, format, ##__VA_ARGS__)
135 #define LDBG2(format, ...) LOG_MSG(LOGLVL_DEBUG2, format, ##__VA_ARGS__)
137 #define LOG_MSG LOG_WORKER_
139 int worker(void * ctx, short revents, int * fd_ptr, short * events_ptr)
141 int ret, val;
142 struct sockaddr_in sin;
143 socklen_t len;
144 ssize_t sret;
145 size_t i;
146 const char * ptr;
147 char size_str[100];
148 size_t size;
150 LDBG2("state=%d", connection_ptr->state);
152 switch (connection_ptr->state)
154 case STATE_NOT_CONNECTED:
155 goto connect;
156 case STATE_CONNECTING:
157 assert((revents & POLLOUT) == POLLOUT);
158 goto async_connect_done;
159 case STATE_SENDING_REQUEST_HEADER:
160 case STATE_SENDING_REQUEST_BODY:
161 assert((revents & POLLOUT) == POLLOUT);
162 if ((revents & (POLLERR | POLLHUP)) != 0)
164 LERR("async send fd error. revents=%#hx", revents);
166 len = sizeof(val);
167 ret = getsockopt(connection_ptr->socket, SOL_SOCKET, SO_ERROR, &val, &len);
168 if (ret == -1)
170 LERR("getsockopt() failed to get socket send error. %d (%s)", errno, strerror(errno));
172 else
174 LERR("async send() error %d (%s)", val, strerror(val));
176 goto error;
178 goto send_request_continue;
179 case STATE_READING_REPLY_HEADER:
180 assert((revents & POLLIN) == POLLIN);
181 if ((revents & (POLLERR | POLLHUP)) != 0)
183 LERR("async reply header recv fd error. revents=%#hx", revents);
185 len = sizeof(val);
186 ret = getsockopt(connection_ptr->socket, SOL_SOCKET, SO_ERROR, &val, &len);
187 if (ret == -1)
189 LERR("getsockopt() failed to get socket recv error. %d (%s)", errno, strerror(errno));
191 else
193 LERR("async recv() error %d (%s)", val, strerror(val));
195 goto error;
197 goto read_reply_header;
198 case STATE_READING_REPLY_BODY:
199 assert((revents & POLLIN) == POLLIN);
200 if ((revents & (POLLERR | POLLHUP)) != 0)
202 LERR("async reply body recv fd error. revents=%#hx", revents);
204 len = sizeof(val);
205 ret = getsockopt(connection_ptr->socket, SOL_SOCKET, SO_ERROR, &val, &len);
206 if (ret == -1)
208 LERR("getsockopt() failed to get socket recv error. %d (%s)", errno, strerror(errno));
210 else
212 LERR("async recv() error %d (%s)", val, strerror(val));
214 goto error;
216 goto read_reply_body;
217 default:
218 assert(false);
219 goto error;
222 assert(false);
224 connect:
225 connection_ptr->socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
226 if (connection_ptr->socket == -1)
228 LERR("socket() failed. %d (%s)", errno, strerror(errno));
229 goto error;
232 ret = fcntl(connection_ptr->socket, F_SETFL, O_NONBLOCK);
233 if (ret == -1)
235 LERR("fcntl() failed to set socket non-blocking mode. %d (%s)", errno, strerror(errno));
236 goto error;
240 sin.sin_family = AF_INET;
241 sin.sin_port = htons(80);
242 sin.sin_addr.s_addr = connection_ptr->ip;
244 ret = connect(connection_ptr->socket, (struct sockaddr *)&sin, sizeof(struct sockaddr_in));
245 if (ret == -1)
247 if (errno == EINPROGRESS)
249 connection_ptr->state = STATE_CONNECTING;
250 *fd_ptr = connection_ptr->socket;
251 *events_ptr = POLLOUT;
252 return 1;
255 LERR("connect() failed. %d (%s)", errno, strerror(errno));
256 goto error;
259 LINF("connect complete.");
260 goto send_request;
262 async_connect_done:
263 if ((revents & (POLLERR | POLLHUP)) != 0)
265 LERR("async connect failed. revents=%#hx", revents);
268 len = sizeof(val);
269 ret = getsockopt(connection_ptr->socket, SOL_SOCKET, SO_ERROR, &val, &len);
270 if (ret == -1)
272 LERR("getsockopt() failed to get socket connect error. %d (%s)", errno, strerror(errno));
273 goto error;
275 if (val != 0)
277 LERR("async connect() failed. %d (%s)", val, strerror(val));
278 goto error;
281 LINF("async connect complete.");
283 send_request:
284 LINF("sending request header...");
286 if (connection_ptr->upload)
288 snprintf(size_str, sizeof(size_str), "%zu", g_ul_size);
291 ret = snprintf(
292 connection_ptr->buffer,
293 sizeof(connection_ptr->buffer),
294 "%s %s HTTP/1.1\r\n"
295 "User-Agent: netspeed/0.0\r\n"
296 "Accept: */*\r\n"
297 "Host: %s\r\n"
298 "%s%s%s"
299 "\r\n",
300 connection_ptr->upload ? "POST" : "GET",
301 connection_ptr->upload ? g_ul_resource : g_dl_resource,
302 connection_ptr->host,
303 connection_ptr->upload ? "Content-Length: " : "",
304 connection_ptr->upload ? size_str : "",
305 connection_ptr->upload ? "\r\n" : "");
306 if (ret < -1 || ret >= (int)sizeof(connection_ptr->buffer))
308 LERR("snprintf() failed compose request. %d", ret);
309 goto error;
312 LDBG1("request-header:\n%s", connection_ptr->buffer);
314 connection_ptr->state = STATE_SENDING_REQUEST_HEADER;
315 connection_ptr->offset = 0;
316 connection_ptr->size = (size_t)ret;
318 send_request_continue:
319 while (connection_ptr->size > 0)
321 if (connection_ptr->state == STATE_SENDING_REQUEST_BODY &&
322 connection_ptr->size >= sizeof(connection_ptr->buffer))
324 size = sizeof(connection_ptr->buffer);
326 else
328 size = connection_ptr->size;
331 sret = send(
332 connection_ptr->socket,
333 connection_ptr->buffer + connection_ptr->offset,
334 size,
335 MSG_NOSIGNAL);
336 if (sret == -1)
338 if (errno == EINTR)
340 continue;
343 if (errno == EAGAIN || errno == EWOULDBLOCK)
345 *fd_ptr = connection_ptr->socket;
346 *events_ptr = POLLOUT;
347 return 1;
350 LERR("send() failed. %d (%s)", errno, strerror(errno));
351 goto error;
354 if (connection_ptr->state == STATE_SENDING_REQUEST_HEADER)
356 connection_ptr->offset += sret;
359 connection_ptr->size -= sret;
362 if (connection_ptr->state == STATE_SENDING_REQUEST_HEADER)
364 LINF("request header sent");
366 if (connection_ptr->upload)
368 connection_ptr->state = STATE_SENDING_REQUEST_BODY;
369 connection_ptr->offset = 0;
370 connection_ptr->size = g_ul_size;
371 LINF("sending request body...");
372 goto send_request_continue;
375 else
377 LINF("request body sent");
380 connection_ptr->state = STATE_READING_REPLY_HEADER;
381 connection_ptr->offset = 0; /* parsed size */
382 connection_ptr->size = 0; /* read size */
384 read_reply_header:
385 if (connection_ptr->size >= sizeof(connection_ptr->buffer))
387 LERR("HTTP reply header too big");
388 goto error;
391 sret = recv(
392 connection_ptr->socket,
393 connection_ptr->buffer + connection_ptr->size,
394 sizeof(connection_ptr->buffer) - connection_ptr->size,
395 MSG_NOSIGNAL);
396 if (sret == -1)
398 if (errno == EINTR)
400 goto read_reply_header;
403 if (errno == EAGAIN || errno == EWOULDBLOCK)
405 *fd_ptr = connection_ptr->socket;
406 *events_ptr = POLLIN;
407 return 1;
410 LERR("recv() failed. %d (%s)", errno, strerror(errno));
411 goto error;
414 connection_ptr->size += sret;
416 for (i = connection_ptr->offset; i + 3 < connection_ptr->size; i++)
418 if (connection_ptr->buffer[i ] == '\r' &&
419 connection_ptr->buffer[i + 1] == '\n' &&
420 connection_ptr->buffer[i + 2] == '\r' &&
421 connection_ptr->buffer[i + 3] == '\n')
423 connection_ptr->offset = i + 4;
424 LINF("header size is %zu bytes", connection_ptr->offset);
425 for (i = 0; i < connection_ptr->offset; i++)
427 if ((signed char)connection_ptr->buffer[i] < 0)
429 LERR("invalid char in HTTP reply header");
430 goto error;
433 connection_ptr->buffer[i] = tolower(connection_ptr->buffer[i]);
436 connection_ptr->buffer[connection_ptr->offset] = 0;
437 LDBG1("reply-header:\n%s", connection_ptr->buffer);
439 /* calculate the size of body bytes we already read */
440 i = connection_ptr->size - connection_ptr->offset;
442 ptr = strstr(connection_ptr->buffer, "content-length");
443 if (ptr == NULL)
445 goto unknown_size;
448 ptr += sizeof("content-length") - 1;
450 while (*ptr == ' ') ptr++;
452 if (*ptr != ':')
454 goto unknown_size;
456 ptr++;
458 while (*ptr == ' ') ptr++;
460 val = atoi(ptr);
462 if (val > 0)
464 LINF("total body size is %d bytes", val);
466 if ((size_t)val < i)
468 LERR("body bigger than announced");
469 goto error;
472 /* substract the already received body bytes */
473 connection_ptr->size = (size_t)val - i;
475 else
477 unknown_size:
478 /* server didnt provide body size,
479 assume body end will be marked by connection close */
480 LWRN("unknown body size");
481 goto error;
482 connection_ptr->size = SIZE_MAX;
485 connection_ptr->state = STATE_READING_REPLY_BODY;
486 connection_ptr->offset = i;
487 goto read_reply_body;
491 if (i >= 4)
493 /* next time don't parse the bytes already parsed */
494 connection_ptr->offset = i - 4;
497 goto read_reply_header;
499 read_reply_body:
500 while (connection_ptr->size > 0)
502 sret = recv(
503 connection_ptr->socket,
504 connection_ptr->buffer,
505 sizeof(connection_ptr->buffer),
506 MSG_NOSIGNAL);
507 if (sret == -1)
509 if (errno == EINTR)
511 goto read_reply_header;
514 if (errno == EAGAIN || errno == EWOULDBLOCK)
516 *fd_ptr = connection_ptr->socket;
517 *events_ptr = POLLIN;
518 return 1;
521 LERR("recv() failed. %d (%s)", errno, strerror(errno));
522 goto error;
525 connection_ptr->size -= sret;
526 connection_ptr->offset += sret;
527 if (g_progress > 0)
529 if (g_progress == 1)
531 printf(".");
533 else
535 printf("(%zd)", sret);
538 fflush(stdout);
542 LINF("%zu body bytes read", connection_ptr->offset);
543 goto send_request;
544 //return 0; /* done */
546 error:
547 connection_ptr->state = STATE_ERROR;
548 return -1;
551 void connection_cleanup(void * ctx)
553 if (connection_ptr->socket != -1)
555 LINF("closing socket...");
556 close(connection_ptr->socket);
560 #undef connection_ptr
561 #undef LOG_MSG
562 #define LOG_MSG LOG_MSG_
564 uint32_t resolve_host(const char * hostname)
566 struct hostent * he_ptr;
568 he_ptr = gethostbyname(hostname);
569 if (he_ptr == NULL)
571 LERR("Cannot resolve \"%s\". h_errno is %d", hostname, h_errno);
572 return 0;
575 return *(uint32_t *)(he_ptr->h_addr);
578 bool
579 create_worker(
580 int worker_no,
581 const char * type,
582 uint32_t ip,
583 const char * hostname,
584 void ** ctx,
585 work_fn * work,
586 cleanup_fn * cleanup)
588 struct connection * connection_ptr;
589 bool upload;
591 if (strcmp(type, "d") == 0)
593 upload = false;
595 else if (strcmp(type, "u") == 0)
597 upload = true;
599 else
601 LOG_WORKER(LOGLVL_ERROR, worker_no, "unknown type \"%s\".", type);
602 return false;
605 LOG_WORKER(LOGLVL_INFO, worker_no, "connecting to %s for %s", hostname, upload ? "uploading" : "downloading");
607 connection_ptr = malloc(sizeof(struct connection));
608 if (connection_ptr == NULL)
610 LOG_WORKER(LOGLVL_ERROR, worker_no, "memory allocation failed.");
611 return false;
614 connection_ptr->no = worker_no;
615 connection_ptr->upload = upload;
616 connection_ptr->host = hostname;
617 connection_ptr->state = STATE_NOT_CONNECTED;
618 connection_ptr->socket = -1;
619 connection_ptr->ip = ip;
621 *ctx = connection_ptr;
622 *work = worker;
623 *cleanup = connection_cleanup;
625 return true;
628 #if !defined(NO_SCHED_FIFO)
629 static bool disable_preemption(int priority)
631 struct sched_param sched_param;
633 sched_param.sched_priority = priority;
634 if (sched_setscheduler(0, SCHED_FIFO, &sched_param) != 0)
636 LERR("Cannot set scheduling policy %d (%s)", errno, strerror(errno));
637 return false;
640 return true;
642 #endif
644 int main(int argc, char ** argv)
646 int ret;
647 uint32_t ip;
648 struct worker
650 void * ctx;
651 work_fn work;
652 cleanup_fn cleanup;
653 struct pollfd pollfd;
655 struct worker * workers = NULL;
656 struct pollfd * pollfds = NULL;
657 int nfds, poll_index;
658 size_t i;
659 bool worker_count_supplied = false;
660 size_t workers_per_host;
661 int host_index;
663 argc--; argv++;
665 /* process options */
666 while (argc > 0 && **argv == '-')
668 if (strcmp(*argv, "-v") == 0)
670 g_log_max++;
672 else if (strcmp(*argv, "-q") == 0)
674 g_log_max--;
676 else if (strcmp(*argv, "-s") == 0)
678 g_log_max = -1;
680 else if (strcmp(*argv, "-p") == 0)
682 g_progress++;
684 else if (strcmp(*argv, "--help") == 0)
686 goto help;
688 else if (strcmp(*argv, "-w") == 0 && argc >= 2)
690 i = atoi(argv[1]);
691 if (i < 1)
693 LERR("Bad value for workers option");
694 goto optfail;
697 g_workers = i;
698 worker_count_supplied = true;
699 argc--; argv++;
701 else if (strcmp(*argv, "-u") == 0 && argc >= 2)
703 i = atoi(argv[1]);
704 if (i < 1)
706 LERR("Bad value for upload size option");
707 goto optfail;
710 g_ul_size = i;
711 argc--; argv++;
713 else
715 LWRN("Ignoring unknown option %s", *argv);
718 //printf("log level max is %d\n", g_log_max);
719 argc--; argv++;
722 if (argc == 0 || (argc % 2) != 0)
724 help:
725 LFRC(ABOUT);
726 LFRC("Usage: netspeed [options] <type> <host> [<type> <host>] ...");
727 LFRC("");
728 LFRC("Options:");
729 LFRC(" -v Increase verbosity. May be used more than once.");
730 LFRC(" -q Decrease verbosity. May be used more than once.");
731 LFRC(" -s Be completely silent.");
732 LFRC(" -p Print progress (dots). Use twice for printing chunk sizes.");
733 LFRC(" -w <num> Worker count (per type/host pair).");
734 LFRC(" -u <num> Upload size. Ignored when downloading.");
735 LFRC(" --help Shows this help text");
736 LFRC("");
737 LFRC(" <type> is either 'u' (upload) or 'd' (download)");
738 LFRC(" <host> is a ookla speedtest host");
739 ret = 0;
740 goto exit;
743 LFRC(ABOUT);
744 if (g_log_max != LOGLVL_DEFAULT_MAX)
746 LFRC("log level max is %d", g_log_max);
749 if (argc > 2)
751 workers_per_host = worker_count_supplied ? g_workers : 1;
753 LFRC("%zu worker(s) per host", workers_per_host);
755 g_workers = workers_per_host * (argc / 2);
757 LFRC("%zu total worker(s)", g_workers);
759 else
761 workers_per_host = g_workers;
762 LFRC("%zu worker(s)", g_workers);
765 LFRC("POST body size: %zu bytes", g_ul_size);
766 //LFRC("download resource: %s", g_dl_resource);
767 //LFRC("upload resource: %s", g_ul_resource);
769 workers = calloc(g_workers, sizeof(struct worker));
770 if (workers == NULL)
772 LERR("memory allocation failed. (workers)");
773 ret = 1;
774 goto free;
777 pollfds = calloc(g_workers, sizeof(struct pollfd));
778 if (pollfds == NULL)
780 LERR("memory allocation failed. (pollfds)");
781 ret = 1;
782 goto free;
785 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
787 LERR("Cannot ignore SIGPIPE. %d (%s)", errno, strerror(errno));
788 goto fail;
791 host_index = 0;
792 while (argc > 0)
794 assert(argc >= 2);
795 assert(argc % 2 == 0);
797 ip = resolve_host(argv[1]);
798 if (ip == 0)
800 goto fail;
803 for (i = host_index * workers_per_host; i < (host_index + 1) * workers_per_host; i++)
805 if (!create_worker(
807 argv[0],
809 argv[1],
810 &workers[i].ctx,
811 &workers[i].work,
812 &workers[i].cleanup))
814 g_workers = 0;
815 goto fail;
818 workers[i].pollfd.fd = -1;
819 workers[i].pollfd.revents = 0;
822 host_index++;
823 argc -= 2;
824 argv += 2;
827 ret = mlockall(MCL_CURRENT | MCL_FUTURE);
828 if (ret == -1)
830 LERR("mlockall() failed. %d (%s)", errno, strerror(errno));
831 //goto fail;
834 #if !defined(NO_SCHED_FIFO)
835 if (!disable_preemption(10))
837 //goto fail;
839 #endif
841 poll_index = 0;
842 loop:
843 assert(poll_index == 0);
844 for (i = 0; i < g_workers; i++)
846 if (workers[i].work != NULL)
848 if (workers[i].pollfd.fd == -1 || /* first time */
849 workers[i].pollfd.revents != 0) /* or when there are pending events */
851 ret = workers[i].work(
852 workers[i].ctx,
853 workers[i].pollfd.revents,
854 &workers[i].pollfd.fd,
855 &workers[i].pollfd.events);
856 if (ret < 0)
858 ret = -ret;
859 goto cleanup;
862 if (ret == 0)
864 /* worker done */
865 workers[i].work = NULL;
866 LOG_WORKER(LOGLVL_INFO, i, "worker done");
867 continue;
870 workers[i].pollfd.revents = 0;
872 assert(workers[i].pollfd.fd != -1);
873 assert(workers[i].pollfd.events != 0);
874 LOG_WORKER(LOGLVL_DEBUG2, i, "worker waits on %d\n", workers[i].pollfd.fd);
876 else
878 LOG_WORKER(LOGLVL_DEBUG2, i, "worker still waits on %d\n", workers[i].pollfd.fd);
881 pollfds[poll_index].fd = workers[i].pollfd.fd;
882 pollfds[poll_index].events = workers[i].pollfd.events;
883 pollfds[poll_index].revents = 0;
884 poll_index++;
888 if (poll_index == 0)
890 ret = 0;
891 LINF("no more workers");
892 goto cleanup;
895 nfds = poll_index;
896 LDBG2("polling %d fds", nfds);
897 ret = poll(pollfds, nfds, -1);
898 LDBG2("poll() returns %d", ret);
899 if (ret == -1)
901 LERR("poll() failed. %d (%s)", errno, strerror(errno));
902 goto fail;
905 assert(ret > 0);
906 poll_index = 0;
907 while (ret > 0)
909 assert(poll_index < nfds);
910 if (pollfds[poll_index].revents != 0)
912 for (i = 0; i < g_workers; i++)
914 if (workers[i].work != NULL &&
915 workers[i].pollfd.fd == pollfds[poll_index].fd)
917 workers[i].pollfd.revents = pollfds[poll_index].revents;
918 assert(workers[i].pollfd.revents != 0);
919 break;
922 assert(i < g_workers); /* fd/worker not found */
923 ret--;
925 poll_index++;
927 poll_index = 0;
928 goto loop;
930 fail:
931 ret = 1;
932 cleanup:
933 for (i = 0; i < g_workers; i++)
935 if (workers[i].cleanup != NULL)
937 workers[i].cleanup(workers[i].ctx);
940 free:
941 free(workers);
942 free(pollfds);
943 exit:
944 return ret;
945 optfail:
946 ret = 1;
947 goto exit;