penguin/tinyproxy

An L4 proxy designed to act as a tiny transparent shim

src/klog.c

raw ยท 3012 bytes

#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "compat.h"

#include "klog.h"
#include "route.h"
#include "compat_thread.h"

static compat_mutex_t log_mu;
static int log_mu_ready;
static _Thread_local int klog_worker_id = -1;

int klog_init(void)
{
	if (log_mu_ready) {
		return 0;
	}

	if (compat_mutex_init(&log_mu) != 0) {
		return errno ? errno : EINVAL;
	}

	log_mu_ready = 1;
	return 0;
}

void klog_free(void)
{
	if (!log_mu_ready) {
		return;
	}

	compat_mutex_destroy(&log_mu);
	log_mu_ready = 0;
}

void klog_set_worker_id(int id)
{
	klog_worker_id = id;
}

static int localtime_compat(const time_t *t, struct tm *out)
{
#ifdef _WIN32
	return localtime_s(out, t);
#else
	return localtime_r(t, out) == NULL ? -1 : 0;
#endif
}

static void print_quoted_value(const char *s)
{
	if (s == NULL) {
		fputs("\"\"", stderr);
		return;
	}

	fputc('"', stderr);

	for (; *s != '\0'; s++) {
		switch (*s) {
		case '\\':
			fputs("\\\\", stderr);
			break;
		case '"':
			fputs("\\\"", stderr);
			break;
		case '\n':
			fputs("\\n", stderr);
			break;
		case '\r':
			fputs("\\r", stderr);
			break;
		case '\t':
			fputs("\\t", stderr);
			break;
		default:
			fputc(*s, stderr);
			break;
		}
	}

	fputc('"', stderr);
}

static void log_print_endpoint(FILE *out, const struct endpoint *ep)
{
	char buf[256];

	endpoint_to_string(ep, buf, sizeof(buf));
	fputs(buf, out);
}

static void print_log_value(struct log_value value)
{
	switch (value.type) {
	case LOG_VALUE_STR:
		print_quoted_value(value.v.s);
		break;
	case LOG_VALUE_INT:
		fprintf(stderr, "%d", value.v.i);
		break;
	case LOG_VALUE_UINT:
		fprintf(stderr, "%u", value.v.u);
		break;
	case LOG_VALUE_LONG:
		fprintf(stderr, "%ld", value.v.l);
		break;
	case LOG_VALUE_ULONG:
		fprintf(stderr, "%lu", value.v.ul);
		break;
	case LOG_VALUE_LLONG:
		fprintf(stderr, "%lld", value.v.ll);
		break;
	case LOG_VALUE_ULLONG:
		fprintf(stderr, "%llu", value.v.ull);
		break;
	case LOG_VALUE_ENDPOINT:
		log_print_endpoint(stderr, value.v.endpoint);
		break;
	default:
		fputs("<invalid>", stderr);
		break;
	}
}

void log_at(char sev, const char *file, int line, const char *msg, ...)
{
#ifdef FUZZ
	return;
#endif
	if (log_mu_ready) {
		compat_mutex_lock(&log_mu);
	}

	struct timespec ts;
	struct tm tm;
	char tbuf[32];

	clock_gettime(CLOCK_REALTIME, &ts);
	if (localtime_compat(&ts.tv_sec, &tm) != 0) {
		memset(&tm, 0, sizeof(tm));
	}

	strftime(tbuf, sizeof(tbuf), "%m%d %H:%M:%S", &tm);

	fprintf(stderr, "%c%s.%06ld %7ld %s:%d] worker=%u msg=",
		sev,
		tbuf,
		ts.tv_nsec / 1000,
		(long)compat_getpid(),
		file,
		line,
		klog_worker_id);

	print_quoted_value(msg);

	va_list ap;
	va_start(ap, msg);

	for (;;) {
		const char *key = va_arg(ap, const char *);
		if (key == NULL) {
			break;
		}

		struct log_value value = va_arg(ap, struct log_value);

		fprintf(stderr, " %s=", key);
		print_log_value(value);
	}

	va_end(ap);

	fputc('\n', stderr);

	if (log_mu_ready) {
		compat_mutex_unlock(&log_mu);
	}
}