mirror of
https://github.com/omar-polo/gmid.git
synced 2024-10-02 23:36:52 +02:00
db623d7551
Due to capsicum(4), opening the log on-demand via syslog(3) fails. openlog(LOG_NDELAY) forces the log to be opened immediately, before we enter capsicum. This doesn't affect OpenBSD (where pledge(2) doesn't stop syslog) nor linux (where the log process is not sandboxed.) Reported by Karl Jeacle, thank you!
346 lines
6.8 KiB
C
346 lines
6.8 KiB
C
/*
|
||
* 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>
|
||
|
||
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);
|
||
}
|
||
|
||
void
|
||
fatal(const char *fmt, ...)
|
||
{
|
||
struct pollfd pfd;
|
||
va_list ap;
|
||
int r;
|
||
char *fmted;
|
||
|
||
va_start(ap, fmt);
|
||
if ((r = vasprintf(&fmted, fmt, ap)) != -1) {
|
||
send_log(IMSG_LOG, LOG_CRIT, fmted, r+1);
|
||
free(fmted);
|
||
|
||
/* wait for the logger process to shut down */
|
||
pfd.fd = logibuf.fd;
|
||
pfd.events = POLLIN;
|
||
poll(&pfd, 1, 1000);
|
||
}
|
||
va_end(ap);
|
||
exit(1);
|
||
}
|
||
|
||
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 *t, *fmted;
|
||
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;
|
||
|
||
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);
|
||
|
||
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;
|
||
}
|