aboutsummaryrefslogtreecommitdiffstats
path: root/test.c
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2017-10-21 23:01:36 +0200
committerMattias Andrée <maandree@kth.se>2017-10-21 23:01:36 +0200
commit593f2554287b979dd7fd3640eaad273bee08688d (patch)
treec4a3e85cf8a53591866e2c5c808ec7ad8c1597a0 /test.c
parentFix whitespace (diff)
downloadsbus-593f2554287b979dd7fd3640eaad273bee08688d.tar.gz
sbus-593f2554287b979dd7fd3640eaad273bee08688d.tar.bz2
sbus-593f2554287b979dd7fd3640eaad273bee08688d.tar.xz
Add test and fix bugs
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to 'test.c')
-rw-r--r--test.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/test.c b/test.c
new file mode 100644
index 0000000..a233761
--- /dev/null
+++ b/test.c
@@ -0,0 +1,356 @@
+#include "libsbus.h"
+
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+static pid_t pid;
+
+#define assert(e)\
+ (errno = 0, (e) ? 0 :\
+ (fprintf(stderr, "FAILURE: %s; errno=%s; line=%i\n",\
+ #e, strerror(errno), __LINE__), exit(1), 0))
+
+static void
+touch(const char *path)
+{
+ int fd;
+ assert((fd = open(path, O_WRONLY | O_CREAT, 0)) > 0);
+ close(fd);
+}
+
+static void
+pdeath()
+{
+ prctl(PR_SET_PDEATHSIG, SIGKILL);
+}
+
+static void
+randomise(void *buf, size_t n)
+{
+ char *p = buf;
+ while (n--)
+ *p++ = rand();
+}
+
+static void
+hexaddr(char *buf, const char *addr, size_t n)
+{
+ const unsigned char *a = (const unsigned char *)addr;
+ for (; n--; a++, buf += 2) {
+ buf[0] = "0123456789abcdef"[*a >> 4];
+ buf[1] = "0123456789abcdef"[*a & 15];
+ }
+ *buf = '\0';
+}
+
+
+static int
+start_random(int autoclose)
+{
+ struct sockaddr_un addr;
+ const char *autoclose_str = "-c";
+ char buf[512], *p;
+ int rw[2], hi, lo, fd;
+ size_t i;
+ alarm(1);
+ if (!autoclose)
+ autoclose_str = NULL;
+ assert(!pipe(rw));
+ assert((pid = fork()) != -1);
+ if (!pid) {
+ alarm(1);
+ close(rw[0]);
+ pdeath();
+ if (rw[1] != STDOUT_FILENO) {
+ assert(dup2(rw[1], STDOUT_FILENO) == STDOUT_FILENO);
+ close(rw[1]);
+ }
+ assert(!execl("./sbusd", "./sbusd", "-fa/dev/unix/abstract", autoclose_str, NULL));
+ abort();
+ }
+ close(rw[1]);
+ assert(read(rw[0], buf, sizeof(buf)) == sizeof("/dev/unix/abstract/") + 2 * sizeof(addr.sun_path));
+ assert(buf[sizeof("/dev/unix/abstract/") + 2 * sizeof(addr.sun_path) - 1] == '\n');
+ assert(!strncmp(buf, "/dev/unix/abstract/", sizeof("/dev/unix/abstract/") - 1));
+ p = &buf[sizeof("/dev/unix/abstract/") - 1];
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ for (i = 0; i < sizeof(addr.sun_path); i++, p += 2) {
+ assert(isxdigit(p[0]));
+ assert(isxdigit(p[1]));
+ hi = (p[0] & 15) + 9 * !isdigit(p[0]);
+ lo = (p[1] & 15) + 9 * !isdigit(p[1]);
+ addr.sun_path[i] = (hi << 4) | lo;
+ }
+ assert(!read(rw[0], buf, sizeof(buf)));
+ close(rw[0]);
+ assert((fd = socket(PF_UNIX, SOCK_SEQPACKET, 0)) >= 0);
+ assert(!connect(fd, (struct sockaddr *)&addr, (socklen_t)sizeof(addr)));
+ return fd;
+}
+
+static int
+start_abstract(int autoclose)
+{
+ struct sockaddr_un addr;
+ const char *autoclose_str = "-c";
+ char buf[512];
+ int rw[2], fd;
+ alarm(1);
+ if (!autoclose)
+ autoclose_str = NULL;
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ randomise(addr.sun_path, sizeof(addr.sun_path));
+ *addr.sun_path = '\0';
+ assert(!pipe(rw));
+ assert((pid = fork()) != -1);
+ if (!pid) {
+ alarm(1);
+ close(rw[0]);
+ pdeath();
+ if (rw[1] != STDOUT_FILENO) {
+ assert(dup2(rw[1], STDOUT_FILENO) == STDOUT_FILENO);
+ close(rw[1]);
+ }
+ sprintf(buf, "/dev/unix/abstract/");
+ hexaddr(&buf[sizeof("/dev/unix/abstract/") - 1], addr.sun_path, sizeof(addr.sun_path));
+ assert(!execl("./sbusd", "./sbusd", "-fa", buf, autoclose_str, NULL));
+ abort();
+ }
+ close(rw[1]);
+ assert(!read(rw[0], buf, sizeof(buf)));
+ close(rw[0]);
+ assert((fd = socket(PF_UNIX, SOCK_SEQPACKET, 0)) >= 0);
+ assert(!connect(fd, (struct sockaddr *)&addr, (socklen_t)sizeof(addr)));
+ return fd;
+}
+
+static int
+start_fd(int autoclose, int call_listen)
+{
+ struct sockaddr_un addr;
+ const char *autoclose_str = "-c";
+ char buf[512];
+ int rw[2], fd;
+ alarm(1);
+ if (!autoclose)
+ autoclose_str = NULL;
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ randomise(addr.sun_path, sizeof(addr.sun_path));
+ *addr.sun_path = '\0';
+ assert((fd = socket(PF_UNIX, SOCK_SEQPACKET, 0)) >= 0);
+ if (fd < 3) {
+ assert(dup2(fd, 9) == 9);
+ close(fd);
+ fd = 9;
+ }
+ assert(!bind(fd, (struct sockaddr *)&addr, (socklen_t)sizeof(addr)));
+ if (call_listen)
+ assert(!listen(fd, 1));
+ assert(!pipe(rw));
+ assert((pid = fork()) != -1);
+ if (!pid) {
+ alarm(1);
+ close(rw[0]);
+ pdeath();
+ if (rw[1] != STDOUT_FILENO) {
+ assert(dup2(rw[1], STDOUT_FILENO) == STDOUT_FILENO);
+ close(rw[1]);
+ }
+ sprintf(buf, "/dev/fd/%i", fd);
+ assert(!execl("./sbusd", "./sbusd", "-fa", buf, autoclose_str, NULL));
+ abort();
+ }
+ close(rw[1]);
+ assert(!read(rw[0], buf, sizeof(buf)));
+ close(rw[0]);
+ close(fd);
+ assert((fd = socket(PF_UNIX, SOCK_SEQPACKET, 0)) >= 0);
+ assert(!connect(fd, (struct sockaddr *)&addr, (socklen_t)sizeof(addr)));
+ return fd;
+}
+
+static int
+start_path(const char *path, int autoclose, int reuse, int group, int other)
+{
+ struct sockaddr_un addr;
+ char flags[8], *p;
+ const char *cflags;
+ int rw[2], fd;
+ alarm(1);
+ p = flags;
+ *p++ = '-';
+ if (autoclose)
+ *p++ = 'c';
+ if (reuse)
+ *p++ = 'r';
+ if (group)
+ *p++ = 'g';
+ if (other)
+ *p++ = 'o';
+ *p++ = '\0';
+ cflags = strlen(flags) > 1 ? flags : NULL;
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, path);
+ assert(!pipe(rw));
+ assert((pid = fork()) != -1);
+ if (!pid) {
+ alarm(1);
+ close(rw[0]);
+ pdeath();
+ if (rw[1] != STDOUT_FILENO) {
+ assert(dup2(rw[1], STDOUT_FILENO) == STDOUT_FILENO);
+ close(rw[1]);
+ }
+ assert(!execl("./sbusd", "./sbusd", "-fa", path, cflags, NULL));
+ abort();
+ }
+ close(rw[1]);
+ assert(!read(rw[0], &fd, 1));
+ close(rw[0]);
+ assert((fd = socket(PF_UNIX, SOCK_SEQPACKET, 0)) >= 0);
+ assert(!connect(fd, (struct sockaddr *)&addr, (socklen_t)sizeof(addr)));
+ return fd;
+}
+
+static void
+check_mode(const char *path, mode_t mode)
+{
+ struct stat st;
+ assert(!stat(path, &st));
+ assert((st.st_mode & 07777) == mode);
+}
+
+static void
+stop(int fd, int autoclose)
+{
+ int status;
+ if (!autoclose)
+ kill(pid, SIGINT);
+ else
+ close(fd);
+ assert(waitpid(pid, &status, 0) == pid);
+ assert(!status);
+ if (!autoclose)
+ close(fd);
+}
+
+static void
+check(int fd)
+{
+ char buf[LIBSBUS_BUFFER_SIZE], key[512], msg[512];
+ union libsbus_packet packet;
+ size_t i;
+
+ alarm(1);
+ assert(!libsbus_subscribe(fd, "test.", 0, buf));
+ assert(!libsbus_subscribe(fd, "discard", 0, buf));
+ assert(!libsbus_unsubscribe(fd, "discard", 0, buf));
+ assert(!libsbus_publish(fd, "discard", "not caught", strlen("not caught"), 0, buf));
+ for (i = 0; i < 100; i++) {
+ sprintf(key, "test.%zu", i);
+ sprintf(msg, "%zu", i);
+ assert(!libsbus_publish(fd, key, msg, strlen(msg), 0, buf));
+ }
+ for (i = 0; i < 100; i++) {
+ sprintf(key, "test.%zu", i);
+ sprintf(msg, "%zu", i);
+ assert(!libsbus_receive(fd, 0, buf, &packet));
+ assert(packet.type == LIBSBUS_MESSAGE);
+ assert(!strcmp(packet.message.key, key));
+ assert(packet.message.n == strlen(msg));
+ assert(!memcmp(packet.message.msg, msg, strlen(msg)));
+ }
+ assert(libsbus_receive(fd, MSG_DONTWAIT, buf, &packet) < 0);
+}
+
+int
+main(void)
+{
+ int fd, autoclose, status, reuse;
+ struct stat _st;
+ pid_t pid;
+
+ srand((unsigned)time(NULL));
+
+ for (autoclose = 0; autoclose < 2; autoclose++) {
+ fd = start_random(autoclose);
+ check(fd);
+ stop(fd, autoclose);
+
+ fd = start_abstract(autoclose);
+ check(fd);
+ stop(fd, autoclose);
+
+ fd = start_fd(autoclose, 0);
+ check(fd);
+ stop(fd, autoclose);
+
+ fd = start_fd(autoclose, 1);
+ check(fd);
+ stop(fd, autoclose);
+
+ fd = start_path(".test.sock", autoclose, 1, 0, 0);
+ check(fd);
+ stop(fd, autoclose);
+
+ assert(stat(".test.sock", &_st));
+ touch(".test.sock");
+
+ for (reuse = 2; reuse--;) {
+ fd = start_path(".test.sock", autoclose, reuse, 0, 0);
+ check_mode(".test.sock", 0700);
+ check(fd);
+ stop(fd, autoclose);
+
+ fd = start_path(".test.sock", autoclose, reuse, 1, 0);
+ check_mode(".test.sock", 0770);
+ check(fd);
+ stop(fd, autoclose);
+
+ fd = start_path(".test.sock", autoclose, reuse, 0, 1);
+ check_mode(".test.sock", 0707);
+ check(fd);
+ stop(fd, autoclose);
+
+ fd = start_path(".test.sock", autoclose, reuse, 1, 1);
+ check_mode(".test.sock", 0777);
+ check(fd);
+ stop(fd, autoclose);
+ }
+ }
+
+ touch(".test.sock");
+ assert((pid = fork()) != -1);
+ if (!pid) {
+ alarm(1);
+ pdeath();
+ fd = open("/dev/null", O_WRONLY);
+ if (fd >= 0 && fd != STDERR_FILENO)
+ dup2(fd, STDERR_FILENO);
+ assert(!execl("./sbusd", "./sbusd", "-fa.test.sock", NULL));
+ abort();
+ }
+ assert(waitpid(pid, &status, 0) == pid);
+ assert(status);
+ unlink(".test.sock");
+
+ return 0;
+}
+
+/* TODO untested sbusd flags: -p[/dev/null] (-f) -u */