aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2017-10-21 17:37:24 +0200
committerMattias Andrée <maandree@kth.se>2017-10-21 17:37:24 +0200
commitf71f61f797ff7c4df8c3d057231567c4b8f53d8a (patch)
treeca62154b68e9a124d618875d10d9e61abc509090
downloadsbus-f71f61f797ff7c4df8c3d057231567c4b8f53d8a.tar.gz
sbus-f71f61f797ff7c4df8c3d057231567c4b8f53d8a.tar.bz2
sbus-f71f61f797ff7c4df8c3d057231567c4b8f53d8a.tar.xz
First commit
Signed-off-by: Mattias Andrée <maandree@kth.se>
-rw-r--r--.gitignore8
-rw-r--r--LICENSE15
-rw-r--r--Makefile43
-rw-r--r--arg.h45
-rw-r--r--config.mk6
-rw-r--r--libsbus.c86
-rw-r--r--libsbus.h38
-rw-r--r--sbusd.c572
8 files changed, 813 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bb72c58
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+*~
+*\#*
+*.o
+*.a
+*.so
+*.su
+*.out
+/sbusd
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..2553a69
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+© 2017 Mattias Andrée <maandree@kth.se>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..65566cb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,43 @@
+.POSIX:
+
+LIB_MAJOR = 1
+LIB_MINOR = 0
+
+CONFIGFILE = config.mk
+include $(CONFIGFILE)
+
+all: sbusd libsbus.so libsbus.a
+
+sbusd.o: arg.h
+libsbus.o: libsbus.h
+
+libsbus.so: libsbus.o
+ $(CC) -shared -Wl,-soname,libsbus.so.$(LIB_MAJOR) -o $@ $^ $(LDFLAGS)
+
+libsbus.a: libsbus.o
+ $(AR) rc $@ $?
+ $(AR) -s $@
+
+install: sbusd libsbus.a libsbus.so
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/bin"
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/lib"
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/share/licenses/sbus"
+ cp -- sbusd "$(DESTDIR)$(PREFIX)/bin/"
+ cp -- libsbus.a "$(DESTDIR)$(PREFIX)/lib/"
+ cp -- libsbus.so "$(DESTDIR)$(PREFIX)/lib/libsbus.so.$(LIB_MAJOR)"
+ ln -sf -- libsbus.so.$(LIB_MAJOR) "$(DESTDIR)$(PREFIX)/lib/libsbus.so"
+ ln -sf -- libsbus.so.$(LIB_MAJOR) "$(DESTDIR)$(PREFIX)/lib/libsbus.so.$(LIB_MAJOR).$(LIB_MINOR)"
+ cp -- LICENSE "$(DESTDIR)$(PREFIX)/share/licenses/sbus/"
+
+uninstall:
+ -rm -f -- "$(DESTDIR)$(PREFIX)/bin/sbusd"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libsbus.a"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libsbus.so"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libsbus.so.$(LIB_MAJOR)"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libsbus.so.$(LIB_MAJOR).$(LIB_MINOR)"
+ -rm -rf -- "$(DESTDIR)$(PREFIX)/share/licenses/sbus"
+
+clean:
+ -rm -f -- sbusd *.o *.so *.a
+
+.PHONY: all install uninstall clean
diff --git a/arg.h b/arg.h
new file mode 100644
index 0000000..81fd8fa
--- /dev/null
+++ b/arg.h
@@ -0,0 +1,45 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef ARG_H__
+#define ARG_H__
+
+extern char *argv0;
+
+/* use main(int argc, char *argv[]) */
+#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
+ argv[0] && argv[0][0] && argv[0][1];\
+ argc--, argv++) {\
+ char argc_;\
+ char **argv_;\
+ int brk_;\
+ if (argv[0][0] == '-') {\
+ if (argv[0][1] == '-' && argv[0][2] == '\0') {\
+ argv++;\
+ argc--;\
+ break;\
+ }\
+ for (brk_ = 0, argv[0]++, argv_ = argv;\
+ argv[0][0] && !brk_;\
+ argv[0]++) {\
+ if (argv_ != argv)\
+ break;\
+ argc_ = argv[0][0];\
+ switch (argc_)
+
+#define ARGEND }\
+ } else {\
+ break;\
+ }\
+ }
+
+#define EARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\
+ (usage(), (char *)0) :\
+ (brk_ = 1, (argv[0][1] != '\0')?\
+ (&argv[0][1]) :\
+ (argc--, argv++, argv[0])))
+
+
+#endif
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..44b4bfc
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,6 @@
+PREFIX = /usr/local
+MANPREFIX = $(PREFIX)/share/man
+
+CFLAGS = -std=c99 -Wall -Wextra -O2 -fPIC
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE
+LDFLAGS = -s
diff --git a/libsbus.c b/libsbus.c
new file mode 100644
index 0000000..bbd0b4f
--- /dev/null
+++ b/libsbus.c
@@ -0,0 +1,86 @@
+/* See LICENSE file for copyright and license details. */
+#include "libsbus.h"
+#include <sys/socket.h>
+#include <errno.h>
+#include <string.h>
+
+int
+libsbus_subscribe(int fd, const char *pattern, char *buf)
+{
+ size_t n = strlen(pattern);
+ if (n + 4 > LIBSBUS_BUFFER_SIZE) {
+ errno = EMSGSIZE;
+ return -1;
+ }
+ buf[0] = 'S', buf[1] = 'U', buf[2] = 'B', buf[3] = ' ';
+ memcpy(&buf[4], pattern, n);
+ return -(send(fd, buf, n + 4, 0) < 0);
+}
+
+int
+libsbus_unsubscribe(int fd, const char *pattern, char *buf)
+{
+ size_t n = strlen(pattern);
+ if (n + 6 > LIBSBUS_BUFFER_SIZE) {
+ errno = EMSGSIZE;
+ return -1;
+ }
+ buf[0] = 'U', buf[1] = 'N', buf[2] = 'S', buf[3] = 'U', buf[4] = 'B', buf[5] = ' ';
+ memcpy(&buf[6], pattern, n);
+ return -(send(fd, buf, n + 6, 0) < 0);
+}
+
+int
+libsbus_publish(int fd, const char *key, const char *msg, size_t n, char *buf)
+{
+ size_t len = strlen(key) + 1;
+ if (len + n > LIBSBUS_BUFFER_SIZE - 4) {
+ errno = EMSGSIZE;
+ return -1;
+ }
+ buf[0] = 'M', buf[1] = 'S', buf[2] = 'G', buf[3] = ' ';
+ memcpy(&buf[4], key, len);
+ memcpy(&buf[4 + len], msg, n);
+ return -(send(fd, buf, len + n + 4, 0) < 0);
+}
+
+ssize_t
+libsbuf_prepare_message(const char *key, char *buf, size_t *remaining)
+{
+ size_t len = strlen(key) + 1;
+ if (len > LIBSBUS_BUFFER_SIZE - 4) {
+ errno = EMSGSIZE;
+ return -1;
+ }
+ buf[0] = 'M', buf[1] = 'S', buf[2] = 'G', buf[3] = ' ';
+ memcpy(&buf[4], key, len);
+ len += 4;
+ *remaining = LIBSBUS_BUFFER_SIZE - len;
+ return (ssize_t)len;
+}
+
+int
+libsbus_receive(int fd, char *buf, union libsbus_packet *packet)
+{
+ ssize_t r;
+ char *p;
+
+ r = recv(fd, buf, LIBSBUS_BUFFER_SIZE, 0);
+ if (r < 0)
+ return -1;
+
+ if (!strncmp(buf, "MSG ", 4)) {
+ p = memchr(buf, '\0', r);
+ if (!*p++)
+ goto unknown;
+ packet->type = LIBSBUS_MESSAGE;
+ packet->message.key = &buf[4];
+ packet->message.msg = p;
+ packet->message.n = (size_t)(r - (p - buf));
+ } else {
+ unknown:
+ packet->type = LIBSBUS_UNKNOWN;
+ packet->unknown.n = (size_t)r;
+ }
+ return 0;
+}
diff --git a/libsbus.h b/libsbus.h
new file mode 100644
index 0000000..98bf745
--- /dev/null
+++ b/libsbus.h
@@ -0,0 +1,38 @@
+/* See LICENSE file for copyright and license details. */
+#ifndef LIBSBUS_H
+#define LIBSBUS_H
+
+#include <stdlib.h>
+
+#define LIBSBUS_BUFFER_SIZE ((3 << 17) - 1)
+
+enum libsbus_packet_type {
+ LIBSBUS_UNKNOWN,
+ LIBSBUS_MESSAGE
+};
+
+struct libsbus_unknown {
+ enum libsbus_packet_type type;
+ size_t n;
+};
+
+struct libsbus_message {
+ enum libsbus_packet_type type;
+ char *key;
+ char *msg;
+ size_t n;
+};
+
+union libsbus_packet {
+ enum libsbus_packet_type type;
+ struct libsbus_unknown unknown;
+ struct libsbus_message message;
+};
+
+int libsbus_subscribe(int fd, const char *pattern, char *buf);
+int libsbus_unsubscribe(int fd, const char *pattern, char *buf);
+int libsbus_publish(int fd, const char *key, const char *msg, size_t n, char *buf);
+ssize_t libsbuf_prepare_message(const char *key, char *buf, size_t *remaining);
+int libsbus_receive(int fd, char *buf, union libsbus_packet *packet);
+
+#endif
diff --git a/sbusd.c b/sbusd.c
new file mode 100644
index 0000000..6367004
--- /dev/null
+++ b/sbusd.c
@@ -0,0 +1,572 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <alloca.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stropts.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "arg.h"
+
+#define STYPE_MAX(T) (long long int)((1ULL << (8 * sizeof(T) - 1)) - 1)
+#define eprintf(...) (weprintf(__VA_ARGS__), exit(1))
+
+struct client {
+ int fd;
+ char **subs;
+ size_t nsubs;
+ size_t subs_siz;
+ struct client *prev;
+ struct client *next;
+};
+
+char *argv0;
+static struct client head;
+static struct client tail;
+static int epfd;
+static int had_client = 0;
+static struct sockaddr_un addr;
+static uid_t *users;
+static size_t nusers;
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-a address] [-f | -p pidfile] [-u user] ... [-cgor]\n", argv0);
+ exit(1);
+}
+
+static void
+weprintf(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ if (strchr(fmt, '\0')[-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ }
+ va_end(args);
+}
+
+static void
+sigexit(int signo)
+{
+ if (*addr.sun_path)
+ unlink(addr.sun_path);
+ exit(0);
+ (void) signo;
+}
+
+static struct client *
+add_client(int fd)
+{
+ struct client *cl;
+ cl = malloc(sizeof(*cl));
+ if (!cl)
+ return NULL;
+ cl->fd = fd;
+ cl->subs = NULL;
+ cl->nsubs = 0;
+ cl->subs_siz = 0;
+ cl->next = &tail;
+ cl->prev = tail.prev;
+ tail.prev->next = cl;
+ tail.prev = cl;
+ return cl;
+}
+
+static void
+remove_client(struct client *cl)
+{
+ close(cl->fd);
+ cl->prev->next = cl->next;
+ cl->next->prev = cl->prev;
+ while (cl->nsubs--)
+ free(cl->subs[cl->nsubs]);
+ free(cl->subs);
+ free(cl);
+}
+
+static void
+accept_client(int fd)
+{
+ struct ucred cred;
+ struct epoll_event ev;
+ size_t i;
+ if (fd < 0) {
+ weprintf("accept <server>:");
+ return;
+ }
+ if (nusers) {
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &(socklen_t){sizeof(cred)}) < 0) {
+ weprintf("getsockopt <client> SOL_SOCKET SO_PEERCRED:");
+ close(fd);
+ return;
+ }
+ for (i = nusers; i--;)
+ if (users[i] == cred.uid)
+ goto cred_ok;
+ weprintf("rejected connection from user %li\n", (long int)cred.uid);
+ close(fd);
+ return;
+ }
+cred_ok:
+ ev.events = EPOLLIN | EPOLLRDHUP;
+ ev.data.ptr = add_client(fd);
+ if (!ev.data.ptr) {
+ close(fd);
+ } else if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev)) {
+ weprintf("epoll_ctl EPOLL_CTL_ADD <client>:");
+ remove_client((void *)ev.data.ptr);
+ } else {
+ had_client = 1;
+ }
+}
+
+static int
+is_subscription_match(const char *sub, const char *key)
+{
+ const char *sub_start = sub;
+ for (;;) {
+ while (*sub && *sub == *key) {
+ sub++;
+ key++;
+ }
+ if (!*key)
+ return !*sub;
+ if (!*sub)
+ return sub == sub_start || sub[-1] == '.';
+ if (*sub == '*') {
+ sub++;
+ while (*key && *key != '.')
+ key++;
+ continue;
+ }
+ return 0;
+ }
+}
+
+static int
+is_subscribed(const struct client *cl, const char *key)
+{
+ size_t i = cl->nsubs;
+ while (i--)
+ if (is_subscription_match(cl->subs[i], key))
+ return 1;
+ return 0;
+}
+
+static void
+add_subscription(struct client *cl, const char *key)
+{
+ size_t n;
+ char **new, *k;
+ if (cl->subs_siz == cl->nsubs) {
+ n = cl->subs_siz ? (cl->subs_siz << 1) : 1;
+ new = realloc(cl->subs, n * sizeof(char *));
+ if (!new) {
+ weprintf("realloc:");
+ remove_client(cl);
+ return;
+ }
+ cl->subs = new;
+ cl->subs_siz = n;
+ }
+ k = strdup(key);
+ if (!k) {
+ weprintf("strdup:");
+ remove_client(cl);
+ return;
+ }
+ cl->subs[cl->nsubs++] = k;
+}
+
+static void
+remove_subscription(struct client *cl, const char *key)
+{
+ size_t i = cl->nsubs;
+ char **new;
+ while (i--) {
+ if (!strcmp(key, cl->subs[i])) {
+ free(cl->subs[i]);
+ memmove(&cl->subs[i], &cl->subs[i + 1], --(cl->nsubs) - i);
+ if (cl->subs_siz >= 4 * cl->nsubs) {
+ new = realloc(cl->subs, cl->nsubs * sizeof(char *));
+ if (new) {
+ cl->subs_siz = cl->nsubs;
+ cl->subs = new;
+ }
+ }
+ break;
+ }
+ }
+}
+
+static void
+broadcast(const char *msg, size_t n)
+{
+ struct client *cl = head.next, *tmp;
+ for (; cl->next; cl = cl->next) {
+ if (!is_subscribed(cl, &msg[4]))
+ continue;
+ if (send(cl->fd, msg, n, 0) < 0) { /* TODO queue instead of block */
+ cl = (tmp = cl)->prev;
+ weprintf("send <client>:");
+ remove_client(tmp);
+ }
+ }
+}
+
+static void
+handle_message(struct client *cl)
+{
+ static char buf[3 << 17];
+ int fd = cl->fd;
+ ssize_t r;
+
+ r = recv(fd, buf, sizeof(buf) - 1, 0);
+ if (r < 0) {
+ weprintf("recv <client>:");
+ remove_client(cl);
+ return;
+ }
+ buf[r] = '\0';
+
+ if (!strncmp(buf, "MSG ", 4)) {
+ broadcast(buf, r);
+ } else if (!strncmp(buf, "UNSUB ", 6)) {
+ remove_subscription(cl, &buf[6]);
+ } else if (!strncmp(buf, "SUB ", 4)) {
+ add_subscription(cl, &buf[4]);
+ } else {
+ weprintf("received bad message\n");
+ remove_client(cl);
+ }
+}
+
+static void
+randomise(void *buf, size_t n)
+{
+ char *p = buf;
+ while (n--)
+ *p++ = rand();
+}
+
+static void
+print_address(void)
+{
+ char buf[2 * sizeof(addr.sun_path) + 1];
+ char *p = buf;
+ const unsigned char *a = (const unsigned char *)addr.sun_path;
+ size_t n = sizeof(addr.sun_path);
+
+ for (; n--; p += 2, a += 1) {
+ p[0] = "0123456789abcdef"[(int)*a >> 4];
+ p[1] = "0123456789abcdef"[(int)*a & 15];
+ }
+ *p = '\0';
+
+ printf("/dev/unix/abstract/%s\n", buf);
+ if (ferror(stderr))
+ eprintf("failed print generated address:");
+}
+
+static int
+make_socket(const char *address, int reuse, mode_t mode)
+{
+ int fd = -1, randaddr = 0, hi, lo, listening = 0;
+ long int tmp;
+ size_t n;
+ const char *p, *q;
+ char *a;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+
+ if (strstr(address, "/dev/fd/") == address) {
+ p = &address[sizeof("/dev/fd/") - 1];
+ if (isdigit(*p))
+ goto def;
+ errno = 0;
+ tmp = strtol(p, &a, 10);
+ if (errno || *a || tmp < 0) {
+ errno = 0;
+ goto def;
+ }
+ if (tmp > INT_MAX) {
+ errno = EBADF;
+ goto bad_address;
+ }
+ fd = (int)tmp;
+ reuse = 0;
+ } else if (!strcmp(address, "/dev/unix/abstract")) {
+ randaddr = 1;
+ reuse = 0;
+ } else if (strstr(address, "/dev/unix/abstract/") == address) {
+ p = &address[sizeof("/dev/unix/abstract/") - 1];
+ n = strlen(p);
+ if (n & 1)
+ goto def;
+ for (q = p; *q; q++)
+ if (!isxdigit(*q))
+ goto def;
+ if (n > sizeof(addr.sun_path) * 2) {
+ errno = ENAMETOOLONG;
+ goto bad_address;
+ }
+ a = addr.sun_path;
+ for (; *p; p += 2) {
+ hi = (p[0] & 15) + 9 * !isdigit(p[0]);
+ lo = (p[1] & 15) + 9 * !isdigit(p[1]);
+ *a++ = (hi << 4) | lo;
+ }
+ reuse = 0;
+ } else {
+ def:
+ if (strlen(address) >= sizeof(addr.sun_path)) {
+ errno = ENAMETOOLONG;
+ goto bad_address;
+ }
+ strcpy(addr.sun_path, address);
+ }
+
+ if (reuse)
+ unlink(addr.sun_path);
+
+ if (fd < 0) {
+ fd = socket(PF_UNIX, SOCK_SEQPACKET, 0);
+ if (fd < 0)
+ eprintf("socket PF_UNIX SOCK_SEQPACKET:");
+ if (fchmod(fd, mode))
+ eprintf("fchmod <socket> %o:", mode);
+ if (randaddr) {
+ srand((unsigned)time(NULL));
+ for (;;) {
+ randomise(&addr.sun_path[1], sizeof(addr.sun_path) - 1);
+ if (!bind(fd, (void *)&addr, sizeof(addr)))
+ break;
+ else if (errno != EADDRINUSE)
+ eprintf("bind <random abstract address>:");
+ }
+ print_address();
+ } else {
+ if (bind(fd, (void *)&addr, sizeof(addr))) {
+ if (*addr.sun_path)
+ eprintf("bind %s:", addr.sun_path);
+ else
+ eprintf("bind <abstract:%s>:", &address[sizeof("/dev/unix/abstract/") - 1]);
+ }
+ }
+ } else {
+ if (mode & 0070)
+ weprintf("ignoring -g due to using passed down socket\n");
+ if (mode & 0007)
+ weprintf("ignoring -o due to using passed down socket\n");
+ if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &listening, &(socklen_t){sizeof(listening)}))
+ eprintf("getsockopt SOL_SOCKET SO_ACCEPTCONN:");
+ }
+
+ if (!listening && listen(fd, SOMAXCONN))
+ eprintf("listen:");
+
+ return fd;
+
+bad_address:
+ eprintf("bad unix socket address:");
+ exit(1);
+}
+
+static void
+daemonise(const char *pidfile)
+{
+ pid_t pid;
+ int rw[2], status = 0, fd;
+ FILE *fp;
+
+ if (pipe(rw))
+ eprintf("pipe:");
+
+ switch ((pid = fork())) {
+ case -1:
+ eprintf("fork:");
+
+ case 0:
+ close(rw[0]);
+ setsid();
+ switch (fork()) {
+ case -1:
+ eprintf("fork:");
+
+ case 0:
+ if (signal(SIGHUP, SIG_IGN) == SIG_ERR)
+ weprintf("signal SIGHUP SIG_IGN:");
+ if (signal(SIGINT, sigexit) == SIG_ERR)
+ weprintf("signal SIGINT <exit>:");
+ if (strcmp(pidfile, "/dev/null")) {
+ pid = getpid();
+ fd = open(pidfile, O_WRONLY | O_CREAT | O_EXCL, 0644);
+ if (fd < 0)
+ eprintf("open %s O_WRONLY O_CREAT O_EXCL:", pidfile);
+ fp = fdopen(fd, "w");
+ fprintf(fp, "%li\n", (long int)pid);
+ if (fflush(fp) || ferror(fp))
+ eprintf("fprintf %s:", pidfile);
+ fclose(fp);
+ }
+ if (chdir("/"))
+ eprintf("chdir /:");
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ if (isatty(STDERR_FILENO)) {
+ fd = open("/dev/null", O_WRONLY);
+ if (fd)
+ eprintf("open /dev/null O_WRONLY:");
+ if (dup2(fd, STDERR_FILENO) != STDERR_FILENO)
+ eprintf("dup2 /dev/null /dev/stderr:");
+ close(fd);
+ }
+ if (write(rw[1], &status, 1) < 1)
+ eprintf("write <pipe>:");
+ close(rw[1]);
+ break;
+
+ default:
+ exit(0);
+ }
+ break;
+
+ default:
+ close(rw[1]);
+ if (waitpid(pid, &status, 0) != pid)
+ eprintf("waitpid:");
+ if (status)
+ exit(1);
+ switch (read(rw[0], &status, 1)) {
+ case -1:
+ eprintf("read <pipe>:");
+ case 0:
+ exit(1);
+ default:
+ exit(0);
+ }
+ }
+}
+
+int
+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;
+ int reuse_address = 0;
+ struct passwd *user;
+ int server, n;
+ long long int tmp;
+ char *arg;
+
+ users = alloca(argc * sizeof(*users));
+
+ ARGBEGIN {
+ case 'a':
+ address = EARGF();
+ break;
+ case 'c':
+ auto_close = 1;
+ break;
+ case 'f':
+ foreground = 1;
+ break;
+ case 'g':
+ mode |= 0070;
+ break;
+ case 'o':
+ mode |= 0007;
+ break;
+ case 'p':
+ pidfile = EARGF();
+ break;
+ case 'r':
+ reuse_address = 1;
+ break;
+ case 'u':
+ arg = EARGF();
+ if (!isdigit(*arg))
+ goto user_by_name;
+ errno = 0;
+ tmp = strtoll(arg, &arg, 10);
+ if (errno || *arg || tmp < 0 || tmp > STYPE_MAX(uid_t))
+ goto user_by_name;
+ users[nusers++] = (uid_t)tmp;
+ user_by_name:
+ user = getpwnam(arg);
+ if (!user)
+ eprintf("getpwnam %s:", arg);
+ users[nusers++] = user->pw_uid;
+ break;
+ default:
+ usage();
+ } ARGEND;
+ if (argc)
+ usage();
+
+ umask(0);
+ server = make_socket(address, reuse_address, mode);
+ if (foreground) {
+ close(0);
+ close(1);
+ if (signal(SIGHUP, sigexit) == SIG_ERR)
+ weprintf("signal SIGHUP <exit>:");
+ if (signal(SIGINT, sigexit) == SIG_ERR)
+ weprintf("signal SIGINT <exit>:");
+ } else {
+ daemonise(pidfile);
+ }
+
+ if (nusers)
+ users[nusers++] = getuid();
+
+ head.next = &tail;
+ tail.prev = &head;
+
+ epfd = epoll_create1(0);
+ if (epfd < 0)
+ eprintf("epoll_create1:");
+
+ evs->events = EPOLLIN;
+ evs->data.ptr = NULL;
+ if (epoll_ctl(epfd, EPOLL_CTL_ADD, server, evs))
+ eprintf("epoll_ctl EPOLL_CTL_ADD <socket>:");
+
+ while (!auto_close || !had_client || head.next->next) {
+ n = epoll_wait(epfd, evs, sizeof(evs) / sizeof(*evs), -1);
+ if (n < 0)
+ eprintf("epoll_wait:");
+ while (n--) {
+ if (!evs[n].data.ptr)
+ accept_client(accept(server, NULL, NULL));
+ else if (evs[n].events & (EPOLLRDHUP | EPOLLHUP))
+ remove_client((void *)evs[n].data.ptr);
+ else
+ handle_message((void *)evs[n].data.ptr);
+ }
+ }
+
+ return 0;
+}