mirror of
https://github.com/bdnugget/kcgi.git
synced 2026-11-07 05:47:27 +00:00
318 lines
6.2 KiB
C
318 lines
6.2 KiB
C
/* $Id$ */
|
|
/*
|
|
* Copyright (c) 2016--2018, 2020 Kristaps Dzonsons <kristaps@bsd.lv>
|
|
*
|
|
* 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 <ctype.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#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);
|
|
}
|