aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile11
-rw-r--r--sbusd.c27
-rw-r--r--test.c356
4 files changed, 384 insertions, 13 deletions
diff --git a/.gitignore b/.gitignore
index bb72c58..08db4ed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,7 @@
*.so
*.su
*.out
+*.pid
+*.sock
/sbusd
+/test
diff --git a/Makefile b/Makefile
index 65566cb..5c112a2 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/sbusd.c b/sbusd.c
index 6367004..8e4a060 100644
--- a/sbusd.c
+++ b/sbusd.c
@@ -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);
}
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 */