summaryrefslogtreecommitdiffstats
path: root/dev/poll
diff options
context:
space:
mode:
Diffstat (limited to 'dev/poll')
-rw-r--r--dev/poll/Makefile13
-rw-r--r--dev/poll/poll.c445
2 files changed, 458 insertions, 0 deletions
diff --git a/dev/poll/Makefile b/dev/poll/Makefile
new file mode 100644
index 0000000..0247099
--- /dev/null
+++ b/dev/poll/Makefile
@@ -0,0 +1,13 @@
+include ../../include/make/verbose.mk
+
+CC = cc
+OPTIMIZE = -O2 -g
+DEFINE =
+INCLUDE =
+OBJS = poll
+
+poll: poll.c
+ $(cmd_CC) $(OPTIMIZE) $(DEFINE) $(INCLUDE) -o $@ $^
+
+clean:
+ rm -f $(OBJS) *.[oas] *~
diff --git a/dev/poll/poll.c b/dev/poll/poll.c
new file mode 100644
index 0000000..022c039
--- /dev/null
+++ b/dev/poll/poll.c
@@ -0,0 +1,445 @@
+#define _GNU_SOURCE // for POLLRDHUP
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef __linux__
+#include <sys/epoll.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* for OSes which don't have it */
+#ifndef POLLRDHUP
+#define POLLRDHUP 0
+#endif
+
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+#ifndef MSG_MORE
+#define MSG_MORE 0
+#endif
+
+int verbose = 0;
+int cmd = 0;
+int cmdstep = 0;
+int zero = 0;
+int one = 1;
+int lfd = -1;
+int cfd = -1;
+int sfd = -1;
+int connected = 0;
+int use_epoll = 0;
+struct sockaddr_in saddr, caddr;
+socklen_t salen, calen;
+
+static inline const char *side(int fd)
+{
+ if (fd == lfd)
+ return "l";
+ if (fd == sfd)
+ return "s";
+ if (fd == cfd)
+ return "c";
+ return "?";
+}
+
+void usage(const char *arg0)
+{
+ printf("Usage: %s [ arg [<action>[,...]] ] ...\n"
+ "args:\n"
+ " -h display this help\n"
+ " -v verbose mode (shows ret values)\n"
+ " -e use epoll instead of poll\n"
+ " -c <actions> perform <action> on client side socket\n"
+ " -s <actions> perform <action> on server side socket\n"
+ " -l <actions> perform <action> on listening socket\n"
+ "\n"
+ "actions for -c/-s/-l (multiple may be delimited by commas) :\n"
+ " con connect to listener, implicit before first -c/-s\n"
+ " acc accept on listener, implicit before first -s\n"
+ " snd send a few bytes of data\n"
+ " mor send a few bytes of data with MSG_MORE\n"
+ " rcv receive a few bytes of data\n"
+ " drn drain: receive till zero\n"
+ " shr SHUT_RD : shutdown read side\n"
+ " shw SHUT_WR : shutdown write side\n"
+ " shb SHUT_RDWR : shutdown both sides\n"
+ " lin disable lingering on the socket\n"
+ " clo close the file descriptor\n"
+ " pol poll() for any event\n"
+ "\n", arg0);
+}
+
+void die(const char *msg)
+{
+ if (msg)
+ fprintf(stderr, "%s\n", msg);
+ exit(1);
+}
+
+const char *get_errno(int ret)
+{
+ static char errmsg[100];
+
+ if (ret >= 0)
+ return "";
+
+ snprintf(errmsg, sizeof(errmsg), " (%s)", strerror(errno));
+ return errmsg;
+}
+
+void do_acc(int fd)
+{
+ int ret;
+
+ calen = sizeof(caddr);
+ ret = accept(lfd, (struct sockaddr*)&caddr, &calen);
+ if (sfd < 0)
+ sfd = ret;
+ if (verbose)
+ printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
+}
+
+void do_con(int fd)
+{
+ int ret;
+
+ ret = connect(cfd, (const struct sockaddr*)&saddr, salen);
+ if (verbose)
+ printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
+ connected = 1;
+}
+
+void do_snd(int fd)
+{
+ int ret;
+
+ ret = send(fd, "foo", 3, MSG_NOSIGNAL|MSG_DONTWAIT);
+ if (verbose)
+ printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
+}
+
+void do_mor(int fd)
+{
+ int ret;
+
+ ret = send(fd, "foo", 3, MSG_NOSIGNAL|MSG_DONTWAIT|MSG_MORE);
+ if (verbose)
+ printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
+}
+
+void do_rcv(int fd)
+{
+ char buf[10];
+ int ret;
+
+ ret = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
+ if (verbose)
+ printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
+}
+
+void do_drn(int fd)
+{
+ char buf[16384];
+ int total = -1;
+ int ret;
+
+ while (1) {
+ ret = recv(fd, buf, sizeof(buf), 0);
+ if (ret <= 0)
+ break;
+ if (total < 0)
+ total = 0;
+ total += ret;
+ }
+
+ if (verbose)
+ printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, total, get_errno(ret));
+}
+
+void do_shr(int fd)
+{
+ int ret;
+
+ ret = shutdown(fd, SHUT_RD);
+ if (verbose)
+ printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
+}
+
+void do_shw(int fd)
+{
+ int ret;
+
+ ret = shutdown(fd, SHUT_WR);
+ if (verbose)
+ printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
+}
+
+void do_shb(int fd)
+{
+ int ret;
+
+ ret = shutdown(fd, SHUT_RDWR);
+ if (verbose)
+ printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
+}
+
+void do_lin(int fd)
+{
+ struct linger nolinger = { .l_onoff = 1, .l_linger = 0 };
+ int ret;
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, &nolinger, sizeof(nolinger));
+ if (verbose)
+ printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
+}
+
+void do_clo(int fd)
+{
+ int ret;
+
+ ret = close(fd);
+ if (verbose)
+ printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s\n", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret));
+}
+
+void do_pol(int fd)
+{
+ struct pollfd fds = { .fd = fd, .events = POLLIN|POLLOUT|POLLRDHUP, .revents=0 };
+ int flags, flag;
+ int ret;
+
+#ifdef __linux__
+ while (use_epoll) {
+ struct epoll_event evt;
+ static int epoll_fd = -1;
+
+ if (epoll_fd == -1)
+ epoll_fd = epoll_create(1024);
+ if (epoll_fd == -1)
+ break;
+ evt.events = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
+ evt.data.fd = fd;
+ epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &evt);
+ ret = epoll_wait(epoll_fd, &evt, 1, 0);
+
+ if (verbose) {
+ printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s ev=%#x ", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret), ret > 0 ? evt.events : 0);
+ if (ret > 0 && evt.events) {
+ putchar('(');
+
+ for (flags = evt.events; flags; flags ^= flag) {
+ flag = flags ^ (flags & (flags - 1)); // keep lowest bit only
+ switch (flag) {
+ case EPOLLIN: printf("IN"); break;
+ case EPOLLOUT: printf("OUT"); break;
+ case EPOLLPRI: printf("PRI"); break;
+ case EPOLLHUP: printf("HUP"); break;
+ case EPOLLERR: printf("ERR"); break;
+ case EPOLLRDHUP: printf("RDHUP"); break;
+ default: printf("???[%#x]", flag); break;
+ }
+ if (flags ^ flag)
+ putchar(' ');
+ }
+ putchar(')');
+ }
+ putchar('\n');
+ }
+
+ evt.data.fd = fd;
+ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &evt);
+ return;
+ }
+#endif
+ ret = poll(&fds, 1, 0);
+ if (verbose) {
+ printf("cmd #%d stp #%d: %s(%s=%d): ret=%d%s ev=%#x ", cmd, cmdstep, __FUNCTION__ + 3, side(fd), fd, ret, get_errno(ret), ret > 0 ? fds.revents : 0);
+ if (ret > 0 && fds.revents) {
+ putchar('(');
+
+ for (flags = fds.revents; flags; flags ^= flag) {
+ flag = flags ^ (flags & (flags - 1)); // keep lowest bit only
+ switch (flag) {
+ case POLLIN: printf("IN"); break;
+ case POLLOUT: printf("OUT"); break;
+ case POLLPRI: printf("PRI"); break;
+ case POLLHUP: printf("HUP"); break;
+ case POLLERR: printf("ERR"); break;
+ case POLLNVAL: printf("NVAL"); break;
+#if POLLRDHUP
+ case POLLRDHUP: printf("RDHUP"); break;
+#endif
+ default: printf("???[%#x]", flag); break;
+ }
+ if (flags ^ flag)
+ putchar(' ');
+ }
+ putchar(')');
+ }
+ putchar('\n');
+ }
+}
+
+int main(int argc, char **argv)
+{
+ const char *arg0;
+ char *word, *next;
+ int fd;
+
+ /* listener */
+ lfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (lfd < 0)
+ die("socket(l)");
+
+ setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(0);
+ salen = sizeof(saddr);
+
+ if (bind(lfd, (struct sockaddr *)&saddr, salen) < 0)
+ die("bind()");
+
+ if (listen(lfd, 1000) < 0)
+ die("listen()");
+
+ if (getsockname(lfd, (struct sockaddr *)&saddr, &salen) < 0)
+ die("getsockname()");
+
+
+ /* client */
+ cfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (cfd < 0)
+ die("socket(c)");
+
+ arg0 = argv[0];
+ if (argc < 2) {
+ usage(arg0);
+ exit(1);
+ }
+
+ write(1, "#### BEGIN ####\n", 16); // add a visible delimiter in the traces
+
+ while (argc > 1) {
+ argc--; argv++;
+ if (**argv != '-') {
+ usage(arg0);
+ exit(1);
+ }
+
+ fd = -1;
+ switch (argv[0][1]) {
+ case 'h' :
+ usage(arg0);
+ exit(0);
+ break;
+ case 'v' :
+ verbose++;
+ break;
+ case 'e' :
+ use_epoll = 1;
+ break;
+ case 'c' :
+ cmd++; cmdstep = 0;
+ if (!connected) {
+ do_con(cfd);
+ /* connection is pending in accept queue, accept() will either be
+ * explicit with "-l acc" below, or implicit on "-s <cmd>"
+ */
+ }
+ fd = cfd;
+ break;
+ case 's' :
+ cmd++; cmdstep = 0;
+ if (!connected)
+ do_con(cfd);
+ if (sfd < 0)
+ do_acc(lfd);
+ if (sfd < 0)
+ die("accept()");
+ fd = sfd;
+ break;
+ case 'l' :
+ cmd++; cmdstep = 0;
+ fd = lfd;
+ break;
+ default : usage(arg0); exit(1); break;
+ }
+
+ if (fd >= 0) { /* an action is required */
+ if (argc < 2) {
+ usage(arg0);
+ exit(1);
+ }
+
+ for (word = argv[1]; word && *word; word = next) {
+ next = strchr(word, ',');
+ if (next)
+ *(next++) = 0;
+ cmdstep++;
+ if (strcmp(word, "acc") == 0) {
+ do_acc(fd);
+ }
+ else if (strcmp(word, "con") == 0) {
+ do_con(fd);
+ }
+ else if (strcmp(word, "snd") == 0) {
+ do_snd(fd);
+ }
+ else if (strcmp(word, "mor") == 0) {
+ do_mor(fd);
+ }
+ else if (strcmp(word, "rcv") == 0) {
+ do_rcv(fd);
+ }
+ else if (strcmp(word, "drn") == 0) {
+ do_drn(fd);
+ }
+ else if (strcmp(word, "shb") == 0) {
+ do_shb(fd);
+ }
+ else if (strcmp(word, "shr") == 0) {
+ do_shr(fd);
+ }
+ else if (strcmp(word, "shw") == 0) {
+ do_shw(fd);
+ }
+ else if (strcmp(word, "lin") == 0) {
+ do_lin(fd);
+ }
+ else if (strcmp(word, "clo") == 0) {
+ do_clo(fd);
+ }
+ else if (strcmp(word, "pol") == 0) {
+ do_pol(fd);
+ }
+ else {
+ printf("Ignoring unknown action '%s' in step #%d of cmd #%d\n", word, cmdstep, cmd);
+ }
+ }
+ argc--; argv++;
+ }
+ }
+
+ write(1, "#### END ####\n", 14); // add a visible delimiter in the traces
+
+ if (!cmd) {
+ printf("No command was requested!\n");
+ usage(arg0);
+ exit(1);
+ }
+
+ return 0;
+}