/*- * Copyright (c) 2018 Rozhuk Ivan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Author: Rozhuk Ivan * */ /* cc acc_test.c -O0 -DDEBUG -pthread -o acc_test */ #include #include #include #include #include #include #include #include #include #include #include /* malloc, exit */ #include /* snprintf, fprintf */ #include /* close, write, sysconf */ #include /* bcopy, bzero, memcpy, memmove, memset, strerror... */ #include #include #include #include #include #include #define LOG_ERR(error, descr) \ if (0 != error) \ fprintf(stderr, "%s , line: %i, error: %i - %s - %s\n", \ __FUNCTION__, __LINE__, (error), strerror((error)), (descr)) #define LOG_EV(descr) \ fprintf(stdout, "%s , line: %i: %s\n", \ __FUNCTION__, __LINE__, (descr)) #define LOG_INFO(descr) \ fprintf(stdout, "%s\n", (descr)) #define LOG_ERR_FMT(error, fmt, args...) \ if (0 != error) \ fprintf(stderr, "%s , line: %i, error: %i - %s" fmt "\n", \ __FUNCTION__, __LINE__, (error), strerror((error)), ##args) #define LOG_EV_FMT(fmt, args...) \ fprintf(stdout, "%s , line: %i: " fmt "\n", \ __FUNCTION__, __LINE__, ##args) #define LOG_INFO_FMT(fmt, args...) \ fprintf(stdout, fmt"\n", ##args) #define LOG_ADD(descr) \ fprintf(stdout, "%s", (descr)); fflush(stdout) typedef enum wait_type_e { WAIT_ACCEPT, WAIT_RECV, WAIT_SELECT, WAIT_POLL, WAIT_KQUEUE } wait_type_t; typedef enum event_source_e { EVS_SHUTDOWN, EVS_CLOSE } event_src_t; typedef enum socket_create_e { SC_LOCAL, SC_REMOTE } skt_create_t; typedef enum event_gegerate_time_e { EVGT_AFTER_WAIT, EVGT_BEFORE_WAIT } ev_gent_t; typedef struct test_params_s { skt_create_t remote_skt; /* Create socket in thread where wait it. */ int so_family; /* AF_INET */ int non_block; ev_gent_t event_gen_time; wait_type_t wait; event_src_t ev_src; } test_prm_t, *test_prm_p; typedef struct test_data_s { size_t port_add; int skt; test_prm_p tprm; volatile int result; } test_d_t, *test_d_p; static test_prm_t test_prm[] = { { SC_LOCAL, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_ACCEPT, EVS_SHUTDOWN }, { SC_REMOTE, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_ACCEPT, EVS_SHUTDOWN }, //{ SC_LOCAL, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_RECV, EVS_SHUTDOWN }, //{ SC_REMOTE, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_RECV, EVS_SHUTDOWN }, { SC_LOCAL, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_SELECT, EVS_SHUTDOWN }, { SC_REMOTE, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_SELECT, EVS_SHUTDOWN }, { SC_LOCAL, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_POLL, EVS_SHUTDOWN }, { SC_REMOTE, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_POLL, EVS_SHUTDOWN }, { SC_LOCAL, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_KQUEUE, EVS_SHUTDOWN }, { SC_REMOTE, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_KQUEUE, EVS_SHUTDOWN }, { SC_LOCAL, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_ACCEPT, EVS_CLOSE }, { SC_REMOTE, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_ACCEPT, EVS_CLOSE }, //{ SC_LOCAL, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_RECV, EVS_CLOSE }, //{ SC_REMOTE, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_RECV, EVS_CLOSE }, { SC_LOCAL, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_SELECT, EVS_CLOSE }, { SC_REMOTE, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_SELECT, EVS_CLOSE }, { SC_LOCAL, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_POLL, EVS_CLOSE }, { SC_REMOTE, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_POLL, EVS_CLOSE }, { SC_LOCAL, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_KQUEUE, EVS_CLOSE }, { SC_REMOTE, AF_INET, 0, EVGT_AFTER_WAIT, WAIT_KQUEUE, EVS_CLOSE }, { SC_LOCAL, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_ACCEPT, EVS_SHUTDOWN }, { SC_REMOTE, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_ACCEPT, EVS_SHUTDOWN }, //{ SC_LOCAL, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_RECV, EVS_SHUTDOWN }, //{ SC_REMOTE, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_RECV, EVS_SHUTDOWN }, { SC_LOCAL, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_SELECT, EVS_SHUTDOWN }, { SC_REMOTE, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_SELECT, EVS_SHUTDOWN }, { SC_LOCAL, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_POLL, EVS_SHUTDOWN }, { SC_REMOTE, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_POLL, EVS_SHUTDOWN }, { SC_LOCAL, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_KQUEUE, EVS_SHUTDOWN }, { SC_REMOTE, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_KQUEUE, EVS_SHUTDOWN }, { SC_LOCAL, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_ACCEPT, EVS_CLOSE }, { SC_REMOTE, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_ACCEPT, EVS_CLOSE }, //{ SC_LOCAL, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_RECV, EVS_CLOSE }, //{ SC_REMOTE, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_RECV, EVS_CLOSE }, { SC_LOCAL, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_SELECT, EVS_CLOSE }, { SC_REMOTE, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_SELECT, EVS_CLOSE }, { SC_LOCAL, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_POLL, EVS_CLOSE }, { SC_REMOTE, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_POLL, EVS_CLOSE }, { SC_LOCAL, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_KQUEUE, EVS_CLOSE },//*/ { SC_REMOTE, AF_INET, 0, EVGT_BEFORE_WAIT, WAIT_KQUEUE, EVS_CLOSE },//*/ { SC_LOCAL, AF_INET, 1, EVGT_AFTER_WAIT, WAIT_ACCEPT, EVS_SHUTDOWN }, { SC_REMOTE, AF_INET, 1, EVGT_AFTER_WAIT, WAIT_ACCEPT, EVS_SHUTDOWN }, //{ SC_LOCAL, AF_INET, 1, EVGT_AFTER_WAIT, WAIT_RECV, EVS_SHUTDOWN }, //{ SC_REMOTE, AF_INET, 1, EVGT_AFTER_WAIT, WAIT_RECV, EVS_SHUTDOWN }, { SC_LOCAL, AF_INET, 1, EVGT_AFTER_WAIT, WAIT_SELECT, EVS_SHUTDOWN }, { SC_REMOTE, AF_INET, 1, EVGT_AFTER_WAIT, WAIT_SELECT, EVS_SHUTDOWN }, { SC_LOCAL, AF_INET, 1, EVGT_AFTER_WAIT, WAIT_POLL, EVS_SHUTDOWN }, { SC_REMOTE, AF_INET, 1, EVGT_AFTER_WAIT, WAIT_POLL, EVS_SHUTDOWN }, { SC_LOCAL, AF_INET, 1, EVGT_AFTER_WAIT, WAIT_KQUEUE, EVS_SHUTDOWN }, { SC_REMOTE, AF_INET, 1, EVGT_AFTER_WAIT, WAIT_KQUEUE, EVS_SHUTDOWN }, { SC_LOCAL, AF_INET, 1, EVGT_BEFORE_WAIT, WAIT_ACCEPT, EVS_SHUTDOWN }, //{ SC_LOCAL, AF_INET, 1, EVGT_BEFORE_WAIT, WAIT_RECV, EVS_SHUTDOWN }, { SC_LOCAL, AF_INET, 1, EVGT_BEFORE_WAIT, WAIT_SELECT, EVS_SHUTDOWN }, { SC_LOCAL, AF_INET, 1, EVGT_BEFORE_WAIT, WAIT_POLL, EVS_SHUTDOWN }, { SC_LOCAL, AF_INET, 1, EVGT_BEFORE_WAIT, WAIT_KQUEUE, EVS_SHUTDOWN }, { SC_LOCAL, 0, 0, 0, 0, 0 } }; /* Set file/socket to non blocking mode */ static int fd_set_nonblocking(int fd, int nonblocked) { int opts; if (-1 == fd) return (EINVAL); opts = fcntl(fd, F_GETFL); /* Read current options. */ if (-1 == opts) return (errno); if (0 == nonblocked) { if (0 == (opts & O_NONBLOCK)) return (0); /* Allready set. */ opts &= ~O_NONBLOCK; } else { if (0 != (opts & O_NONBLOCK)) return (0); /* Allready set. */ opts |= O_NONBLOCK; } if (-1 == fcntl(fd, F_SETFL, opts)) /* Update options. */ return (errno); return (0); } static int socket_create_listen(int family, int type, int proto, int non_block, uint16_t port, int *skt_ret) { int error = 0, on = 1, skt; socklen_t addrlen = 0; struct sockaddr_storage addr; if (NULL == skt_ret) return (EINVAL); skt = socket(family, type, proto); if (-1 == skt) { error = errno; goto err_out; } /* Tune socket. */ setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)); #ifdef SO_REUSEPORT setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(int)); #endif if (0 != fd_set_nonblocking(skt, non_block)) { error = errno; goto err_out; } /* Prepare socket. */ memset(&addr, 0x00, sizeof(struct sockaddr_storage)); switch (family) { case AF_INET: addrlen = sizeof(struct sockaddr_in); ((struct sockaddr_in*)&addr)->sin_len = addrlen; ((struct sockaddr_in*)&addr)->sin_family = AF_INET; ((struct sockaddr_in*)&addr)->sin_port = htons(port); break; case AF_INET6: addrlen = sizeof(struct sockaddr_in6); ((struct sockaddr_in6*)&addr)->sin6_len = addrlen; ((struct sockaddr_in6*)&addr)->sin6_family = AF_INET6; ((struct sockaddr_in6*)&addr)->sin6_port = htons(port); break; } if (-1 == bind(skt, (struct sockaddr*)&addr, addrlen)) { error = errno; goto err_out; } if (-1 == listen(skt, -1)) { error = errno; goto err_out; } err_out: if (0 != error) { close(skt); skt = -1; } (*skt_ret) = skt; return (error); } static void * thread_wait_proc(void *data) { test_d_p td = (test_d_p)data; int error = 0, skt_new, kq_fd; struct sockaddr_storage addr; socklen_t addrlen; fd_set sfds; struct pollfd pfds; struct kevent kev; td->result = -1; if (SC_REMOTE == td->tprm->remote_skt) { /* Create socket. */ LOG_ADD(" rskt "); error = socket_create_listen(td->tprm->so_family, SOCK_STREAM, IPPROTO_TCP, td->tprm->non_block, (55556 + td->port_add), &td->skt); if (0 != error) goto err_out; } switch (td->tprm->wait) { case WAIT_ACCEPT: LOG_ADD(" accept "); addrlen = sizeof(struct sockaddr_storage); skt_new = accept(td->skt, (struct sockaddr*)&addr, &addrlen); error = errno; break; case WAIT_RECV: LOG_ADD(" recv "); addrlen = sizeof(struct sockaddr_storage); if (-1 == recv(td->skt, &addr, addrlen, 0)) { error = errno; } break; case WAIT_SELECT: LOG_ADD(" select "); FD_ZERO(&sfds); FD_SET(td->skt, &sfds); if (-1 == select(1, &sfds, &sfds, &sfds, NULL)) { error = errno; } break; case WAIT_POLL: LOG_ADD(" poll "); pfds.fd = td->skt; pfds.events = (POLLIN | POLLPRI | POLLRDNORM | POLLWRNORM | POLLRDBAND | POLLWRBAND); pfds.revents = 0; if (-1 == poll(&pfds, 1, INFTIM) || 0 != pfds.revents) { error = errno; } break; case WAIT_KQUEUE: LOG_ADD(" kqueue "); kq_fd = kqueue(); if (-1 == kq_fd) { error = errno; break; } EV_SET(&kev, td->skt, EVFILT_READ, EV_ADD, 0, 0, NULL); if (-1 == kevent(kq_fd, &kev, 1, NULL, 0, NULL)) { error = errno; } else { if (EV_ERROR & kev.flags) { error = (int)kev.data; } else if (-1 == kevent(kq_fd, NULL, 0, &kev, 1, NULL)) { error = errno; } } close(kq_fd); break; } err_out: if (0 > error) { error = 0; /* Silence some errors, neg codes recerved for int use. */ } td->result = error; return (NULL); } int main(int argc __unused, char *argv[] __unused) { int error = 0; size_t i; pthread_t tid = NULL; test_d_p td; struct timespec abstime; abstime.tv_sec = 1; abstime.tv_nsec = 0; for (i = 0; 0 != test_prm[i].so_family; i ++) { if (EVGT_BEFORE_WAIT == test_prm[i].event_gen_time && SC_REMOTE == test_prm[i].remote_skt) continue; /* Skip, always fail. */ if (EVGT_BEFORE_WAIT == test_prm[i].event_gen_time && EVS_CLOSE == test_prm[i].ev_src) continue; /* Skip, always fail. */ if (0 != test_prm[i].non_block && WAIT_ACCEPT == test_prm[i].wait) continue; /* Skip, always OK, by design. */ fprintf(stdout, "%zu: socket(%s, %s) ...", i, ((AF_INET == test_prm[i].so_family) ? "AF_INET" : "AF_INET6"), ((0 != test_prm[i].non_block) ? "nblock" : "block")); fflush(stdout); td = calloc(1, sizeof(test_d_t)); if (NULL == td) { error = errno; LOG_ERR(error, "calloc()"); continue; } td->port_add = i; td->result = -2; td->tprm = &test_prm[i]; if (SC_LOCAL == test_prm[i].remote_skt) { /* Create socket. */ LOG_ADD(" lskt "); error = socket_create_listen(td->tprm->so_family, SOCK_STREAM, IPPROTO_TCP, td->tprm->non_block, (45556 + td->port_add), &td->skt); if (0 != error) { LOG_ERR(error, "socket_create_listen()"); continue; } } if (EVGT_AFTER_WAIT == test_prm[i].event_gen_time) { usleep(100000); if (0 != pthread_create(&tid, NULL, thread_wait_proc, (void*)td)) { error = errno; LOG_ERR(error, "pthread_create()"); continue; } usleep(100000); } switch (test_prm[i].ev_src) { case EVS_SHUTDOWN: LOG_ADD(" shutdown "); shutdown(td->skt, SHUT_RDWR); break; case EVS_CLOSE: LOG_ADD(" close "); close(td->skt); break; } if (EVGT_BEFORE_WAIT == test_prm[i].event_gen_time) { if (0 != pthread_create(&tid, NULL, thread_wait_proc, (void*)td)) { error = errno; LOG_ERR(error, "pthread_create()"); continue; } } usleep(100000); LOG_ADD(" chk "); if (0 != pthread_timedjoin_np(tid, NULL, &abstime)) { pthread_cancel(tid); } if (0 == td->result) { LOG_INFO("OK"); } else if (0 < td->result) { LOG_INFO_FMT("OK, ret code: %i - %s", td->result, strerror(td->result)); } else { LOG_INFO_FMT("FAIL, ret code: %i ", td->result); } } return (0); }