diff options
Diffstat (limited to 'libsbusd.c')
-rw-r--r-- | libsbusd.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/libsbusd.c b/libsbusd.c new file mode 100644 index 0000000..7c0596e --- /dev/null +++ b/libsbusd.c @@ -0,0 +1,342 @@ +/* See LICENSE file for copyright and license details. */ +#include "libsbusd.h" + +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/un.h> +#include <sys/wait.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <pwd.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + + +#define STYPE_MAX(T) (long long int)((1ULL << (8 * sizeof(T) - 1)) - 1) + + +extern char *argv0; + + +void +libsbusd_weprintf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + fprintf(stderr, "%s: ", argv0); + vfprintf(stderr, fmt, args); + if (strchr(fmt, '\0')[-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } + va_end(args); +} + +int +libsbusd_doessubmatch(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; + } +} + +int +libsbusd_issubed(char *const *subs, size_t nsubs, const char *key) +{ + while (nsubs--) + if (libsbusd_doessubmatch(subs[nsubs], key)) + return 1; + return 0; +} + +void +libsbusd_adduser(uid_t *users, size_t *nusers, const char *arg) +{ + struct passwd *user; + long long int tmp; + if (!isdigit(*arg)) + goto user_by_name; + errno = 0; + tmp = strtoll(arg, (void *)&arg, 10); + if (errno || *arg || tmp < 0 || tmp > STYPE_MAX(uid_t)) + goto user_by_name; + users[(*nusers)++] = (uid_t)tmp; + return; +user_by_name: + user = getpwnam(arg); + if (!user) + eprintf("getpwnam %s:", arg); + users[(*nusers)++] = user->pw_uid; +} + +static void +randomise(void *buf, size_t n) +{ + char *p = buf; + while (n--) + *p++ = rand(); +} + +static void +print_address(struct sockaddr_un *addr) +{ + 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 (fflush(stdout) || ferror(stdout)) + eprintf("failed print generated address:"); +} + +int +libsbusd_afunix(struct sockaddr_un *addr, int *fdp, const char *address) +{ + const char *p, *q; + long int tmp; + int hi, lo; + size_t n; + 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; + return -1; + } + *fdp = (int)tmp; + return LIBSBUS_AFUNIX_FD; + } else if (!strcmp(address, "/dev/unix/abstract")) { + return LIBSBUS_AFUNIX_RANDOM; + } 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; + return -1; + } + 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; + } + return LIBSBUS_AFUNIX_ABSTRACT; + } else { + def: + if (strlen(address) >= sizeof(addr->sun_path)) { + errno = ENAMETOOLONG; + return -1; + } + strcpy(addr->sun_path, address); + return LIBSBUS_AFUNIX_CONCRETE; + } +} + +int +libsbusd_mksocket(struct sockaddr_un *addr, const char *address, int reuse, mode_t mode) +{ + int fd, randaddr = 0, listening = 0; + + switch (libsbusd_afunix(addr, &fd, address)) { + case LIBSBUS_AFUNIX_FD: + reuse = 0; + break; + case LIBSBUS_AFUNIX_RANDOM: + randaddr = 1; + reuse = 0; + fd = -1; + break; + case LIBSBUS_AFUNIX_ABSTRACT: + reuse = 0; + fd = -1; + break; + case LIBSBUS_AFUNIX_CONCRETE: + fd = -1; + break; + default: + eprintf("bad unix socket address:"); + exit(1); + } + + 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(addr); + } 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; +} + +void +libsbusd_daemonise(const char *pidfile, void (*sigexit)(int)) +{ + 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 (pidfile) { + 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); + } + } +} + +void +libsbusd_initalise(int foreground, const char **pidfilep, void (*sigexit)(int)) +{ + if (foreground) { + 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>:"); + *pidfilep = NULL; + } else { + if (!strcmp(*pidfilep, "/dev/null")) + *pidfilep = NULL; + libsbusd_daemonise(*pidfilep, sigexit); + } +} |