diff options
Diffstat (limited to 'log.c')
-rw-r--r-- | log.c | 224 |
1 files changed, 118 insertions, 106 deletions
diff --git a/log.c b/log.c index 2681bac..d6b3f2a 100644 --- a/log.c +++ b/log.c @@ -1,157 +1,169 @@ -/* Copyright (C) 2018 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 + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify this Program, or any covered work, by linking or + * combining it with OpenSSL (or a modified version of that library), + * containing parts covered by the terms of the OpenSSL License and the + * original SSLeay license, the licensors of this Program grant you + * additional permission to convey the resulting work. Corresponding + * Source for a non-source form of such a combination shall include the + * source code for the parts of OpenSSL used as well as that of the + * covered work. */ +#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 <string.h> #include <sys/stat.h> #include <sysexits.h> #include <time.h> +#include <unistd.h> + +#ifdef __FreeBSD__ +#include <capsicum_helpers.h> +#endif #include "chat.h" -static int logRoot = -1; +static int logDir = -1; -static struct Log { - int dir; - int year; - int month; - int day; - FILE *file; -} logs[TagsLen]; +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); -void logOpen(const char *path) { - logRoot = open(path, O_RDONLY | O_CLOEXEC); - if (logRoot < 0) err(EX_CANTCREAT, "%s", path); +#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 *name) { - for (; name[0]; ++name) { - if (name[0] == '/') name[0] = '_'; +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 FILE *logFile(struct Tag tag, const struct tm *time) { - struct Log *log = &logs[tag.id]; - if ( - log->file - && log->year == time->tm_year - && log->month == time->tm_mon - && log->day == time->tm_mday - ) return log->file; +static struct { + int year; + int month; + int day; + FILE *file; +} logs[IDCap]; - if (log->file) { - fclose(log->file); +static FILE *logFile(uint id, const struct tm *tm) { + if ( + logs[id].file && + logs[id].year == tm->tm_year && + logs[id].month == tm->tm_mon && + logs[id].day == tm->tm_mday + ) return logs[id].file; + + if (logs[id].file) { + int error = fclose(logs[id].file); + if (error) err(EX_IOERR, "%s", idNames[id]); + } - } else { - char *name = strdup(tag.name); - if (!name) err(EX_OSERR, "strdup"); - sanitize(name); + logs[id].year = tm->tm_year; + logs[id].month = tm->tm_mon; + logs[id].day = tm->tm_mday; - int error = mkdirat(logRoot, name, 0700); - if (error && errno != EEXIST) err(EX_CANTCREAT, "%s", name); + char path[PATH_MAX]; + char *ptr = path, *end = &path[sizeof(path)]; - log->dir = openat(logRoot, name, O_RDONLY | O_CLOEXEC); - if (log->dir < 0) err(EX_CANTCREAT, "%s", name); + ptr = seprintf(ptr, end, "%s", network.name); + sanitize(path, ptr); + logMkdir(path); - free(name); - } + char *name = ptr; + ptr = seprintf(ptr, end, "/%s", idNames[id]); + sanitize(&name[1], ptr); + logMkdir(path); - log->year = time->tm_year; - log->month = time->tm_mon; - log->day = time->tm_mday; + size_t len = strftime(ptr, end - ptr, "/%F.log", tm); + if (!len) errx(EX_CANTCREAT, "log path too long"); - char path[sizeof("YYYY-MM-DD.log")]; - strftime(path, sizeof(path), "%F.log", time); int fd = openat( - log->dir, path, O_RDWR | O_APPEND | O_CREAT | O_CLOEXEC, 0600 + logDir, path, + O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, + S_IRUSR | S_IWUSR ); - if (fd < 0) err(EX_CANTCREAT, "%s/%s", tag.name, path); + if (fd < 0) err(EX_CANTCREAT, "log/%s", path); + logs[id].file = fdopen(fd, "a"); + if (!logs[id].file) err(EX_OSERR, "fdopen"); - log->file = fdopen(fd, "a+"); - if (!log->file) err(EX_CANTCREAT, "%s/%s", tag.name, path); - setlinebuf(log->file); - - return log->file; + setlinebuf(logs[id].file); + return logs[id].file; } -enum { StampLen = sizeof("YYYY-MM-DDThh:mm:ss+hhmm") - 1 }; - -void logFmt(struct Tag tag, const time_t *ts, const char *format, ...) { - if (logRoot < 0) return; - - time_t t; - if (!ts) { - t = time(NULL); - ts = &t; +void logClose(void) { + 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); +} - struct tm *time = localtime(ts); - if (!time) err(EX_SOFTWARE, "localtime"); +void logFormat(uint id, const time_t *src, const char *format, ...) { + if (logDir < 0) return; - FILE *file = logFile(tag, time); + time_t ts = (src ? *src : time(NULL)); + struct tm *tm = localtime(&ts); + if (!tm) err(EX_OSERR, "localtime"); - char stamp[StampLen + 1]; - strftime(stamp, sizeof(stamp), "%FT%T%z", time); - fprintf(file, "[%s] ", stamp); - if (ferror(file)) err(EX_IOERR, "%s", tag.name); + FILE *file = logFile(id, tm); + + char buf[sizeof("0000-00-00T00:00:00+0000")]; + strftime(buf, sizeof(buf), "%FT%T%z", tm); + 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", tag.name); - - fprintf(file, "\n"); - if (ferror(file)) err(EX_IOERR, "%s", tag.name); -} - -static void logRead(struct Tag tag, bool replay) { - if (logRoot < 0) return; - - time_t t = time(NULL); - struct tm *time = localtime(&t); - if (!time) err(EX_SOFTWARE, "localtime"); - - FILE *file = logFile(tag, time); - rewind(file); - - char *line = NULL; - size_t cap = 0; - ssize_t len; - while (0 < (len = getline(&line, &cap, file))) { - if (replay) { - if (len < 1 + StampLen + 2 + 1) continue; - line[len - 1] = '\0'; - uiFmt(tag, UICold, "\3%d%s", IRCGray, &line[1 + StampLen + 2]); - } else { - printf("%s", line); - } - } - if (ferror(file)) err(EX_IOERR, "%s", tag.name); - free(line); -} - -void logList(struct Tag tag) { - logRead(tag, false); -} + if (n < 0) err(EX_IOERR, "%s", idNames[id]); -void logReplay(struct Tag tag) { - logRead(tag, true); + n = fprintf(file, "\n"); + if (n < 0) err(EX_IOERR, "%s", idNames[id]); } |