#include "common.h" #include "config_parse.h" #include "unittest.h" #include "shared.h" static const char rcsid[] = "$Id: ipc-comparisontest.c,v 1.1 2015/06/30 21:01:35 karls Exp $"; #define MOTHER (0) /* descriptor mother reads/writes on. */ #define CHILD (1) /* descriptor child reads/writes on. */ #define ITERATIONS (1000000) static void child(const int mode, const int s); static void parent(const int mode, const int s); static void cleanup(void); pid_t childpid; sockd_io_t sentio; int do_test(mode) const int mode; { const char *function = "do_test()"; socklen_t optlen; int p, min, sndbuf, rcvbuf, pipev[2]; /* sockscf.option.debug = 0; */ rcvbuf = (sizeof(sockd_io_t) + CMSG_SPACE(sizeof(int)) * FDPASS_MAX); rcvbuf += SENDMSG_PADBYTES; sndbuf = rcvbuf * (SOCKD_IOMAX + 1); if (HAVE_PIPEBUFFER_SEND_BASED) ; /* as expected. */ else if (HAVE_PIPEBUFFER_RECV_BASED) { /* * reverse of our assumption that how much we can write to the pipe * depends on the pipe's sndbuf. On this platform it instead depends * on the pipe's rcvbuf. */ const size_t tmp = sndbuf; sndbuf = rcvbuf; rcvbuf = tmp; } else if (HAVE_PIPEBUFFER_UNKNOWN) { /* wastes a lot of memory. */ rcvbuf = MAX(sndbuf, rcvbuf); sndbuf = MAX(sndbuf, rcvbuf); } if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, pipev) != 0) serr("socketpair(2) failed"); /* * Need to set the datapipe non-blocking too, even though it's a * DGRAM-based pipe, or we will block on write. Because it's * AF_LOCAL? */ SASSERTX(setnonblocking(pipev[MOTHER], "") != -1); p = min = rcvbuf; do { if (setsockopt(pipev[MOTHER], SOL_SOCKET, SO_RCVBUF, &p, sizeof(p)) != 0 || setsockopt(pipev[CHILD], SOL_SOCKET, SO_RCVBUF, &p, sizeof(p)) != 0) { slog(LOG_DEBUG, "could not set SO_RCVBUF to %d: %s", p, strerror(errno)); p -= min; } else break; } while (p > min); min = sndbuf; p = sndbuf; do { if (setsockopt(pipev[MOTHER], SOL_SOCKET, SO_SNDBUF, &p, sizeof(p)) != 0 || setsockopt(pipev[CHILD], SOL_SOCKET, SO_SNDBUF, &p, sizeof(p)) != 0) { slog(LOG_DEBUG, "could not set SO_SNDBUF to %d: %s", p, strerror(errno)); p -= min; } else break; } while (p > min); sndbuf = rcvbuf * (SOCKD_IOMAX + 1); /* * The io we will send to child. Child can use this global, pre-fork, * object, to verify the io it receives matches. */ memset(&sentio, 0xdeadbeef, sizeof(sentio)); switch ((childpid = fork())) { case -1: serr("fork(2) failed"); case 0: newprocinit(); child(mode, pipev[CHILD]); _exit(0); default: slog(LOG_DEBUG, "forked child with pid %ld", (long)childpid); SASSERTX(atexit(cleanup) == 0); parent(mode, pipev[MOTHER]); } return 0; } static void parent(mode, s) const int mode; const int s; { const char *function = "parent()"; sockd_io_t *io; struct iovec iov[1]; struct msghdr msg; size_t i, ioc, fdc; int fdv[FDPASS_MAX + 1]; CMSG_AALLOC(cmsg, sizeof(fdv)); for (fdc = 0; fdc < ELEMENTS(fdv) - 1; ++fdc) { if ((fdv[fdc] = makedummyfd(0, 0, 1)) == -1) { cleanup(); serr("%s: makedummyfd() failed", function); } CMSG_ADDOBJECT(fdv[fdc], cmsg, sizeof(fdv[fdc]) * fdc); } bzero(&msg, sizeof(msg)); msg.msg_iov = iov; msg.msg_name = NULL; for (i = 0; i < ITERATIONS; ++i) { ssize_t rc; size_t length, ioc = 0; switch (mode) { case MODE__LARGE_MESSAGE: iov[ioc].iov_base = &sentio; iov[ioc].iov_len = sizeof(sentio); length = iov[ioc].iov_len; ++ioc; break; case MODE__FILE: fdv[fdc] = socks_mklock(SOCKS_MSGFILE, NULL, 0); SASSERTX(fdv[fdc] != -1); SASSERTX(write(fdv[fdc], &sentio, sizeof(sentio)) == sizeof(sentio)); bzero(&iov, sizeof(iov)); CMSG_ADDOBJECT(fdv[fdc], cmsg, sizeof(fdv[fdc]) * fdc); ++fdc; length = 0; break; default: SERRX(mode); } msg.msg_iovlen = ioc; CMSG_SETHDR_SEND(msg, cmsg, sizeof(*fdv) * fdc); slog(LOG_DEBUG, "%s: sending packet #%lu", function, (unsigned long)i + 1); rc = sendmsgn(s, &msg, 0, 1000); switch (mode) { case MODE__LARGE_MESSAGE: break; case MODE__FILE: { close(fdv[fdc - 1]); --fdc; break; } default: SERRX(mode); } if (rc != length) { if (errno == EINTR) /* * From SIGPROF, and for some reason select() does not get * restarted. */ continue; else serr("%s: sent only %ld out of %ld bytes on iteration #%lu", function, (long)rc, (long)length, (unsigned long)i + 1); } } } static void child(mode, s) const int mode; const int s; { const char *function = "child()"; fd_set *rset; struct iovec iov[1]; struct msghdr msg; struct timeval timeout; sockd_io_t receivedio; ssize_t r; size_t i, fdexpect, fdreceived, ioc, toreceive; int fdv[FDPASS_MAX + 1], p; CMSG_AALLOC(cmsg, sizeof(fdv)); switch (mode) { case MODE__LARGE_MESSAGE: toreceive = sizeof(receivedio); break; case MODE__FILE: toreceive = 0; break; default: SERRX(mode); } bzero(iov, sizeof(iov)); ioc = 0; iov[ioc].iov_base = &receivedio; iov[ioc].iov_len = sizeof(receivedio); ++ioc; bzero(&msg, sizeof(msg)); msg.msg_iov = iov; msg.msg_iovlen = ioc; msg.msg_name = NULL; msg.msg_namelen = 0; CMSG_SETHDR_RECV(msg, cmsg, CMSG_MEMSIZE(cmsg)); rset = allocate_maxsize_fdset(); FD_ZERO(rset); timeout.tv_sec = 1; timeout.tv_usec = 0; for (i = 0; i < ITERATIONS; ++i) { struct timeval timeout = { 1, 0 }; int fd; FD_SET(s, rset); p = select(s + 1, rset, NULL, NULL, &timeout); if (p == 0) serrx("%s: received only %lu/%lu messages from parent", function, (unsigned long)i, (unsigned long)ITERATIONS); if ((r = recvmsg(s, &msg, 0)) < toreceive) serr("%s: recvmsg(): unexpected short read (%ld < %lu)", function, (long)r, (unsigned long)toreceive, strerror(errno)); slog(LOG_DEBUG, "%s: received packet #%lu, %lu bytes", function, i + 1, (unsigned long)r); SASSERTX(!socks_msghaserrors(function, &msg)); /* * Descriptors sent us. */ SASSERTX(cmsg->cmsg_level == SOL_SOCKET); SASSERTX(cmsg->cmsg_type == SCM_RIGHTS); for (fdreceived = 0; fdreceived < ELEMENTS(fdv) - 1; ++fdreceived) { CMSG_GETOBJECT(fd, cmsg, sizeof(fd) * fdreceived); SASSERTX(close(fd) == 0); } switch (mode) { case MODE__LARGE_MESSAGE: break; case MODE__FILE: CMSG_GETOBJECT(fd, cmsg, sizeof(fd) * fdreceived); ++fdreceived; SASSERTX(lseek(fd, (off_t)0, SEEK_SET) == 0); SASSERTX(read(fd, &receivedio, sizeof(receivedio)) == sizeof(receivedio)); SASSERTX(close(fd) == 0); break; default: SERRX(mode); } SASSERTX(memcmp(&receivedio, &sentio, sizeof(sentio)) == 0); } _exit(0); } static void cleanup(void) { const int errno_s = errno; int rc; if (childpid > 0) { if ((rc = kill(childpid, SIGTERM)) != 0) slog(LOG_DEBUG, "kill(SIGTERM) to pid %ld returned %d (%s)", (long)childpid, rc, strerror(errno)); sleep(1); if ((rc = kill(childpid, SIGKILL)) != 0 && errno != ESRCH) swarn("kill(SIGKILL) to pid %ld returned %d (%s)", (long)childpid, rc, strerror(errno)); } errno = errno_s; }