diff options
author | Omar Polo <op@omarpolo.com> | 2023-06-06 08:50:54 +0000 |
---|---|---|
committer | Omar Polo <op@omarpolo.com> | 2023-06-06 08:50:54 +0000 |
commit | 281a8852b3a2d76c10d2fb6476a706746d05509b (patch) | |
tree | a9c5a73adf8e85597f0eb87d1865e28249c2b8a1 /logger.c | |
parent | 3dd89fbb44f7f61b8c89db12f4469ca49c17bc68 (diff) |
rename log.[ch] to logger.[ch]
Diffstat (limited to 'logger.c')
-rw-r--r-- | logger.c | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/logger.c b/logger.c new file mode 100644 index 0000000..872846a --- /dev/null +++ b/logger.c @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2021 Omar Polo <op@omarpolo.com> + * + * Permission to use, copy, modify, and 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. + */ + +#include "gmid.h" + +#include <sys/types.h> +#include <sys/uio.h> + +#include <errno.h> +#include <event.h> +#include <imsg.h> +#include <netdb.h> +#include <poll.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <time.h> + +#include "logger.h" + +static struct event imsgev; + +static FILE *log; + +static void handle_imsg_quit(struct imsgbuf*, struct imsg*, size_t); +static void handle_imsg_log(struct imsgbuf*, struct imsg*, size_t); +static void handle_imsg_log_type(struct imsgbuf*, struct imsg*, size_t); +static void handle_dispatch_imsg(int, short, void*); + +static imsg_handlerfn *handlers[] = { + [IMSG_QUIT] = handle_imsg_quit, + [IMSG_LOG] = handle_imsg_log, + [IMSG_LOG_REQUEST] = handle_imsg_log, + [IMSG_LOG_TYPE] = handle_imsg_log_type, +}; + +static inline void +print_date(FILE *f) +{ + struct tm tminfo; + time_t t; + char buf[20]; + + time(&t); + strftime(buf, sizeof(buf), "%F %T", + localtime_r(&t, &tminfo)); + fprintf(f, "[%s] ", buf); +} + +static inline int +should_log(int priority) +{ + switch (priority) { + case LOG_ERR: + return 1; + case LOG_WARNING: + return 1; + case LOG_NOTICE: + return conf.verbose >= 1; + case LOG_INFO: + return conf.verbose >= 2; + case LOG_DEBUG: + return conf.verbose >= 3; + default: + return 0; + } +} + +static inline void +send_log(int type, int priority, const char *msg, size_t len) +{ + imsg_compose(&logibuf, type, priority, 0, -1, msg, len); + imsg_flush(&logibuf); +} + +static __dead void +fatal_impl(int use_err, const char *fmt, va_list ap) +{ + struct pollfd pfd; + char *str, *tmp; + int r, t, err; + + err = errno; + + if ((r = vasprintf(&str, fmt, ap)) != -1) { + if (use_err && + (t = asprintf(&tmp, "%s: %s", str, strerror(err))) != + -1) { + free(str); + str = tmp; + r = t; + } + } else + str = NULL, r = 0; + + send_log(IMSG_LOG, LOG_CRIT, str, r + 1); + free(str); + + /* wait for the logger process to shut down */ + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = logibuf.fd; + pfd.events = POLLIN; + poll(&pfd, 1, 1000); + + exit(1); +} + +void __dead +fatal(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fatal_impl(1, fmt, ap); + va_end(ap); +} + +void __dead +fatalx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fatal_impl(0, fmt, ap); + va_end(ap); +} + +static inline void +vlog(int priority, struct client *c, + const char *fmt, va_list ap) +{ + char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + char *fmted, *s; + size_t len; + int ec; + + if (!should_log(priority)) + return; + + if (c != NULL) { + len = sizeof(c->addr); + ec = getnameinfo((struct sockaddr*)&c->addr, len, + hbuf, sizeof(hbuf), + sbuf, sizeof(sbuf), + NI_NUMERICHOST | NI_NUMERICSERV); + if (ec != 0) + fatal("getnameinfo: %s", gai_strerror(ec)); + } + + if (vasprintf(&fmted, fmt, ap) == -1) + fatal("vasprintf: %s", strerror(errno)); + + if (c == NULL) + ec = asprintf(&s, "internal: %s", fmted); + else + ec = asprintf(&s, "%s:%s %s", hbuf, sbuf, fmted); + + if (ec < 0) + fatal("asprintf: %s", strerror(errno)); + + send_log(IMSG_LOG, priority, s, ec+1); + + free(fmted); + free(s); +} + +void +log_err(struct client *c, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog(LOG_ERR, c, fmt, ap); + va_end(ap); +} + +void +log_warn(struct client *c, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog(LOG_WARNING, c, fmt, ap); + va_end(ap); +} + +void +log_notice(struct client *c, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog(LOG_NOTICE, c, fmt, ap); + va_end(ap); +} + +void +log_info(struct client *c, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog(LOG_INFO, c, fmt, ap); + va_end(ap); +} + +void +log_debug(struct client *c, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog(LOG_DEBUG, c, fmt, ap); + va_end(ap); +} + +/* strchr, but with a bound */ +static char * +gmid_strnchr(char *s, int c, size_t len) +{ + size_t i; + + for (i = 0; i < len; ++i) + if (s[i] == c) + return &s[i]; + return NULL; +} + +void +log_request(struct client *c, char *meta, size_t l) +{ + char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV], b[GEMINI_URL_LEN]; + char *fmted; + const char *t; + size_t len; + int ec; + + len = sizeof(c->addr); + ec = getnameinfo((struct sockaddr*)&c->addr, len, + hbuf, sizeof(hbuf), + sbuf, sizeof(sbuf), + NI_NUMERICHOST | NI_NUMERICSERV); + if (ec != 0) + fatalx("getnameinfo: %s", gai_strerror(ec)); + + if (c->iri.schema != NULL) { + /* serialize the IRI */ + strlcpy(b, c->iri.schema, sizeof(b)); + strlcat(b, "://", sizeof(b)); + + /* log the decoded host name, but if it was invalid + * use the raw one. */ + if (*c->domain != '\0') + strlcat(b, c->domain, sizeof(b)); + else + strlcat(b, c->iri.host, sizeof(b)); + + if (*c->iri.path != '/') + strlcat(b, "/", sizeof(b)); + strlcat(b, c->iri.path, sizeof(b)); /* TODO: sanitize UTF8 */ + if (*c->iri.query != '\0') { /* TODO: sanitize UTF8 */ + strlcat(b, "?", sizeof(b)); + strlcat(b, c->iri.query, sizeof(b)); + } + } else { + if ((t = c->req) == NULL) + t = ""; + strlcpy(b, t, sizeof(b)); + } + + if ((t = gmid_strnchr(meta, '\r', l)) == NULL) + t = meta + len; + + ec = asprintf(&fmted, "%s:%s GET %s %.*s", hbuf, sbuf, b, + (int)(t-meta), meta); + if (ec < 0) + err(1, "asprintf"); + send_log(IMSG_LOG_REQUEST, LOG_NOTICE, fmted, ec+1); + free(fmted); +} + + + +static void +do_log(int type, int priority, const char *msg) +{ + int quit = 0; + + if (priority == LOG_CRIT) { + quit = 1; + priority = LOG_ERR; + } + + if (log != NULL) { + if (type != IMSG_LOG_REQUEST) + print_date(log); + fprintf(log, "%s\n", msg); + } else + syslog(LOG_DAEMON | priority, "%s", msg); + + if (quit) + exit(1); +} + +static void +handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) +{ + event_loopbreak(); +} + +static void +handle_imsg_log(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) +{ + int priority; + char *msg; + + msg = imsg->data; + msg[datalen-1] = '\0'; + priority = imsg->hdr.peerid; + do_log(imsg->hdr.type, priority, msg); +} + +static void +handle_imsg_log_type(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen) +{ + if (log != NULL && log != stderr) { + fflush(log); + fclose(log); + } + log = NULL; + + if (imsg->fd != -1) { + if ((log = fdopen(imsg->fd, "a")) == NULL) { + syslog(LOG_DAEMON | LOG_ERR, "fdopen: %s", + strerror(errno)); + exit(1); + } + } +} + +static void +handle_dispatch_imsg(int fd, short ev, void *d) +{ + struct imsgbuf *ibuf = d; + dispatch_imsg(ibuf, handlers, sizeof(handlers)); +} + +int +logger_main(int fd, struct imsgbuf *ibuf) +{ + log = stderr; + + openlog(getprogname(), LOG_NDELAY, LOG_DAEMON); + tzset(); + + event_init(); + + event_set(&imsgev, fd, EV_READ | EV_PERSIST, &handle_dispatch_imsg, ibuf); + event_add(&imsgev, NULL); + + sandbox_logger_process(); + + event_dispatch(); + + closelog(); + + return 0; +} |