about summary refs log tree commit diff
path: root/log.c
diff options
context:
space:
mode:
Diffstat (limited to 'log.c')
-rw-r--r--log.c104
1 files changed, 75 insertions, 29 deletions
diff --git a/log.c b/log.c
index c114c41..d6b3f2a 100644
--- a/log.c
+++ b/log.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 General Public License as published by
@@ -27,16 +27,60 @@
 
 #include <assert.h>
 #include <err.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <limits.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/stat.h>
 #include <sysexits.h>
 #include <time.h>
+#include <unistd.h>
+
+#ifdef __FreeBSD__
+#include <capsicum_helpers.h>
+#endif
 
 #include "chat.h"
 
-bool logEnable;
+static int logDir = -1;
+
+void logOpen(void) {
+	char buf[PATH_MAX];
+	int error = mkdir(dataPath(buf, sizeof(buf), "", 0), S_IRWXU);
+	if (error && errno != EEXIST) err(EX_CANTCREAT, "%s", buf);
+
+	error = mkdir(dataPath(buf, sizeof(buf), "log", 0), S_IRWXU);
+	if (error && errno != EEXIST) err(EX_CANTCREAT, "%s", buf);
+
+	logDir = open(buf, O_RDONLY | O_CLOEXEC);
+	if (logDir < 0) err(EX_CANTCREAT, "%s", buf);
+
+#ifdef __FreeBSD__
+	cap_rights_t rights;
+	cap_rights_init(
+		&rights, CAP_MKDIRAT, CAP_CREATE, CAP_WRITE,
+		/* for fdopen(3) */ CAP_FCNTL, CAP_FSTAT
+	);
+	error = caph_rights_limit(logDir, &rights);
+	if (error) err(EX_OSERR, "cap_rights_limit");
+#endif
+}
+
+static void logMkdir(const char *path) {
+	int error = mkdirat(logDir, path, S_IRWXU);
+	if (error && errno != EEXIST) err(EX_CANTCREAT, "log/%s", path);
+}
+
+static void sanitize(char *ptr, char *end) {
+	for (char *ch = ptr; ch < end && *ch == '.'; ++ch) {
+		*ch = '_';
+	}
+	for (char *ch = ptr; ch < end; ++ch) {
+		if (*ch == '/') *ch = '_';
+	}
+}
 
 static struct {
 	int year;
@@ -62,44 +106,46 @@ static FILE *logFile(uint id, const struct tm *tm) {
 	logs[id].month = tm->tm_mon;
 	logs[id].day = tm->tm_mday;
 
-	char path[PATH_MAX] = "log";
-	size_t len = strlen(path);
-	dataMkdir("");
-	dataMkdir(path);
+	char path[PATH_MAX];
+	char *ptr = path, *end = &path[sizeof(path)];
 
-	path[len++] = '/';
-	for (const char *ch = network.name; *ch; ++ch) {
-		path[len++] = (*ch == '/' ? '_' : *ch);
-	}
-	path[len] = '\0';
-	dataMkdir(path);
+	ptr = seprintf(ptr, end, "%s", network.name);
+	sanitize(path, ptr);
+	logMkdir(path);
 
-	path[len++] = '/';
-	for (const char *ch = idNames[id]; *ch; ++ch) {
-		path[len++] = (*ch == '/' ? '_' : *ch);
-	}
-	path[len] = '\0';
-	dataMkdir(path);
+	char *name = ptr;
+	ptr = seprintf(ptr, end, "/%s", idNames[id]);
+	sanitize(&name[1], ptr);
+	logMkdir(path);
+
+	size_t len = strftime(ptr, end - ptr, "/%F.log", tm);
+	if (!len) errx(EX_CANTCREAT, "log path too long");
 
-	strftime(&path[len], sizeof(path) - len, "/%F.log", tm);
-	logs[id].file = dataOpen(path, "a");
-	if (!logs[id].file) exit(EX_CANTCREAT);
+	int fd = openat(
+		logDir, path,
+		O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC,
+		S_IRUSR | S_IWUSR
+	);
+	if (fd < 0) err(EX_CANTCREAT, "log/%s", path);
+	logs[id].file = fdopen(fd, "a");
+	if (!logs[id].file) err(EX_OSERR, "fdopen");
 
 	setlinebuf(logs[id].file);
 	return logs[id].file;
 }
 
 void logClose(void) {
-	if (!logEnable) return;
+	if (logDir < 0) return;
 	for (uint id = 0; id < IDCap; ++id) {
 		if (!logs[id].file) continue;
 		int error = fclose(logs[id].file);
 		if (error) err(EX_IOERR, "%s", idNames[id]);
 	}
+	close(logDir);
 }
 
 void logFormat(uint id, const time_t *src, const char *format, ...) {
-	if (!logEnable) return;
+	if (logDir < 0) return;
 
 	time_t ts = (src ? *src : time(NULL));
 	struct tm *tm = localtime(&ts);
@@ -109,15 +155,15 @@ void logFormat(uint id, const time_t *src, const char *format, ...) {
 
 	char buf[sizeof("0000-00-00T00:00:00+0000")];
 	strftime(buf, sizeof(buf), "%FT%T%z", tm);
-	fprintf(file, "[%s] ", buf);
-	if (ferror(file)) err(EX_IOERR, "%s", idNames[id]);
+	int n = fprintf(file, "[%s] ", buf);
+	if (n < 0) err(EX_IOERR, "%s", idNames[id]);
 
 	va_list ap;
 	va_start(ap, format);
-	vfprintf(file, format, ap);
+	n = vfprintf(file, format, ap);
 	va_end(ap);
-	if (ferror(file)) err(EX_IOERR, "%s", idNames[id]);
+	if (n < 0) err(EX_IOERR, "%s", idNames[id]);
 
-	fprintf(file, "\n");
-	if (ferror(file)) err(EX_IOERR, "%s", idNames[id]);
+	n = fprintf(file, "\n");
+	if (n < 0) err(EX_IOERR, "%s", idNames[id]);
 }