aboutsummaryrefslogblamecommitdiffstats
path: root/test.c
blob: daaec357420d8a2d410d44c7d18745e37fea364e (plain) (tree)





















                                                              
                                                              












































































































































































































































                                                                                                           
                                                        



                                                                                            
                                            



                                                                            
                                            



































































                                                                              
                 
                   














                                                                            
                           
                           
#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);
	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 test CMSG server */