/* $Id$ */ /* * Copyright (c) 2016--2018, 2020 Kristaps Dzonsons * * 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 "config.h" #include #include #include #include #include #include #include #include #include "kcgi.h" enum llevel { LLEVEL_INFO, LLEVEL_WARN, LLEVEL_ERROR, LLEVEL__MAX }; static const char *llevels[LLEVEL__MAX] = { "INFO", /* LLEVEL_INFO */ "WARN", /* LLEVEL_WARN */ "ERROR", /* LLEVEL_ERROR */ }; /* * Actual logging function. * Only the optional error string is trusted to have "good" characters; * all others may contain anything and are filtered. */ static void logmsg(const struct kreq *r, const char *err, const char *lvl, const char *ident, const char *fmt, va_list ap) { int i, cmpsz, sz; char date[64]; char *msg, *var = NULL, *cmp, *p; /* * Convert to GMT. * We can't use localtime because we're probably going to be * chrooted, and maybe sandboxed, and touching our timezone * files will crash us (or at least not be applicable). */ khttp_epoch2str(time(NULL), date, sizeof(date)); /* * Format the variable args, then compose with the log prefix * to form the basic message. */ if (fmt != NULL) { kvasprintf(&var, fmt, ap); cmpsz = kasprintf (&cmp, "%s %s [%s] %s %s", r == NULL ? "-" : r->remote, ident == NULL ? "-" : ident, date, lvl == NULL ? "-" : lvl, var); free(var); } else cmpsz = kasprintf (&cmp, "%s %s [%s] %s -", r == NULL ? "-" : r->remote, ident == NULL ? "-" : ident, date, lvl == NULL ? "-" : lvl); /* * Allocate the required memory for the message, leaving room * for whitespace character expansion and optional error message. * Start by buffering for \n\0. */ sz = cmpsz + 2; for (i = 0; i < cmpsz; i++) switch (cmp[i]) { case '\n': case '\r': case '\t': sz++; break; default: break; } /* Next, buffer for ": ". */ if (err != NULL) sz += 2 + strlen(err); p = msg = kmalloc(sz); /* * Copy message into final buffer, filtering unprintables * and whitespace. * This draws from strvis(3). */ for (i = 0; i < cmpsz; i++) switch (cmp[i]) { case '\a': *p++ = '\\'; *p++ = 'a'; break; case '\b': *p++ = '\\'; *p++ = 'b'; break; case '\f': *p++ = '\\'; *p++ = 'f'; break; case '\n': *p++ = '\\'; *p++ = 'n'; break; case '\r': *p++ = '\\'; *p++ = 'r'; break; case '\t': *p++ = '\\'; *p++ = 't'; break; case '\v': *p++ = '\\'; *p++ = 'v'; break; case '\0': *p++ = '\\'; *p++ = '0'; break; default: if (isprint((unsigned char)cmp[i])) *p++ = cmp[i]; else *p++ = '?'; break; } *p = '\0'; free(cmp); /* Append optional error message, and newline */ if (err != NULL) { (void)strlcat(msg, ": ", sz); (void)strlcat(msg, err, sz); } (void)strlcat(msg, "\n", sz); fputs(msg, stderr); free(msg); } int kutil_openlog(const char *file) { if (file != NULL && freopen(file, "a", stderr) == NULL) return 0; return setvbuf(stderr, NULL, _IOLBF, 0) != EOF; } void kutil_vlog(const struct kreq *r, const char *lvl, const char *ident, const char *fmt, va_list ap) { logmsg(r, strerror(errno), lvl, ident, fmt, ap); } void kutil_vlogx(const struct kreq *r, const char *lvl, const char *ident, const char *fmt, va_list ap) { logmsg(r, NULL, lvl, ident, fmt, ap); } void kutil_warnx(const struct kreq *r, const char *ident, const char *fmt, ...) { va_list ap; va_start(ap, fmt); kutil_vlogx(r, llevels[LLEVEL_WARN], ident, fmt, ap); va_end(ap); } void kutil_errx(const struct kreq *r, const char *ident, const char *fmt, ...) { va_list ap; va_start(ap, fmt); kutil_vlogx(r, llevels[LLEVEL_ERROR], ident, fmt, ap); va_end(ap); exit(EXIT_FAILURE); } void kutil_verrx(const struct kreq *r, const char *ident, const char *fmt, va_list ap) { kutil_vlogx(r, llevels[LLEVEL_ERROR], ident, fmt, ap); exit(EXIT_FAILURE); } void kutil_vwarnx(const struct kreq *r, const char *ident, const char *fmt, va_list ap) { kutil_vlogx(r, llevels[LLEVEL_WARN], ident, fmt, ap); } void kutil_err(const struct kreq *r, const char *ident, const char *fmt, ...) { va_list ap; va_start(ap, fmt); kutil_vlog(r, llevels[LLEVEL_ERROR], ident, fmt, ap); va_end(ap); exit(EXIT_FAILURE); } void kutil_warn(const struct kreq *r, const char *ident, const char *fmt, ...) { va_list ap; va_start(ap, fmt); kutil_vlog(r, llevels[LLEVEL_WARN], ident, fmt, ap); va_end(ap); } void kutil_verr(const struct kreq *r, const char *ident, const char *fmt, va_list ap) { kutil_vlog(r, llevels[LLEVEL_ERROR], ident, fmt, ap); exit(EXIT_FAILURE); } void kutil_vwarn(const struct kreq *r, const char *ident, const char *fmt, va_list ap) { kutil_vlog(r, llevels[LLEVEL_WARN], ident, fmt, ap); } void kutil_info(const struct kreq *r, const char *ident, const char *fmt, ...) { va_list ap; va_start(ap, fmt); kutil_vlogx(r, llevels[LLEVEL_INFO], ident, fmt, ap); va_end(ap); } void kutil_vinfo(const struct kreq *r, const char *ident, const char *fmt, va_list ap) { kutil_vlogx(r, llevels[LLEVEL_INFO], ident, fmt, ap); } void kutil_logx(const struct kreq *r, const char *lvl, const char *ident, const char *fmt, ...) { va_list ap; va_start(ap, fmt); kutil_vlogx(r, lvl, ident, fmt, ap); va_end(ap); } void kutil_log(const struct kreq *r, const char *lvl, const char *ident, const char *fmt, ...) { va_list ap; va_start(ap, fmt); kutil_vlog(r, lvl, ident, fmt, ap); va_end(ap); }