#include "libsbus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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); alarm(1); if (!pid) { 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 */ /* TODO test credentials */ /* TODO CMSG echo/{off,on} */