about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Makefile47
-rw-r--r--catsit-timer.12
-rw-r--r--catsit-timer.c2
-rw-r--r--catsit-watch.12
-rw-r--r--catsit-watch.c2
-rw-r--r--catsit.827
-rw-r--r--catsit.conf.513
-rw-r--r--catsit.in14
-rw-r--r--catsitd.82
-rw-r--r--daemon.c107
-rw-r--r--daemon.h6
-rw-r--r--service.c6
12 files changed, 137 insertions, 93 deletions
diff --git a/Makefile b/Makefile
index 2f52b2f..a3b40b7 100644
--- a/Makefile
+++ b/Makefile
@@ -3,10 +3,10 @@ UNAME != uname
 PREFIX ?= /usr/local
 MANDIR ?= ${PREFIX}/man
 RUNDIR ?= /var/run
-.if ${UNAME} == OpenBSD
-ETCDIR ?= /etc
-.else
+.if ${UNAME} == FreeBSD
 ETCDIR ?= ${PREFIX}/etc
+.else
+ETCDIR ?= /etc
 .endif
 
 CFLAGS += -std=c99 -Wall -Wextra -Wpedantic
@@ -18,12 +18,9 @@ RC_SCRIPT = ${UNAME}/catsitd
 
 BINS = catsit-timer catsit-watch
 SBINS = catsit catsitd
-MAN1 = ${BINS:=.1}
-MAN5 = catsit.conf.5
-MAN8 = ${SBINS:=.8}
+MANS = ${BINS:=.1} catsit.conf.5 ${SBINS:=.8}
 
-OBJS += daemon.o
-OBJS += service.o
+OBJS = daemon.o service.o
 
 dev: tags all
 
@@ -46,34 +43,28 @@ tags: *.[ch]
 clean:
 	rm -f ${BINS} ${SBINS} ${OBJS} ${RC_SCRIPT} tags
 
-install: ${BINS} ${SBINS} ${RC_SCRIPT} ${MAN1} ${MAN5} ${MAN8}
+install: ${BINS} ${SBINS} ${MANS} ${RC_SCRIPT}
 	install -d ${DESTDIR}${PREFIX}/bin
 	install -d ${DESTDIR}${PREFIX}/sbin
-	install -d ${DESTDIR}${MANDIR}/man1
-	install -d ${DESTDIR}${MANDIR}/man5
-	install -d ${DESTDIR}${MANDIR}/man8
+.for sect in 1 5 8
+	install -d ${DESTDIR}${MANDIR}/man${sect}
+.endfor
 	install -d ${DESTDIR}${ETCDIR}/rc.d
 	install ${BINS} ${DESTDIR}${PREFIX}/bin
 	install ${SBINS} ${DESTDIR}${PREFIX}/sbin
+.for man in ${MANS}
+	install -m 444 ${man} ${DESTDIR}${MANDIR}/man${man:E}
+.endfor
 	install ${RC_SCRIPT} ${DESTDIR}${ETCDIR}/rc.d
-	install -m 644 ${MAN1} ${DESTDIR}${MANDIR}/man1
-	install -m 644 ${MAN5} ${DESTDIR}${MANDIR}/man5
-	install -m 644 ${MAN8} ${DESTDIR}${MANDIR}/man8
 
 uninstall:
-.for BIN in ${BINS}
-	rm -f ${DESTDIR}${PREFIX}/bin/${BIN}
-.endfor
-.for SBIN in ${SBINS}
-	rm -f ${DESTDIR}${PREFIX}/sbin/${SBIN}
+.for bin in ${BINS}
+	rm -f ${DESTDIR}${PREFIX}/bin/${bin}
 .endfor
-	rm -f ${DESTDIR}${ETCDIR}/rc.d/${RC_SCRIPT:T}
-.for MAN in ${MAN1}
-	rm -f ${DESTDIR}${MANDIR}/man1/${MAN}
+.for sbin in ${SBINS}
+	rm -f ${DESTDIR}${PREFIX}/sbin/${sbin}
 .endfor
-.for MAN in ${MAN5}
-	rm -f ${DESTDIR}${MANDIR}/man5/${MAN}
-.endfor
-.for MAN in ${MAN8}
-	rm -f ${DESTDIR}${MANDIR}/man8/${MAN}
+.for man in ${MANS}
+	rm -f ${DESTDIR}${MANDIR}/man${man:E}/${man}
 .endfor
+	rm -f ${DESTDIR}${ETCDIR}/rc.d/${RC_SCRIPT:T}
diff --git a/catsit-timer.1 b/catsit-timer.1
index 9ad8956..53f6024 100644
--- a/catsit-timer.1
+++ b/catsit-timer.1
@@ -59,4 +59,4 @@ exits with the same status.
 .Xr catsitd 8
 .
 .Sh AUTHORS
-.An June Bug Aq Mt june@causal.agency
+.An June McEnroe Aq Mt june@causal.agency
diff --git a/catsit-timer.c b/catsit-timer.c
index 2869311..1f754c3 100644
--- a/catsit-timer.c
+++ b/catsit-timer.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2021  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/catsit-watch.1 b/catsit-watch.1
index 5e97bdd..b5ccccd 100644
--- a/catsit-watch.1
+++ b/catsit-watch.1
@@ -61,4 +61,4 @@ exits with the same status.
 .Xr catsitd 8
 .
 .Sh AUTHORS
-.An June Bug Aq Mt june@causal.agency
+.An June McEnroe Aq Mt june@causal.agency
diff --git a/catsit-watch.c b/catsit-watch.c
index 096449b..1bee8cf 100644
--- a/catsit-watch.c
+++ b/catsit-watch.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2021  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2021  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
diff --git a/catsit.8 b/catsit.8
index 7448012..e52b188 100644
--- a/catsit.8
+++ b/catsit.8
@@ -1,4 +1,4 @@
-.Dd August 16, 2020
+.Dd September 28, 2021
 .Dt CATSIT 8
 .Os
 .
@@ -8,6 +8,7 @@
 .
 .Sh SYNOPSIS
 .Nm
+.Op Fl q
 .Op Fl c Ar control
 .Cm start|stop|restart|status|drop Ns | Ns Ar signal
 .Ar service ...
@@ -29,6 +30,15 @@ Communication with
 is unidirectional.
 The daemon logs any feedback
 with syslog.
+The
+.Nm
+utility waits
+a tenth of a second
+after sending the command
+then shows recent messages from
+.Xr catsitd 8
+in
+.Pa /var/log/messages .
 .
 .Pp
 The arguments are as follows:
@@ -36,6 +46,10 @@ The arguments are as follows:
 .It Fl c Ar control
 Set the path of the named pipe.
 .
+.It Fl q
+Do not show
+.Pa /var/log/messages .
+.
 .It Cm start
 Start any matching services
 which are not already started.
@@ -72,10 +86,9 @@ to the processes of any matching started services.
 Signal names are case-insensitive.
 .
 .It Ar service ...
-The list of services to operate on.
-Service names can include
-.Sy *?[]
-shell-style pattern operators.
+The list of services to operate on,
+using shell-style pattern matching as in
+.Xr glob 7 .
 Patterns must be quoted
 to be interpreted by
 .Xr catsitd 8
@@ -103,7 +116,7 @@ The default path of the named pipe.
 .
 .Sh EXAMPLES
 .Bd -literal
-catsit restart pounce/freenode
+catsit restart pounce/tilde
 catsit INFO 'pounce/*'
 .Ed
 .
@@ -111,4 +124,4 @@ catsit INFO 'pounce/*'
 .Xr catsitd 8
 .
 .Sh AUTHORS
-.An June Bug Aq Mt june@causal.agency
+.An June McEnroe Aq Mt june@causal.agency
diff --git a/catsit.conf.5 b/catsit.conf.5
index b7a45d4..e9de2ce 100644
--- a/catsit.conf.5
+++ b/catsit.conf.5
@@ -1,4 +1,4 @@
-.Dd March  1, 2021
+.Dd September 28, 2021
 .Dt CATSIT.CONF 5
 .Os
 .
@@ -13,6 +13,10 @@ file lists the services managed by the
 .Xr catsitd 8
 daemon.
 Leading whitespace is ignored.
+The current line
+can be extended over multiple lines
+using a backslash
+.Pq Ql \e .
 Each line of the file
 is one of the following:
 .
@@ -78,11 +82,12 @@ calico	calico -H irc.example.org $socks
 pounce	pounce -U $socks pounce.conf
 
 # Templating command lines using service names:
-pounce/freenode	pounce ${0#*/}.conf
 pounce/tilde	pounce ${0#*/}.conf
+pounce/libera	pounce ${0#*/}.conf
 
 # Privileged services:
-@scooper	kfcgi -d -U $USER -p ~/.local -- /bin/scooper
+@scooper	kfcgi -d -U $USER -p ~/.local -- \e
+		/bin/scooper /share/litterbox/litterbox.sqlite
 .Ed
 .
 .Sh SEE ALSO
@@ -91,4 +96,4 @@ pounce/tilde	pounce ${0#*/}.conf
 .Xr catsitd 8
 .
 .Sh AUTHORS
-.An June Bug Aq Mt june@causal.agency
+.An June McEnroe Aq Mt june@causal.agency
diff --git a/catsit.in b/catsit.in
index 6e934f1..0d28a1d 100644
--- a/catsit.in
+++ b/catsit.in
@@ -8,18 +8,20 @@ die() {
 
 : ${CATSITD_PIPE:='%%RUNDIR%%/catsitd.pipe'}
 
-while getopts 'c:' opt; do
-	case "${opt}" in
+quiet=
+while getopts 'c:q' opt; do
+	case $opt in
 		(c) CATSITD_PIPE=$OPTARG;;
+		(q) quiet=q;;
 		(?) exit 1;;
 	esac
 done
 shift $((OPTIND - 1))
 
-if ! [ -p "${CATSITD_PIPE}" ]; then
+if ! test -p "${CATSITD_PIPE}"; then
 	die "${CATSITD_PIPE} is not a named pipe"
 fi
-if ! [ -w "${CATSITD_PIPE}" ]; then
+if ! test -w "${CATSITD_PIPE}"; then
 	die "${CATSITD_PIPE} is not writable"
 fi
 
@@ -35,3 +37,7 @@ if [ "${action}" != "${valid}" ]; then
 fi
 
 echo "$@" > "${CATSITD_PIPE}"
+test $quiet && exit
+
+sleep 0.1
+tail /var/log/messages | grep catsitd
diff --git a/catsitd.8 b/catsitd.8
index fa1e67a..aaedbf0 100644
--- a/catsitd.8
+++ b/catsitd.8
@@ -223,4 +223,4 @@ used for service control.
 .Xr catsit 8
 .
 .Sh AUTHORS
-.An June Bug Aq Mt june@causal.agency
+.An June McEnroe Aq Mt june@causal.agency
diff --git a/daemon.c b/daemon.c
index f36e277..736fd08 100644
--- a/daemon.c
+++ b/daemon.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2020  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -49,6 +49,34 @@ static void signalHandler(int signal) {
 	signals[signal] = 1;
 }
 
+static ssize_t getlinecont(char **line, size_t *lcap, FILE *file) {
+	size_t cap = 0;
+	char *buf = NULL;
+	ssize_t llen = getline(line, lcap, file);
+	while (llen > 1 && (*line)[llen - 1] == '\n' && (*line)[llen - 2] == '\\') {
+		llen -= 2;
+		ssize_t len = getline(&buf, &cap, file);
+		if (len < 0) {
+			llen = -1;
+			break;
+		}
+		size_t req = llen + len + 1;
+		if (req > *lcap) {
+			char *ptr = realloc(*line, req);
+			if (!ptr) {
+				llen = -1;
+				break;
+			}
+			*line = ptr;
+			*lcap = req;
+		}
+		strlcpy(*line + llen, buf, *lcap - llen);
+		llen += len;
+	}
+	free(buf);
+	return llen;
+}
+
 static int parseConfig(const char *path) {
 	int ret = -1;
 	size_t cap = 0;
@@ -63,14 +91,15 @@ static int parseConfig(const char *path) {
 	prependClear();
 
 	int line = 1;
-	for (ssize_t len; 0 <= (len = getline(&buf, &cap, file)); ++line) {
+	for (ssize_t len; 0 <= (len = getlinecont(&buf, &cap, file)); ++line) {
 		if (buf[len - 1] == '\n') buf[len - 1] = '\0';
 
 		char *ptr = &buf[strspn(buf, WS)];
 		if (!ptr[0] || ptr[0] == '#') {
 			continue;
 		} else if (ptr[0] == '%') {
-			int error = prependAdd(&ptr[1]);
+			ptr++;
+			int error = prependAdd(&ptr[strspn(ptr, WS)]);
 			if (error) {
 				syslog(LOG_WARNING, "cannot add prepend command: %m");
 				goto err;
@@ -84,7 +113,7 @@ static int parseConfig(const char *path) {
 				);
 				goto err;
 			}
-			int error = serviceAdd(name, ptr);
+			int error = serviceAdd(name, &ptr[strspn(ptr, WS)]);
 			if (error) {
 				syslog(LOG_WARNING, "cannot add service: %m");
 				goto err;
@@ -215,30 +244,9 @@ int main(int argc, char *argv[]) {
 		}
 	}
 
-#ifdef __OpenBSD__
-	struct {
-		const char *path;
-		const char *mode;
-	} paths[] = {
-		{ fifoPath, "crw" },
-		{ configPath, "r" },
-		{ "/", "r" },
-		{ "/dev/null", "rw" },
-		{ serviceDir, "r" },
-		{ _PATH_BSHELL, "x" },
-		{ pidPath, "cw" },
-		{ NULL, NULL },
-	};
-	for (size_t i = 0; paths[i].path; ++i) {
-		error = unveil(paths[i].path, paths[i].mode);
-		if (error) err(EX_CANTCREAT, "%s", paths[i].path);
-	}
-	error = pledge(
-		"stdio cpath dpath rpath wpath flock getpw proc exec id", NULL
-	);
-	if (error) err(EX_OSERR, "pledge");
-#endif
-	
+	error = access(configPath, R_OK);
+	if (error) err(EX_NOINPUT, "%s", configPath);
+
 	error = access(serviceDir, X_OK);
 	if (error) err(EX_NOINPUT, "%s", serviceDir);
 
@@ -292,9 +300,6 @@ int main(int argc, char *argv[]) {
 	int writer = open(fifoPath, O_WRONLY | O_NONBLOCK | O_CLOEXEC);
 	if (writer < 0) err(EX_CANTCREAT, "%s", fifoPath);
 
-	error = parseConfig(configPath);
-	if (error) return EX_DATAERR;
-
 	if (daemonize) {
 		error = daemon(0, 0);
 		if (error) {
@@ -302,27 +307,47 @@ int main(int argc, char *argv[]) {
 			return EX_OSERR;
 		}
 	}
-	if (pidPath) {
-		int len = dprintf(pidFile, "%ju", (uintmax_t)getpid());
-		if (len < 0) syslog(LOG_WARNING, "%s: %m", pidPath);
-	}
 
 #ifdef __OpenBSD__
-	error = pledge("stdio cpath rpath proc exec id", NULL);
+	error = 0
+		|| unveil(fifoPath, "c")
+		|| unveil(configPath, "r")
+		|| unveil(serviceDir, "r")
+		|| unveil(_PATH_BSHELL, "x");
+	if (error) err(EX_OSERR, "unveil");
+	if (pidPath) {
+		error = unveil(pidPath, "c");
+		if (error) err(EX_OSERR, "unveil");
+	}
+	error = pledge("stdio rpath cpath proc exec id", NULL);
 	if (error) err(EX_OSERR, "pledge");
 #endif
 
+	if (pidPath) {
+		int len = dprintf(pidFile, "%ju", (uintmax_t)getpid());
+		if (len < 0) syslog(LOG_WARNING, "%s: %m", pidPath);
+	}
+
 	signal(SIGHUP, signalHandler);
 	signal(SIGINT, signalHandler);
 	signal(SIGTERM, signalHandler);
 	signal(SIGCHLD, signalHandler);
 	signal(SIGINFO, signalHandler);
 
+	parseConfig(configPath);
 	for (size_t i = 0; i < services.len; ++i) {
 		serviceStart(&services.ptr[i]);
 	}
 	setTitle();
 
+	struct pollfd *fds = calloc(1 + 2 * services.len, sizeof(*fds));
+	if (!fds) {
+		syslog(LOG_ERR, "calloc: %m");
+		goto shutdown;
+	}
+	fds[0].fd = fifo;
+	fds[0].events = POLLIN;
+
 	sigset_t mask;
 	sigemptyset(&mask);
 	for (;;) {
@@ -342,6 +367,11 @@ int main(int argc, char *argv[]) {
 		}
 		if (signals[SIGHUP]) {
 			parseConfig(configPath);
+			fds = reallocarray(fds, 1 + 2 * services.len, sizeof(*fds));
+			if (!fds) {
+				syslog(LOG_ERR, "reallocarray: %m");
+				goto shutdown;
+			}
 			setTitle();
 			signals[SIGHUP] = 0;
 		}
@@ -351,10 +381,6 @@ int main(int argc, char *argv[]) {
 			signals[SIGINFO] = 0;
 		}
 
-		struct pollfd fds[1 + 2 * services.len];
-		fds[0].fd = fifo;
-		fds[0].events = POLLIN;
-
 		struct timespec deadline = {0};
 		for (size_t i = 0; i < services.len; ++i) {
 			struct Service *service = &services.ptr[i];
@@ -425,6 +451,7 @@ int main(int argc, char *argv[]) {
 		}
 	}
 
+shutdown:
 	close(fifo);
 	unlink(fifoPath);
 
diff --git a/daemon.h b/daemon.h
index e86e43a..b68c51c 100644
--- a/daemon.h
+++ b/daemon.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2020  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -38,7 +38,9 @@ static inline void prependClear(void) {
 static inline int prependAdd(const char *command) {
 	if (prepend.len == prepend.cap) {
 		size_t cap = (prepend.cap ? prepend.cap * 2 : 8);
-		void *ptr = realloc(prepend.commands, sizeof(*prepend.commands) * cap);
+		void *ptr = reallocarray(
+			prepend.commands, cap, sizeof(*prepend.commands)
+		);
 		if (!ptr) return -1;
 		prepend.cap = cap;
 		prepend.commands = ptr;
diff --git a/service.c b/service.c
index 38049ff..2406175 100644
--- a/service.c
+++ b/service.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020  C. McEnroe <june@causal.agency>
+/* Copyright (C) 2020  June McEnroe <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -82,7 +82,7 @@ int serviceAdd(const char *name, const char *command) {
 
 	if (services.len == services.cap) {
 		size_t cap = (services.cap ? services.cap * 2 : 8);
-		void *ptr = realloc(services.ptr, sizeof(*services.ptr) * cap);
+		void *ptr = reallocarray(services.ptr, cap, sizeof(*services.ptr));
 		if (!ptr) return -1;
 		services.cap = cap;
 		services.ptr = ptr;
@@ -247,7 +247,7 @@ void serviceStart(struct Service *service) {
 	}
 	int n = snprintf(
 		&command[len], sizeof(command) - len, "%s%s",
-		(service->command[strcspn(service->command, ";&|()")] ? "exec " : ""),
+		(service->command[strcspn(service->command, ";&|()")] ? "" : "exec "),
 		service->command
 	);
 	assert(n > 0);