stdout-syslog-bridge.c revision 81527be142678057215665be66e4b3c8306a7ab3
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <assert.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <systemd/sd-daemon.h>
#include "util.h"
#include "log.h"
#include "list.h"
#include "tcpwrap.h"
#include "def.h"
#define STREAMS_MAX 4096
#define SERVER_FD_MAX 16
typedef struct Server {
int syslog_fd;
int kmsg_fd;
int epoll_fd;
unsigned n_server_fd;
bool syslog_is_stream;
unsigned n_streams;
} Server;
typedef enum StreamTarget {
} StreamTarget;
typedef enum StreamState {
} StreamState;
struct Stream {
int fd;
int priority;
char *process;
bool prefix:1;
bool tee_console:1;
};
int priority;
assert(s);
assert(p);
if (s->prefix)
parse_syslog_priority(&p, &priority);
if (*p == 0)
return 0;
/* Patch in configured facility if necessary */
if ((priority & LOG_FACMASK) == 0)
/*
* The format glibc uses to talk to the syslog daemon is:
*
* <priority>time process[pid]: msg
*
* The format the kernel uses is:
*
* <priority>msg\n
*
* We extend the latter to include the process name and pid.
*/
if (s->target == STREAM_SYSLOG) {
time_t t;
return -EINVAL;
return -EINVAL;
}
if (s->target == STREAM_SYSLOG) {
union {
} control;
/* When using syslog via SOCK_STREAM separate the messages by NUL chars */
if (s->server->syslog_is_stream)
for (;;) {
ssize_t n;
/* Hmm, maybe the process this
* line originates from is
* dead? Then let's patch in
* our own pid and retry,
* since we have nothing
* better */
continue;
}
}
return -errno;
}
if (!s->server->syslog_is_stream ||
break;
}
} else if (s->target == STREAM_KMSG) {
return -errno;
} else
assert_not_reached("Unknown log target");
if (s->tee_console) {
int console;
}
}
return 0;
}
int r;
assert(s);
assert(p);
p = strstrip(p);
switch (s->state) {
case STREAM_TARGET:
s->target = STREAM_SYSLOG;
s->target = STREAM_KMSG;
else {
log_warning("/dev/kmsg logging not available.");
return -EPERM;
}
} else {
log_warning("Failed to parse log target line.");
return -EBADMSG;
}
if (endswith(p, "+console"))
s->tee_console = true;
s->state = STREAM_PRIORITY;
return 0;
case STREAM_PRIORITY:
log_warning("Failed to parse log priority line: %m");
return r;
}
if (s->priority < 0) {
log_warning("Log priority negative: %m");
return -ERANGE;
}
s->state = STREAM_PROCESS;
return 0;
case STREAM_PROCESS:
return -ENOMEM;
s->state = STREAM_PREFIX;
return 0;
case STREAM_PREFIX:
if ((r = parse_boolean(p)) < 0)
return r;
s->prefix = r;
s->state = STREAM_RUNNING;
return 0;
case STREAM_RUNNING:
return stream_log(s, p, ts);
}
assert_not_reached("Unknown stream state");
}
char *p;
int r = 0;
assert(s);
p = s->buffer;
for (;;) {
char *end;
if (!end) {
} else
break;
} else
*end = 0;
r = stream_line(s, p, ts);
if (r >= 0) {
p += skip;
}
}
if (p > s->buffer) {
}
return r;
}
ssize_t l;
int r;
assert(s);
if (l < 0) {
return 0;
log_warning("Failed to read from stream: %m");
return -errno;
}
if (l == 0)
return 0;
s->length += l;
r = stream_scan(s, ts);
if (r < 0)
return r;
return 1;
}
static void stream_free(Stream *s) {
assert(s);
if (s->server) {
}
if (s->fd >= 0) {
if (s->server)
close_nointr_nofail(s->fd);
}
free(s);
}
int fd;
struct epoll_event ev;
int r;
assert(s);
if (fd < 0)
return -errno;
if (s->n_streams >= STREAMS_MAX) {
log_warning("Too many connections, refusing connection.");
return 0;
}
return 0;
}
return -ENOMEM;
}
r = -errno;
goto fail;
}
r = -errno;
goto fail;
}
r = -errno;
goto fail;
}
s->n_streams ++;
return 0;
fail:
return r;
}
static void server_done(Server *s) {
unsigned i;
assert(s);
while (s->streams)
stream_free(s->streams);
for (i = 0; i < s->n_server_fd; i++)
if (s->syslog_fd >= 0)
if (s->epoll_fd >= 0)
if (s->kmsg_fd >= 0)
}
int r;
unsigned i;
union {
struct sockaddr_un un;
} sa;
assert(s);
zero(*s);
s->n_server_fd = n_sockets;
s->syslog_fd = -1;
s->kmsg_fd = -1;
r = -errno;
log_error("Failed to create epoll object: %m");
goto fail;
}
for (i = 0; i < n_sockets; i++) {
struct epoll_event ev;
int fd;
fd = SD_LISTEN_FDS_START+i;
goto fail;
}
if (!r) {
log_error("Wrong file descriptor type.");
r = -EINVAL;
goto fail;
}
/* We use ev.data.ptr instead of ev.data.fd here,
* since on 64bit archs fd is 32bit while a pointer is
* 64bit. To make sure we can easily distinguish fd
* values and pointer values we want to make sure to
* write the full field unconditionally. */
r = -errno;
log_error("Failed to add server fd to epoll object: %m");
goto fail;
}
}
r = -errno;
log_error("Failed to create log fd: %m");
goto fail;
}
r = -errno;
log_error("Failed to create log fd: %m");
goto fail;
}
r = -errno;
goto fail;
}
s->syslog_is_stream = true;
} else
s->syslog_is_stream = false;
log_warning("Failed to open /dev/kmsg for logging, disabling kernel log buffer support: %m");
return 0;
fail:
server_done(s);
return r;
}
int r;
assert(s);
/* Yes, this is a bit ugly, we assume that that valid pointers
* are > SD_LISTEN_FDS_START+SERVER_FD_MAX. Which is certainly
* true on Linux (and probably most other OSes, too, since the
* first 4k usually are part of a separate null pointer
* dereference page. */
log_info("Got invalid event from epoll. (1)");
return -EIO;
}
return r;
}
} else {
log_info("Got invalid event from epoll. (2)");
return 0;
}
if (r < 0)
return 0;
}
}
return 0;
}
int r = EXIT_FAILURE, n;
if (getppid() != 1) {
log_error("This program should be invoked by init only.");
return EXIT_FAILURE;
}
if (argc > 1) {
log_error("This program does not take arguments.");
return EXIT_FAILURE;
}
log_open();
umask(0022);
n = sd_listen_fds(true);
if (n < 0) {
return EXIT_FAILURE;
}
if (n <= 0 || n > SERVER_FD_MAX) {
log_error("No or too many file descriptors passed.");
return EXIT_FAILURE;
}
if (server_init(&server, (unsigned) n) < 0)
return EXIT_FAILURE;
sd_notify(false,
"READY=1\n"
"STATUS=Processing requests...");
for (;;) {
struct epoll_event event;
int k;
&event, 1,
continue;
log_error("epoll_wait() failed: %m");
goto fail;
}
if (k <= 0)
break;
goto fail;
}
r = EXIT_SUCCESS;
fail:
sd_notify(false,
"STATUS=Shutting down...");
return r;
}