mirror of
https://github.com/pygos/usyslog.git
synced 2024-12-04 17:37:03 +01:00
Initial import
Signed-off-by: David Oberhollenzer <david.oberhollenzer@tele2.at>
This commit is contained in:
commit
a4423189ab
12 changed files with 1260 additions and 0 deletions
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
.deps
|
||||
Makefile
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
compile
|
||||
config.h.in
|
||||
config.log
|
||||
config.status
|
||||
configure
|
||||
depcomp
|
||||
install-sh
|
||||
missing
|
||||
stamp-h1
|
||||
config.h
|
||||
*.o
|
||||
*~
|
||||
klogd
|
||||
usyslogd
|
11
Makefile.am
Normal file
11
Makefile.am
Normal file
|
@ -0,0 +1,11 @@
|
|||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
AM_CPPFLAGS = -D_GNU_SOURCE
|
||||
AM_CFLAGS = $(WARN_CFLAGS)
|
||||
|
||||
usyslogd_SOURCES = syslogd.c syslogd.h proto.c logfile.c mksock.c protomap.c
|
||||
klogd_SOURCES = klogd.c
|
||||
|
||||
bin_PROGRAMS =
|
||||
sbin_PROGRAMS = usyslogd klogd
|
||||
# EXTRA_DIST = README LICENSE
|
3
autogen.sh
Executable file
3
autogen.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
autoreconf --force --install --symlink
|
35
configure.ac
Normal file
35
configure.ac
Normal file
|
@ -0,0 +1,35 @@
|
|||
AC_PREREQ([2.60])
|
||||
AC_INIT([usyslog], [0.1], [david.oberhollenzer@tele2.at], usyslog)
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AM_INIT_AUTOMAKE([foreign dist-xz])
|
||||
AM_SILENT_RULES([yes])
|
||||
AC_PROG_CC
|
||||
AC_PROG_CC_C99
|
||||
AC_PROG_INSTALL
|
||||
|
||||
UL_WARN_ADD([-Wall])
|
||||
UL_WARN_ADD([-Wextra])
|
||||
UL_WARN_ADD([-Wunused])
|
||||
UL_WARN_ADD([-Wmissing-prototypes])
|
||||
UL_WARN_ADD([-Wmissing-declarations])
|
||||
UL_WARN_ADD([-Wwrite-strings])
|
||||
UL_WARN_ADD([-Wjump-misses-init])
|
||||
UL_WARN_ADD([-Wuninitialized])
|
||||
UL_WARN_ADD([-Winit-self])
|
||||
UL_WARN_ADD([-Wlogical-op])
|
||||
UL_WARN_ADD([-Wunused-but-set-parameter])
|
||||
UL_WARN_ADD([-Wunused-but-set-variable])
|
||||
UL_WARN_ADD([-Wunused-parameter])
|
||||
UL_WARN_ADD([-Wunused-result])
|
||||
UL_WARN_ADD([-Wunused-variable])
|
||||
UL_WARN_ADD([-Wduplicated-cond])
|
||||
UL_WARN_ADD([-Wduplicated-branches])
|
||||
UL_WARN_ADD([-Wrestrict])
|
||||
UL_WARN_ADD([-Wnull-dereference])
|
||||
UL_WARN_ADD([-pedantic])
|
||||
|
||||
AC_SUBST([WARN_CFLAGS])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
AC_OUTPUT([Makefile])
|
193
klogd.c
Normal file
193
klogd.c
Normal file
|
@ -0,0 +1,193 @@
|
|||
/* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <sys/klog.h>
|
||||
#include <syslog.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "config.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 <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";
|
||||
|
||||
#define GPL_URL "https://gnu.org/licenses/gpl.html"
|
||||
|
||||
static const char *version_string =
|
||||
"klogd (usyslog) " PACKAGE_VERSION "\n"
|
||||
"Copyright (C) 2018 David Oberhollenzer\n\n"
|
||||
"License GPLv3+: GNU GPL version 3 or later <" GPL_URL ">.\n"
|
||||
"This is free software: you are free to change and redistribute it.\n"
|
||||
"There is NO WARRANTY, to the extent permitted by law.\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':
|
||||
fputs(version_string, stdout);
|
||||
exit(EXIT_SUCCESS);
|
||||
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;
|
||||
}
|
242
logfile.c
Normal file
242
logfile.c
Normal file
|
@ -0,0 +1,242 @@
|
|||
/* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "syslogd.h"
|
||||
|
||||
|
||||
typedef struct logfile_t {
|
||||
struct logfile_t *next;
|
||||
size_t size;
|
||||
int fd;
|
||||
char filename[];
|
||||
} logfile_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
log_backend_t base;
|
||||
logfile_t *list;
|
||||
size_t maxsize;
|
||||
int flags;
|
||||
} log_backend_file_t;
|
||||
|
||||
|
||||
static int logfile_open(logfile_t *file)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
file->fd = open(file->filename, O_WRONLY | O_CREAT, 0640);
|
||||
if (file->fd < 0) {
|
||||
perror(file->filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lseek(file->fd, 0, SEEK_END))
|
||||
goto fail;
|
||||
|
||||
if (fstat(file->fd, &sb))
|
||||
goto fail;
|
||||
|
||||
file->size = sb.st_size;
|
||||
return 0;
|
||||
fail:
|
||||
perror(file->filename);
|
||||
close(file->fd);
|
||||
file->fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static logfile_t *logfile_create(const char *filename)
|
||||
{
|
||||
logfile_t *file = calloc(1, sizeof(*file) + strlen(filename) + 1);
|
||||
|
||||
if (file == NULL) {
|
||||
perror("calloc");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(file->filename, filename);
|
||||
|
||||
if (logfile_open(file)) {
|
||||
free(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
static int logfile_write(logfile_t *file, const syslog_msg_t *msg)
|
||||
{
|
||||
const char *lvl_str, *fac_name;
|
||||
char timebuf[32];
|
||||
struct tm tm;
|
||||
int ret;
|
||||
|
||||
if (file->fd < 0 && logfile_open(file) != 0)
|
||||
return -1;
|
||||
|
||||
lvl_str = level_id_to_string(msg->level);
|
||||
if (lvl_str == NULL)
|
||||
return -1;
|
||||
|
||||
gmtime_r(&msg->timestamp, &tm);
|
||||
strftime(timebuf, sizeof(timebuf), "%FT%T", &tm);
|
||||
|
||||
if (msg->ident != NULL) {
|
||||
fac_name = facility_id_to_string(msg->facility);
|
||||
if (fac_name == NULL)
|
||||
return -1;
|
||||
|
||||
ret = dprintf(file->fd, "[%s][%s][%s][%u] %s\n", timebuf,
|
||||
fac_name, lvl_str, msg->pid, msg->message);
|
||||
} else {
|
||||
ret = dprintf(file->fd, "[%s][%s][%u] %s\n", timebuf, lvl_str,
|
||||
msg->pid, msg->message);
|
||||
}
|
||||
|
||||
fsync(file->fd);
|
||||
|
||||
if (ret > 0)
|
||||
file->size += ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int logfile_rotate(logfile_t *f, int flags)
|
||||
{
|
||||
char timebuf[32];
|
||||
char *filename;
|
||||
struct tm tm;
|
||||
time_t now;
|
||||
|
||||
if (flags & LOG_ROTATE_OVERWRITE) {
|
||||
strcpy(timebuf, "1");
|
||||
} else {
|
||||
now = time(NULL);
|
||||
gmtime_r(&now, &tm);
|
||||
strftime(timebuf, sizeof(timebuf), "%FT%T", &tm);
|
||||
}
|
||||
|
||||
filename = alloca(strlen(f->filename) + strlen(timebuf) + 2);
|
||||
sprintf(filename, "%s.%s", f->filename, timebuf);
|
||||
|
||||
if (rename(f->filename, filename)) {
|
||||
perror(filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(f->fd);
|
||||
logfile_open(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static int file_backend_init(log_backend_t *backend, int flags,
|
||||
size_t sizelimit)
|
||||
{
|
||||
log_backend_file_t *log = (log_backend_file_t *)backend;
|
||||
|
||||
log->flags = flags;
|
||||
log->maxsize = sizelimit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void file_backend_cleanup(log_backend_t *backend)
|
||||
{
|
||||
log_backend_file_t *log = (log_backend_file_t *)backend;
|
||||
logfile_t *f;
|
||||
|
||||
while (log->list != NULL) {
|
||||
f = log->list;
|
||||
log->list = f->next;
|
||||
|
||||
close(f->fd);
|
||||
free(f);
|
||||
}
|
||||
}
|
||||
|
||||
static int file_backend_write(log_backend_t *backend, const syslog_msg_t *msg)
|
||||
{
|
||||
log_backend_file_t *log = (log_backend_file_t *)backend;
|
||||
const char *ident;
|
||||
char *filename;
|
||||
logfile_t *f;
|
||||
size_t len;
|
||||
|
||||
if (msg->ident != NULL) {
|
||||
ident = msg->ident;
|
||||
} else {
|
||||
ident = facility_id_to_string(msg->facility);
|
||||
if (ident == NULL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = strlen(ident) + strlen(".log") + 1;
|
||||
filename = alloca(len);
|
||||
strcpy(filename, ident);
|
||||
strcat(filename, ".log");
|
||||
|
||||
for (f = log->list; f != NULL; f = f->next) {
|
||||
if (strcmp(filename, f->filename) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (f == NULL) {
|
||||
f = logfile_create(filename);
|
||||
if (f == NULL)
|
||||
return -1;
|
||||
f->next = log->list;
|
||||
log->list = f;
|
||||
}
|
||||
|
||||
if (logfile_write(f, msg))
|
||||
return -1;
|
||||
|
||||
if ((log->flags & LOG_ROTATE_SIZE_LIMIT) && f->size >= log->maxsize)
|
||||
logfile_rotate(f, log->flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void file_backend_rotate(log_backend_t *backend)
|
||||
{
|
||||
log_backend_file_t *log = (log_backend_file_t *)backend;
|
||||
logfile_t *f;
|
||||
|
||||
for (f = log->list; f != NULL; f = f->next)
|
||||
logfile_rotate(f, log->flags);
|
||||
}
|
||||
|
||||
log_backend_file_t filebackend = {
|
||||
.base = {
|
||||
.init = file_backend_init,
|
||||
.cleanup = file_backend_cleanup,
|
||||
.write = file_backend_write,
|
||||
.rotate = file_backend_rotate,
|
||||
},
|
||||
.list = NULL,
|
||||
};
|
||||
|
||||
log_backend_t *logmgr = (log_backend_t *)&filebackend;
|
40
m4/compiler.m4
Normal file
40
m4/compiler.m4
Normal file
|
@ -0,0 +1,40 @@
|
|||
dnl Copyright (C) 2008-2011 Free Software Foundation, Inc.
|
||||
dnl This file is free software; the Free Software Foundation
|
||||
dnl gives unlimited permission to copy and/or distribute it,
|
||||
dnl with or without modifications, as long as this notice is preserved.
|
||||
|
||||
dnl From Simon Josefsson
|
||||
dnl -- derivated from coreutils m4/warnings.m4
|
||||
|
||||
# UL_AS_VAR_APPEND(VAR, VALUE)
|
||||
# ----------------------------
|
||||
# Provide the functionality of AS_VAR_APPEND if Autoconf does not have it.
|
||||
m4_ifdef([AS_VAR_APPEND],
|
||||
[m4_copy([AS_VAR_APPEND], [UL_AS_VAR_APPEND])],
|
||||
[m4_define([UL_AS_VAR_APPEND],
|
||||
[AS_VAR_SET([$1], [AS_VAR_GET([$1])$2])])])
|
||||
|
||||
# UL_ADD_WARN(COMPILER_OPTION [, VARNAME])
|
||||
# ------------------------
|
||||
# Adds parameter to WARN_CFLAGS (or to $VARNAME) if the compiler supports it.
|
||||
AC_DEFUN([UL_WARN_ADD], [
|
||||
m4_define([warnvarname], m4_default([$2],WARN_CFLAGS))
|
||||
AS_VAR_PUSHDEF([ul_Warn], [ul_cv_warn_$1])dnl
|
||||
AC_CACHE_CHECK([whether compiler handles $1], m4_defn([ul_Warn]), [
|
||||
# store AC_LANG_WERROR status, then turn it on
|
||||
save_ac_[]_AC_LANG_ABBREV[]_werror_flag="${ac_[]_AC_LANG_ABBREV[]_werror_flag}"
|
||||
AC_LANG_WERROR
|
||||
|
||||
ul_save_CPPFLAGS="$CPPFLAGS"
|
||||
CPPFLAGS="-Werror ${CPPFLAGS} $1"
|
||||
AC_PREPROC_IFELSE([AC_LANG_PROGRAM([])],
|
||||
[AS_VAR_SET(ul_Warn, [yes])],
|
||||
[AS_VAR_SET(ul_Warn, [no])])
|
||||
# restore AC_LANG_WERROR
|
||||
ac_[]_AC_LANG_ABBREV[]_werror_flag="${save_ac_[]_AC_LANG_ABBREV[]_werror_flag}"
|
||||
|
||||
CPPFLAGS="$ul_save_CPPFLAGS"
|
||||
])
|
||||
AS_VAR_IF(ul_Warn, [yes], [UL_AS_VAR_APPEND(warnvarname, [" $1"])])
|
||||
])
|
||||
|
63
mksock.c
Normal file
63
mksock.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
/* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "syslogd.h"
|
||||
|
||||
int mksock(const char *path)
|
||||
{
|
||||
struct sockaddr_un un;
|
||||
const char *errmsg;
|
||||
int fd;
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
|
||||
if (fd < 0) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&un, 0, sizeof(un));
|
||||
un.sun_family = AF_UNIX;
|
||||
|
||||
strcpy(un.sun_path, path);
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&un, sizeof(un))) {
|
||||
errmsg ="bind";
|
||||
goto fail_errno;
|
||||
}
|
||||
|
||||
if (chmod(path, 0777)) {
|
||||
errmsg = "chmod";
|
||||
goto fail_errno;
|
||||
}
|
||||
|
||||
return fd;
|
||||
fail_errno:
|
||||
fprintf(stderr, "%s: %s: %s\n", path, errmsg, strerror(errno));
|
||||
close(fd);
|
||||
unlink(path);
|
||||
return -1;
|
||||
}
|
194
proto.c
Normal file
194
proto.c
Normal file
|
@ -0,0 +1,194 @@
|
|||
/* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "syslogd.h"
|
||||
|
||||
static const char *months[] = {
|
||||
"Jan", "Feb", "Mar", "Apr",
|
||||
"May", "Jun", "Jul", "Aug",
|
||||
"Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
|
||||
static const int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||
|
||||
static int isleap(int year)
|
||||
{
|
||||
return ((year % 4 == 0) && (year % 100 != 0)) || ((year % 400) == 0);
|
||||
}
|
||||
|
||||
static int mdays(int year, int month)
|
||||
{
|
||||
return (isleap(year) && month == 2) ? 29 : days[month - 1];
|
||||
}
|
||||
|
||||
static char *read_num(char *str, int *out, int maxval)
|
||||
{
|
||||
if (str == NULL || !isdigit(*str))
|
||||
return NULL;
|
||||
for (*out = 0; isdigit(*str); ++str) {
|
||||
(*out) = (*out) * 10 + (*str) - '0';
|
||||
if ((*out) > maxval)
|
||||
return NULL;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *skip_space(char *str)
|
||||
{
|
||||
if (str == NULL || !isspace(*str))
|
||||
return NULL;
|
||||
while (isspace(*str))
|
||||
++str;
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *read_date_bsd(char *str, struct tm *tm)
|
||||
{
|
||||
int year, month, day, hour, minute, second;
|
||||
time_t t;
|
||||
|
||||
/* decode date */
|
||||
for (month = 0; month < 12; ++month) {
|
||||
if (strncmp(str, months[month], 3) == 0) {
|
||||
str = skip_space(str + 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
str = read_num(str, &day, 31);
|
||||
str = skip_space(str);
|
||||
|
||||
t = time(NULL);
|
||||
if (localtime_r(&t, tm) == NULL)
|
||||
return NULL;
|
||||
|
||||
year = tm->tm_year;
|
||||
|
||||
/* sanity check */
|
||||
if (str == NULL || month >= 12 || day < 1)
|
||||
return NULL;
|
||||
if (month == 11 && tm->tm_mon == 0)
|
||||
--year;
|
||||
if (day > mdays(year + 1900, month + 1))
|
||||
return NULL;
|
||||
|
||||
/* decode time */
|
||||
str = read_num(str, &hour, 23);
|
||||
if (str == NULL || *(str++) != ':')
|
||||
return NULL;
|
||||
str = read_num(str, &minute, 59);
|
||||
if (str == NULL || *(str++) != ':')
|
||||
return NULL;
|
||||
str = read_num(str, &second, 59);
|
||||
str = skip_space(str);
|
||||
|
||||
/* store result */
|
||||
memset(tm, 0, sizeof(*tm));
|
||||
tm->tm_sec = second;
|
||||
tm->tm_min = minute;
|
||||
tm->tm_hour = hour;
|
||||
tm->tm_mday = day;
|
||||
tm->tm_mon = month;
|
||||
tm->tm_year = year;
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *decode_priority(char *str, int *priority)
|
||||
{
|
||||
while (isspace(*str))
|
||||
++str;
|
||||
if (*(str++) != '<')
|
||||
return NULL;
|
||||
str = read_num(str, priority, 23 * 8 + 7);
|
||||
if (str == NULL || *(str++) != '>')
|
||||
return NULL;
|
||||
while (isspace(*str))
|
||||
++str;
|
||||
return str;
|
||||
}
|
||||
|
||||
int syslog_msg_parse(syslog_msg_t *msg, char *str)
|
||||
{
|
||||
char *ident, *ptr;
|
||||
struct tm tstamp;
|
||||
pid_t pid = 0;
|
||||
int priority;
|
||||
size_t len;
|
||||
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
|
||||
str = decode_priority(str, &priority);
|
||||
if (str == NULL)
|
||||
return -1;
|
||||
|
||||
msg->facility = priority >> 3;
|
||||
msg->level = priority & 0x07;
|
||||
|
||||
str = read_date_bsd(str, &tstamp);
|
||||
if (str == NULL)
|
||||
return -1;
|
||||
|
||||
ident = str;
|
||||
while (*str != '\0' && *str != ':')
|
||||
++str;
|
||||
|
||||
if (*str == ':') {
|
||||
*(str++) = '\0';
|
||||
while (isspace(*str))
|
||||
++str;
|
||||
|
||||
ptr = ident;
|
||||
while (*ptr != '[' && *ptr != '\0')
|
||||
++ptr;
|
||||
|
||||
if (*ptr == '[') {
|
||||
*(ptr++) = '\0';
|
||||
|
||||
while (isdigit(*ptr))
|
||||
pid = pid * 10 + *(ptr++) - '0';
|
||||
}
|
||||
} else {
|
||||
ident = NULL;
|
||||
}
|
||||
|
||||
if (ident != NULL && ident[0] == '\0')
|
||||
ident = NULL;
|
||||
|
||||
msg->timestamp = mktime(&tstamp);
|
||||
msg->pid = pid;
|
||||
msg->ident = ident;
|
||||
msg->message = str;
|
||||
|
||||
len = strlen(str);
|
||||
while (len > 0 && isspace(str[len - 1]))
|
||||
--len;
|
||||
str[len] = '\0';
|
||||
|
||||
if (ident != NULL) {
|
||||
for (ptr = ident; *ptr != '\0'; ++ptr) {
|
||||
if (!isalnum(*ptr))
|
||||
*ptr = '_';
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
81
protomap.c
Normal file
81
protomap.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
/* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "syslogd.h"
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
int id;
|
||||
} enum_map_t;
|
||||
|
||||
static const enum_map_t levels[] = {
|
||||
{ "emergency", 0 },
|
||||
{ "alert", 1 },
|
||||
{ "critical", 2 },
|
||||
{ "error", 3 },
|
||||
{ "warning", 4 },
|
||||
{ "notice", 5 },
|
||||
{ "info", 6 },
|
||||
{ "debug", 7 },
|
||||
{ NULL, 0 },
|
||||
};
|
||||
|
||||
static const enum_map_t facilities[] = {
|
||||
{ "kernel", 0 },
|
||||
{ "user", 1 },
|
||||
{ "mail", 2 },
|
||||
{ "daemon", 3 },
|
||||
{ "auth", 4 },
|
||||
{ "syslog", 5 },
|
||||
{ "lpr", 6 },
|
||||
{ "news", 7 },
|
||||
{ "uucp", 8 },
|
||||
{ "clock", 9 },
|
||||
{ "authpriv", 10 },
|
||||
{ "ftp", 11 },
|
||||
{ "ntp", 12 },
|
||||
{ "audit", 13 },
|
||||
{ "alert", 14 },
|
||||
{ "cron", 15 },
|
||||
{ "local0", 16 },
|
||||
{ "local1", 17 },
|
||||
{ "local2", 18 },
|
||||
{ "local3", 19 },
|
||||
{ "local4", 20 },
|
||||
{ "local5", 21 },
|
||||
{ "local6", 22 },
|
||||
{ "local7", 23 },
|
||||
{ NULL, 0 },
|
||||
};
|
||||
|
||||
static const char *enum_to_name(const enum_map_t *map, int id)
|
||||
{
|
||||
while (map->name != NULL && map->id != id)
|
||||
++map;
|
||||
|
||||
return map->name;
|
||||
}
|
||||
|
||||
const char *level_id_to_string(int level)
|
||||
{
|
||||
return enum_to_name(levels, level);
|
||||
}
|
||||
|
||||
const char *facility_id_to_string(int level)
|
||||
{
|
||||
return enum_to_name(facilities, level);
|
||||
}
|
282
syslogd.c
Normal file
282
syslogd.c
Normal file
|
@ -0,0 +1,282 @@
|
|||
/* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
||||
#include "syslogd.h"
|
||||
|
||||
static const struct option long_opts[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{ "rotate-replace", no_argument, NULL, 'r' },
|
||||
{ "chroot", no_argument, NULL, 'c' },
|
||||
{ "max-size", required_argument, NULL, 'm' },
|
||||
{ "user", required_argument, NULL, 'u' },
|
||||
{ "group", required_argument, NULL, 'g' },
|
||||
{ NULL, 0, NULL, 0 },
|
||||
};
|
||||
|
||||
static const char *short_opts = "hVcrm:u:g:";
|
||||
|
||||
const char *usage_string =
|
||||
"Usage: usyslogd [OPTIONS..]\n\n"
|
||||
"The following options are supported:\n"
|
||||
" -h, --help Print this help text and exit\n"
|
||||
" -V, --version Print version information and exit\n"
|
||||
" -r, --rotate-replace Replace old log files when doing log rotation.\n"
|
||||
" -m, --max-size <size> Automatically rotate log files bigger than this.\n"
|
||||
" -u, --user <name> Run the syslog daemon as this user. If not set,\n"
|
||||
" try to use the user '" DEFAULT_USER "'.\n"
|
||||
" -g, --group <name> Run the syslog daemon as this group. If not set,\n"
|
||||
" try to use the group '" DEFAULT_GROUP "'.\n"
|
||||
" -c, --chroot If set, do a chroot into the log file path.\n";
|
||||
|
||||
|
||||
|
||||
static volatile sig_atomic_t syslog_run = 1;
|
||||
static volatile sig_atomic_t syslog_rotate = 0;
|
||||
static int log_flags = 0;
|
||||
static size_t max_size = 0;
|
||||
static uid_t uid = 0;
|
||||
static gid_t gid = 0;
|
||||
static bool dochroot = false;
|
||||
|
||||
|
||||
|
||||
static void sighandler(int signo)
|
||||
{
|
||||
switch (signo) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
syslog_run = 0;
|
||||
break;
|
||||
case SIGHUP:
|
||||
syslog_rotate = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void signal_setup(void)
|
||||
{
|
||||
struct sigaction act;
|
||||
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = sighandler;
|
||||
|
||||
sigaction(SIGINT, &act, NULL);
|
||||
sigaction(SIGTERM, &act, NULL);
|
||||
sigaction(SIGHUP, &act, NULL);
|
||||
}
|
||||
|
||||
static int handle_data(int fd)
|
||||
{
|
||||
char buffer[2048];
|
||||
syslog_msg_t msg;
|
||||
ssize_t ret;
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
ret = read(fd, buffer, sizeof(buffer));
|
||||
if (ret <= 0)
|
||||
return -1;
|
||||
|
||||
if (syslog_msg_parse(&msg, buffer))
|
||||
return -1;
|
||||
|
||||
return logmgr->write(logmgr, &msg);
|
||||
}
|
||||
|
||||
#define GPL_URL "https://gnu.org/licenses/gpl.html"
|
||||
|
||||
static const char *version_string =
|
||||
"usyslogd (usyslog) " PACKAGE_VERSION "\n"
|
||||
"Copyright (C) 2018 David Oberhollenzer\n\n"
|
||||
"License GPLv3+: GNU GPL version 3 or later <" GPL_URL ">.\n"
|
||||
"This is free software: you are free to change and redistribute it.\n"
|
||||
"There is NO WARRANTY, to the extent permitted by law.\n";
|
||||
|
||||
static void process_options(int argc, char **argv)
|
||||
{
|
||||
struct passwd *pw = getpwnam(DEFAULT_USER);
|
||||
struct group *grp = getgrnam(DEFAULT_GROUP);
|
||||
char *end;
|
||||
int i;
|
||||
|
||||
if (pw != NULL)
|
||||
uid = pw->pw_uid;
|
||||
|
||||
if (grp != NULL)
|
||||
gid = grp->gr_gid;
|
||||
|
||||
for (;;) {
|
||||
i = getopt_long(argc, argv, short_opts, long_opts, NULL);
|
||||
if (i == -1)
|
||||
break;
|
||||
|
||||
switch (i) {
|
||||
case 'r':
|
||||
log_flags |= LOG_ROTATE_OVERWRITE;
|
||||
break;
|
||||
case 'm':
|
||||
log_flags |= LOG_ROTATE_SIZE_LIMIT;
|
||||
max_size = strtol(optarg, &end, 10);
|
||||
if (max_size == 0 || *end != '\0') {
|
||||
fputs("Numeric argument > 0 expected for -m\n",
|
||||
stderr);
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
pw = getpwnam(optarg);
|
||||
if (pw == NULL) {
|
||||
fprintf(stderr, "Cannot get UID for user %s\n",
|
||||
optarg);
|
||||
goto fail;
|
||||
}
|
||||
uid = pw->pw_uid;
|
||||
break;
|
||||
case 'g':
|
||||
grp = getgrnam(optarg);
|
||||
if (grp == NULL) {
|
||||
fprintf(stderr,
|
||||
"Cannot get GID for group %s\n",
|
||||
optarg);
|
||||
goto fail;
|
||||
}
|
||||
gid = grp->gr_gid;
|
||||
break;
|
||||
case 'c':
|
||||
dochroot = true;
|
||||
break;
|
||||
case 'h':
|
||||
fputs(usage_string, stdout);
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'V':
|
||||
fputs(version_string, stdout);
|
||||
exit(EXIT_SUCCESS);
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return;
|
||||
fail:
|
||||
fputs("Try `usyslogd --help' for more information\n", stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int chroot_setup(void)
|
||||
{
|
||||
if (mkdir(SYSLOG_PATH, 0750)) {
|
||||
if (errno != EEXIST) {
|
||||
perror("mkdir " SYSLOG_PATH);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (uid > 0 && gid > 0 && chown(SYSLOG_PATH, uid, gid) != 0) {
|
||||
perror("chown " SYSLOG_PATH);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chmod(SYSLOG_PATH, 0750)) {
|
||||
perror("chmod " SYSLOG_PATH);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chdir(SYSLOG_PATH)) {
|
||||
perror("cd " SYSLOG_PATH);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dochroot && chroot(SYSLOG_PATH) != 0) {
|
||||
perror("chroot " SYSLOG_PATH);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int user_setup(void)
|
||||
{
|
||||
if (gid > 0 && setresgid(gid, gid, gid) != 0) {
|
||||
perror("setgid");
|
||||
return -1;
|
||||
}
|
||||
if (uid > 0 && setresuid(uid, uid, uid) != 0) {
|
||||
perror("setuid");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int sfd, status = EXIT_FAILURE;
|
||||
|
||||
process_options(argc, argv);
|
||||
|
||||
signal_setup();
|
||||
|
||||
sfd = mksock(SYSLOG_SOCKET);
|
||||
if (sfd < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (uid > 0 && gid > 0 && chown(SYSLOG_SOCKET, uid, gid) != 0) {
|
||||
perror("chown " SYSLOG_SOCKET);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chroot_setup())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (user_setup())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (logmgr->init(logmgr, log_flags, max_size))
|
||||
goto out;
|
||||
|
||||
while (syslog_run) {
|
||||
if (syslog_rotate) {
|
||||
logmgr->rotate(logmgr);
|
||||
syslog_rotate = 0;
|
||||
}
|
||||
|
||||
handle_data(sfd);
|
||||
}
|
||||
|
||||
status = EXIT_SUCCESS;
|
||||
out:
|
||||
logmgr->cleanup(logmgr);
|
||||
if (sfd > 0)
|
||||
close(sfd);
|
||||
unlink(SYSLOG_SOCKET);
|
||||
return status;
|
||||
}
|
97
syslogd.h
Normal file
97
syslogd.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
/* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef SYSLOGD_H
|
||||
#define SYSLOGD_H
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
|
||||
#define SYSLOG_SOCKET "/dev/log"
|
||||
#define SYSLOG_PATH "/var/log"
|
||||
#define DEFAULT_USER "syslogd"
|
||||
#define DEFAULT_GROUP "syslogd"
|
||||
|
||||
|
||||
/*
|
||||
encapsulates the split up data from a message received
|
||||
through the local syslog socket.
|
||||
*/
|
||||
typedef struct {
|
||||
int facility;
|
||||
int level;
|
||||
time_t timestamp;
|
||||
pid_t pid;
|
||||
const char *ident;
|
||||
const char *message;
|
||||
} syslog_msg_t;
|
||||
|
||||
|
||||
enum {
|
||||
/*
|
||||
Rotate log data in a way that we still generate a continuous stream
|
||||
of log data. E.g. in the case of log files, move the current log file
|
||||
to one suffixed with a timestamp. We don't lose any log data.
|
||||
*/
|
||||
LOG_ROTATE_CONTINUOUS = 0x00,
|
||||
|
||||
/*
|
||||
Rotate log data by overwriting old data with more recent data.
|
||||
E.g. in the case of log files, move the current log file to one
|
||||
with a constant prefix, overwriting any existing data.
|
||||
*/
|
||||
LOG_ROTATE_OVERWRITE = 0x01,
|
||||
|
||||
/*
|
||||
Automatically do a log rotatation if a log stream reaches a preset
|
||||
size limit.
|
||||
*/
|
||||
LOG_ROTATE_SIZE_LIMIT = 0x10,
|
||||
};
|
||||
|
||||
typedef struct log_backend_t {
|
||||
int (*init)(struct log_backend_t *log, int flags, size_t sizelimit);
|
||||
|
||||
void (*cleanup)(struct log_backend_t *log);
|
||||
|
||||
int (*write)(struct log_backend_t *log, const syslog_msg_t *msg);
|
||||
|
||||
void (*rotate)(struct log_backend_t *log);
|
||||
} log_backend_t;
|
||||
|
||||
|
||||
extern log_backend_t *logmgr;
|
||||
|
||||
/*
|
||||
Parse a message string received from the syslog socket and produce
|
||||
a split up representation for the message.
|
||||
*/
|
||||
int syslog_msg_parse(syslog_msg_t *msg, char *str);
|
||||
|
||||
/* Create a unix DGRAM socket. */
|
||||
int mksock(const char *path);
|
||||
|
||||
const char *level_id_to_string(int level);
|
||||
|
||||
const char *facility_id_to_string(int level);
|
||||
|
||||
#endif /* LOGFILE_H */
|
Loading…
Reference in a new issue