/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "config.h"
#include "util.h"
enum {
KLOG_CLOSE = 0,
KLOG_OPEN = 1,
KLOG_READ = 2,
KLOG_CONSOLE_OFF = 6,
KLOG_CONSOLE_ON = 7,
KLOG_CONSOLE_LEVEL = 8,
};
static char log_buffer[4096];
static sig_atomic_t running = 1;
static int level = 0;
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "level", required_argument, NULL, 'l' },
{ NULL, 0, NULL, 0 },
};
static const char *shortopt = "hVl:";
static const char *helptext =
"Usage: klogd [OPTION]... \n\n"
"Collect printk() messages from the kernel and forward them to syslogd.\n"
"\n"
"The following OPTIONSs can be used:\n"
" -l, --level Minimum log level that should be printed to console.\n"
" If not set, logging to console is turned off.\n"
" -h, --help Print this help text and exit\n"
" -V, --version Print version information and exit\n\n";
static void process_options(int argc, char **argv)
{
int c;
for (;;) {
c = getopt_long(argc, argv, shortopt, options, NULL);
if (c == -1)
break;
switch (c) {
case 'l':
level = strtoul(optarg, NULL, 10);
break;
case 'h':
fputs(helptext, stdout);
exit(EXIT_SUCCESS);
case 'V':
print_version("klogd");
default:
fputs("Try `klogd --help' for more information\n",
stderr);
exit(EXIT_FAILURE);
}
}
}
static void sighandler(int signo)
{
if (signo == SIGTERM || signo == SIGINT)
running = 0;
}
static void sigsetup(void)
{
struct sigaction act;
sigset_t mask;
memset(&act, 0, sizeof(act));
act.sa_handler = sighandler;
sigaction(SIGTERM, &act, NULL);
sigaction(SIGINT, &act, NULL);
sigfillset(&mask);
sigdelset(&mask, SIGTERM);
sigdelset(&mask, SIGINT);
sigprocmask(SIG_SETMASK, &mask, NULL);
}
static void log_open(void)
{
klogctl(KLOG_OPEN, NULL, 0);
if (level) {
klogctl(KLOG_CONSOLE_LEVEL, NULL, level);
} else {
klogctl(KLOG_CONSOLE_OFF, NULL, 0);
}
openlog("kernel", 0, LOG_KERN);
}
static void log_close(void)
{
klogctl(KLOG_CONSOLE_ON, NULL, 0);
klogctl(KLOG_CLOSE, NULL, 0);
syslog(LOG_NOTICE, "-- klogd terminating --");
}
int main(int argc, char **argv)
{
int diff, count = 0, priority, ret = EXIT_SUCCESS;
char *ptr, *end;
process_options(argc, argv);
sigsetup();
log_open();
/* TODO: seccomp lockdown? */
while (running) {
diff = klogctl(KLOG_READ, log_buffer + count,
sizeof(log_buffer) - 1 - count);
if (diff < 0) {
if (errno == EINTR)
continue;
syslog(LOG_CRIT, "klogctl read: %s", strerror(errno));
ret = EXIT_FAILURE;
break;
}
count += diff;
log_buffer[count] = '\0';
ptr = log_buffer;
for (;;) {
end = strchr(ptr, '\n');
if (end == NULL) {
count = strlen(ptr);
memmove(log_buffer, ptr, count);
break;
}
*(end++) = '\0';
priority = LOG_INFO;
if (*ptr == '<') {
++ptr;
if (*ptr)
priority = strtoul(ptr, &ptr, 10);
if (*ptr == '>')
++ptr;
}
if (*ptr)
syslog(priority, "%s", ptr);
ptr = end;
}
}
log_close();
return ret;
}