Initial import

Signed-off-by: David Oberhollenzer <david.oberhollenzer@tele2.at>
This commit is contained in:
David Oberhollenzer 2018-10-28 12:10:07 +01:00
commit a4423189ab
12 changed files with 1260 additions and 0 deletions

19
.gitignore vendored Normal file
View 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
View 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
View File

@ -0,0 +1,3 @@
#!/bin/sh
autoreconf --force --install --symlink

35
configure.ac Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 */