diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 11 | ||||
-rw-r--r-- | sbusd.c | 27 | ||||
-rw-r--r-- | test.c | 356 |
4 files changed, 384 insertions, 13 deletions
@@ -5,4 +5,7 @@ *.so *.su *.out +*.pid +*.sock /sbusd +/test @@ -6,10 +6,12 @@ LIB_MINOR = 0 CONFIGFILE = config.mk include $(CONFIGFILE) -all: sbusd libsbus.so libsbus.a +all: sbusd libsbus.so libsbus.a test sbusd.o: arg.h libsbus.o: libsbus.h +test.o: libsbus.h +test: test.o libsbus.a libsbus.so: libsbus.o $(CC) -shared -Wl,-soname,libsbus.so.$(LIB_MAJOR) -o $@ $^ $(LDFLAGS) @@ -18,6 +20,9 @@ libsbus.a: libsbus.o $(AR) rc $@ $? $(AR) -s $@ +check: test sbusd + ./test + install: sbusd libsbus.a libsbus.so mkdir -p -- "$(DESTDIR)$(PREFIX)/bin" mkdir -p -- "$(DESTDIR)$(PREFIX)/lib" @@ -38,6 +43,6 @@ uninstall: -rm -rf -- "$(DESTDIR)$(PREFIX)/share/licenses/sbus" clean: - -rm -f -- sbusd *.o *.so *.a + -rm -f -- sbusd test *.o *.so *.a .test.sock .test.pid -.PHONY: all install uninstall clean +.PHONY: all check install uninstall clean @@ -41,6 +41,7 @@ static int had_client = 0; static struct sockaddr_un addr; static uid_t *users; static size_t nusers; +static const char *pidfile = "/run/sbus.pid"; static void usage(void) @@ -66,7 +67,11 @@ static void sigexit(int signo) { if (*addr.sun_path) - unlink(addr.sun_path); + if (unlink(addr.sun_path)) + weprintf("unlink %s:", addr.sun_path); + if (pidfile) + if (unlink(pidfile)) + weprintf("unlink %s:", pidfile); exit(0); (void) signo; } @@ -281,7 +286,7 @@ print_address(void) *p = '\0'; printf("/dev/unix/abstract/%s\n", buf); - if (ferror(stderr)) + if (fflush(stdout) || ferror(stdout)) eprintf("failed print generated address:"); } @@ -299,7 +304,7 @@ make_socket(const char *address, int reuse, mode_t mode) if (strstr(address, "/dev/fd/") == address) { p = &address[sizeof("/dev/fd/") - 1]; - if (isdigit(*p)) + if (!isdigit(*p)) goto def; errno = 0; tmp = strtol(p, &a, 10); @@ -391,7 +396,7 @@ bad_address: } static void -daemonise(const char *pidfile) +daemonise(void) { pid_t pid; int rw[2], status = 0, fd; @@ -416,7 +421,7 @@ daemonise(const char *pidfile) weprintf("signal SIGHUP SIG_IGN:"); if (signal(SIGINT, sigexit) == SIG_ERR) weprintf("signal SIGINT <exit>:"); - if (strcmp(pidfile, "/dev/null")) { + if (pidfile) { pid = getpid(); fd = open(pidfile, O_WRONLY | O_CREAT | O_EXCL, 0644); if (fd < 0) @@ -471,7 +476,6 @@ main(int argc, char *argv[]) { struct epoll_event evs[32]; const char *address = "/run/sbus.socket"; - const char *pidfile = "/run/sbus.pid"; int auto_close = 0; int foreground = 0; mode_t mode = 0700; @@ -529,14 +533,17 @@ main(int argc, char *argv[]) umask(0); server = make_socket(address, reuse_address, mode); if (foreground) { - close(0); - close(1); + close(STDIN_FILENO); + close(STDOUT_FILENO); if (signal(SIGHUP, sigexit) == SIG_ERR) weprintf("signal SIGHUP <exit>:"); if (signal(SIGINT, sigexit) == SIG_ERR) weprintf("signal SIGINT <exit>:"); + pidfile = NULL; } else { - daemonise(pidfile); + if (!strcmp(pidfile, "/dev/null")) + pidfile = NULL; + daemonise(); } if (nusers) @@ -568,5 +575,5 @@ main(int argc, char *argv[]) } } - return 0; + sigexit(0); } @@ -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 */ |