commit 7e9b7812b3d3ef436d3aa0f793bbecd3cf08c914 from: Omar Polo date: Sun Feb 07 16:15:51 2021 UTC move logging code to log.c commit - 3077ce5bee2a492a165e88b9a60bda87c80caedc commit + 7e9b7812b3d3ef436d3aa0f793bbecd3cf08c914 blob - /dev/null blob + 26bd98850aee4bb0188725c4f265c3cacda9a917 (mode 644) --- /dev/null +++ log.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * 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 +#include +#include +#include +#include +#include + +#include "gmid.h" + +void +fatal(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + + if (conf.foreground) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } else + vsyslog(LOG_DAEMON | LOG_CRIT, fmt, ap); + + va_end(ap); + exit(1); +} + +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 void +do_log(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) { + strncpy(hbuf, "", sizeof(hbuf)); + sbuf[0] = '\0'; + } else { + 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 (conf.foreground) + fprintf(stderr, "%s:%s %s\n", hbuf, sbuf, fmted); + else { + if (asprintf(&s, "%s:%s %s", hbuf, sbuf, fmted) == -1) + fatal("asprintf: %s", strerror(errno)); + syslog(priority | LOG_DAEMON, "%s", s); + free(s); + } + + free(fmted); +} + +void +log_err(struct client *c, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + do_log(LOG_ERR, c, fmt, ap); + va_end(ap); +} + +void +log_warn(struct client *c, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + do_log(LOG_WARNING, c, fmt, ap); + va_end(ap); +} + +void +log_notice(struct client *c, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + do_log(LOG_NOTICE, c, fmt, ap); + va_end(ap); +} + +void +log_info(struct client *c, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + do_log(LOG_INFO, c, fmt, ap); + va_end(ap); +} + +void +log_debug(struct client *c, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + do_log(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 *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) + fatal("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)); + + 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 { + strlcpy(b, c->req, sizeof(b)); + } + + if ((t = gmid_strnchr(meta, '\r', l)) == NULL) + t = meta + len; + + if (conf.foreground) + fprintf(stderr, "%s:%s GET %s %.*s\n", hbuf, sbuf, b, + (int)(t - meta), meta); + else + syslog(LOG_INFO | LOG_DAEMON, "%s:%s GET %s %.*s", + hbuf, sbuf, b, (int)(t - meta), meta); +}