This patch adds support for RFC 5424 syslog format to journald. Journald can now forward logs to a multicast UDP group.
RFC 5424 format: <PRI>VERSION SP TIMESTAMP SP HOSTNAME SP APP-NAME SP PROCID SP MSGID SP [SD-ID]s SP MSG Example conf: file: journald.conf SysLogAddress=239.0.0.1:6000 --- Makefile.am | 1 + man/journald.conf.xml | 12 ++ src/journal/journald-gperf.gperf | 1 + src/journal/journald-native.c | 3 + src/journal/journald-server.c | 40 +++++- src/journal/journald-server.h | 14 ++ src/journal/journald-stream.c | 4 + src/journal/journald-syslog-network.c | 246 ++++++++++++++++++++++++++++++++++ src/journal/journald-syslog.c | 3 + src/journal/journald-syslog.h | 2 + 10 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 src/journal/journald-syslog-network.c diff --git a/Makefile.am b/Makefile.am index ba63f68..b015f69 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4487,6 +4487,7 @@ libsystemd_journal_core_la_SOURCES = \ src/journal/journald-kmsg.h \ src/journal/journald-syslog.c \ src/journal/journald-syslog.h \ + src/journal/journald-syslog-network.c \ src/journal/journald-stream.c \ src/journal/journald-stream.h \ src/journal/journald-server.c \ diff --git a/man/journald.conf.xml b/man/journald.conf.xml index 364b58f..4fb037b 100644 --- a/man/journald.conf.xml +++ b/man/journald.conf.xml @@ -355,6 +355,18 @@ </varlistentry> <varlistentry> + <term><varname>SysLogAddress=</varname></term> + <listitem><para>Controls whether log messages received by the + journal daemon shall be forwarded to a multicast UDP network + group in syslog RFC 5424 format.</para> + + <para>The the address string format is similar to socket units. See + <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>1</manvolnum></citerefentry> + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><varname>TTYPath=</varname></term> <listitem><para>Change the console TTY to use if diff --git a/src/journal/journald-gperf.gperf b/src/journal/journald-gperf.gperf index 74554c1..9cdffbc 100644 --- a/src/journal/journald-gperf.gperf +++ b/src/journal/journald-gperf.gperf @@ -40,3 +40,4 @@ Journal.MaxLevelKMsg, config_parse_log_level, 0, offsetof(Server, max_lev Journal.MaxLevelConsole, config_parse_log_level, 0, offsetof(Server, max_level_console) Journal.MaxLevelWall, config_parse_log_level, 0, offsetof(Server, max_level_wall) Journal.SplitMode, config_parse_split_mode, 0, offsetof(Server, split_mode) +Journal.SysLogAddress, config_parse_syslog_network_address, 0, offsetof(Server, syslog_addr) diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index 851625d..9fd370f 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -273,6 +273,9 @@ void server_process_native_message( if (s->forward_to_syslog) server_forward_syslog(s, priority, identifier, message, ucred, tv); + if (s->forward_to_network) + server_forward_syslog_network(s, priority, identifier, message, ucred, tv); + if (s->forward_to_kmsg) server_forward_kmsg(s, priority, identifier, message, ucred); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 7ee8174..de4ef50 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -86,7 +86,7 @@ static const char* const split_mode_table[_SPLIT_MAX] = { DEFINE_STRING_TABLE_LOOKUP(split_mode, SplitMode); DEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode, "Failed to parse split mode setting"); -static uint64_t available_space(Server *s, bool verbose) { +uint64_t available_space(Server *s, bool verbose) { char ids[33]; _cleanup_free_ char *p = NULL; sd_id128_t machine; @@ -1356,6 +1356,35 @@ static int server_parse_config_file(Server *s) { false, s); } +int config_parse_syslog_network_address(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Server *s = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = socket_address_parse(&s->syslog_addr, rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to parse address value, ignoring: %s", rvalue); + return 0; + } + + s->forward_to_network = true; + return 0; +} + static int server_dispatch_sync(sd_event_source *es, usec_t t, void *userdata) { Server *s = userdata; @@ -1578,6 +1607,10 @@ int server_init(Server *s) { if (r < 0) return r; + r = server_open_syslog_network_socket(s); + if (r < 0) + log_error_errno(r, "Failed to open syslog network socket. Ignoring: %m."); + r = server_open_native_socket(s); if (r < 0) return r; @@ -1673,6 +1706,7 @@ void server_done(Server *s) { sd_event_unref(s->event); safe_close(s->syslog_fd); + safe_close(s->syslog_network_fd); safe_close(s->native_fd); safe_close(s->stdout_fd); safe_close(s->dev_kmsg_fd); @@ -1682,6 +1716,9 @@ void server_done(Server *s) { if (s->rate_limit) journal_rate_limit_free(s->rate_limit); + if (s->syslog_network_rate_limit) + journal_rate_limit_free(s->syslog_network_rate_limit); + if (s->kernel_seqnum) munmap(s->kernel_seqnum, sizeof(uint64_t)); @@ -1689,6 +1726,7 @@ void server_done(Server *s) { free(s->tty_path); free(s->cgroup_root); free(s->hostname_field); + free(s->hostname); if (s->mmap) mmap_cache_unref(s->mmap); diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index c96877c..ba9e456 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -27,6 +27,7 @@ #include <sys/types.h> #include <sys/socket.h> +#include "socket-util.h" #include "sd-event.h" #include "journal-file.h" #include "hashmap.h" @@ -56,6 +57,7 @@ typedef struct StdoutStream StdoutStream; typedef struct Server { int syslog_fd; + int syslog_network_fd; int native_fd; int stdout_fd; int dev_kmsg_fd; @@ -86,6 +88,8 @@ typedef struct Server { size_t buffer_size; JournalRateLimit *rate_limit; + JournalRateLimit *syslog_network_rate_limit; + usec_t sync_interval_usec; usec_t rate_limit_interval; unsigned rate_limit_burst; @@ -98,12 +102,15 @@ typedef struct Server { bool forward_to_kmsg; bool forward_to_syslog; + bool forward_to_network; bool forward_to_console; bool forward_to_wall; unsigned n_forward_syslog_missed; usec_t last_warn_forward_syslog_missed; + unsigned n_forward_syslog_network_missed; + uint64_t cached_available_space; usec_t cached_available_space_timestamp; @@ -140,6 +147,9 @@ typedef struct Server { char machine_id_field[sizeof("_MACHINE_ID=") + 32]; char boot_id_field[sizeof("_BOOT_ID=") + 32]; char *hostname_field; + char *hostname; + + SocketAddress syslog_addr; /* Cached cgroup root, so that we don't have to query that all the time */ char *cgroup_root; @@ -166,13 +176,17 @@ int config_parse_split_mode(const char *unit, const char *filename, unsigned lin const char *split_mode_to_string(SplitMode s) _const_; SplitMode split_mode_from_string(const char *s) _pure_; +int config_parse_syslog_network_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue,void *data, void *userdata); + void server_fix_perms(Server *s, JournalFile *f, uid_t uid); int server_init(Server *s); void server_done(Server *s); void server_sync(Server *s); void server_vacuum(Server *s); void server_rotate(Server *s); +uint64_t available_space(Server *s, bool verbose); int server_schedule_sync(Server *s, int priority); int server_flush_to_var(Server *s); void server_maybe_append_tags(Server *s); int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata); +int server_open_syslog_network_socket(Server *s); diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 942a857..ccc10a3 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -69,6 +69,7 @@ struct StdoutStream { int priority; bool level_prefix:1; bool forward_to_syslog:1; + bool forward_to_network:1; bool forward_to_kmsg:1; bool forward_to_console:1; @@ -243,6 +244,9 @@ static int stdout_stream_log(StdoutStream *s, const char *p) { if (s->forward_to_syslog || s->server->forward_to_syslog) server_forward_syslog(s->server, syslog_fixup_facility(priority), s->identifier, p, &s->ucred, NULL); + if (s->forward_to_network || s->server->forward_to_network) + server_forward_syslog_network(s->server, syslog_fixup_facility(priority), s->identifier, p, &s->ucred, NULL); + if (s->forward_to_kmsg || s->server->forward_to_kmsg) server_forward_kmsg(s->server, priority, s->identifier, p, &s->ucred); diff --git a/src/journal/journald-syslog-network.c b/src/journal/journald-syslog-network.c new file mode 100644 index 0000000..0f7b494 --- /dev/null +++ b/src/journal/journald-syslog-network.c @@ -0,0 +1,246 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 Susant Sahani + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <unistd.h> +#include <stddef.h> +#include <poll.h> + +#include "shared/in-addr-util.h" +#include "journald-server.h" +#include "journald-syslog.h" +#include "systemd/sd-messages.h" + +/* Warn once every 30s if we missed syslog message */ +#define WARN_FORWARD_SYSLOG_MISSED_ID "syslog-network-missed" + +#define RFC_5424_NILVALUE "-" +#define RFC_5424_PROTOCOL 1 + +static void server_maybe_warn_forward_syslog_network_missed(Server *s, int priority) { + int r; + + assert(s); + + if (s->n_forward_syslog_network_missed <= 0) + return; + + r = journal_rate_limit_test(s->syslog_network_rate_limit, WARN_FORWARD_SYSLOG_MISSED_ID, + priority & LOG_PRIMASK, available_space(s, false)); + if (r == 0) + return; + + server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, + "Forwarding to syslog network missed %u messages.", + s->n_forward_syslog_network_missed); + + s->n_forward_syslog_network_missed = 0; +} + +static int syslog_network_send(Server *s, struct iovec *iovec, unsigned n_iovec, int priority) { + struct msghdr mh = { }; + + assert(s); + assert(iovec); + assert(n_iovec > 0); + + mh.msg_iov = iovec; + mh.msg_iovlen = n_iovec; + + if (s->syslog_addr.sockaddr.sa.sa_family == AF_INET) { + mh.msg_name = &s->syslog_addr.sockaddr.sa; + mh.msg_namelen = sizeof(s->syslog_addr.sockaddr.sa); + } else if (s->syslog_addr.sockaddr.sa.sa_family == AF_INET6) { + mh.msg_name = &s->syslog_addr.sockaddr.in6; + mh.msg_namelen = sizeof(s->syslog_addr.sockaddr.in6); + } else + return -EAFNOSUPPORT; + + if (sendmsg(s->syslog_network_fd, &mh, MSG_NOSIGNAL) >= 0) + return 0; + + s->n_forward_syslog_network_missed++; + + server_maybe_warn_forward_syslog_network_missed(s, priority); + + return 0; +} + +/* RFC3339 timestamp format: YYYY-MM-DDTHH:MM:SS[.frac]<+/->ZZ:ZZ */ +void format_rfc3339_timestamp(const struct timeval *tv, char *header_time, size_t header_size) { + char gm_buf[sizeof("+0530") + 1]; + struct tm tm; + time_t t; + + t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC)); + localtime_r(&t, &tm); + + strftime(header_time, header_size, "%Y-%m-%dT%T", &tm); + + /* add fractional part */ + if (tv) + snprintf(header_time + strlen(header_time), header_size, ".%06ld", tv->tv_usec); + + /* format the timezone according to RFC */ + xstrftime(gm_buf, "%z", &tm); + snprintf(header_time + strlen(header_time), header_size, "%.3s:%.2s ", gm_buf, gm_buf + 3); +} + +/* The Syslog Protocol RFC5424 format : + * <PRI>VERSION SP TIMESTAMP SP HOSTNAME SP APP-NAME SP PROCID SP MSGID SP [SD-ID]s SP MSG + */ +void server_forward_syslog_network(Server *s, + int priority, + const char *identifier, + const char *message, + const struct ucred *ucred, + const struct timeval *tv) { + char header_pid[DECIMAL_STR_MAX(pid_t) + 1]; + char header_priority[sizeof("< >1 ") + 1]; + char header_time[FORMAT_TIMESTAMP_MAX]; + struct iovec iov[13]; + int n = 0; + + assert(s); + assert(priority >= 0); + assert(priority <= 999); + assert(message); + + if (LOG_PRI(priority) > s->max_level_syslog) + return; + + /* First: priority field Second: Version '<pri>version' */ + snprintf(header_priority, sizeof(header_priority), "<%i>%i ", priority, RFC_5424_PROTOCOL); + IOVEC_SET_STRING(iov[n++], header_priority); + + /* Third: timestamp */ + format_rfc3339_timestamp(tv, header_time, sizeof(header_time)); + IOVEC_SET_STRING(iov[n++], header_time); + + /* Fourth: hostname */ + if (s->hostname) { + IOVEC_SET_STRING(iov[n++], s->hostname); + IOVEC_SET_STRING(iov[n++], " "); + } + + /* Fifth: app-name or tag */ + if (identifier) { + IOVEC_SET_STRING(iov[n++], identifier); + IOVEC_SET_STRING(iov[n++], " "); + } else { + IOVEC_SET_STRING(iov[n++], RFC_5424_NILVALUE); + IOVEC_SET_STRING(iov[n++], " "); + } + + /* Sixth: procid */ + if (ucred) { + xsprintf(header_pid, PID_FMT , ucred->pid); + + IOVEC_SET_STRING(iov[n++], header_pid); + IOVEC_SET_STRING(iov[n++], " "); + } else { + IOVEC_SET_STRING(iov[n++], RFC_5424_NILVALUE); + IOVEC_SET_STRING(iov[n++], " "); + } + + /* Seventh: msgid */ + IOVEC_SET_STRING(iov[n++], RFC_5424_NILVALUE); + IOVEC_SET_STRING(iov[n++], " "); + + /* Eighth: [structured-data] */ + IOVEC_SET_STRING(iov[n++], RFC_5424_NILVALUE); + IOVEC_SET_STRING(iov[n++], " "); + + /* Ninth: message */ + IOVEC_SET_STRING(iov[n++], message); + + syslog_network_send(s, iov, n, priority); +} + +static int syslog_network_fd(Server *s) { + const int ttl = 255; + const int one = 1; + int fd, r; + + assert(s); + + fd = socket(s->syslog_addr.sockaddr.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) + return -errno; + + r = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + s->syslog_network_fd = fd; + + return fd; + + fail: + fd = safe_close(fd); + return r; +} + +int server_open_syslog_network_socket(Server *s) { + int r; + + assert(s); + + s->hostname = gethostname_malloc(); + if (!s->hostname) { + r = -ENOMEM; + goto fail; + } + + if (s->syslog_addr.sockaddr.sa.sa_family == AF_INET || s->syslog_addr.sockaddr.sa.sa_family == AF_INET6) { + + r = syslog_network_fd(s); + if (r < 0) + goto fail; + } else { + r = -EAFNOSUPPORT; + goto fail; + } + + s->syslog_network_rate_limit = journal_rate_limit_new(s->rate_limit_interval, s->rate_limit_burst); + if (!s->syslog_network_rate_limit) { + r = -ENOMEM; + goto fail; + } + + return r; + fail: + s->forward_to_network = false; + return r; +} diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index 7d545ca..4ecbc43 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -336,6 +336,9 @@ void server_process_syslog_message( syslog_skip_date((char**) &buf); syslog_parse_identifier(&buf, &identifier, &pid); + if (s->forward_to_network) + server_forward_syslog_network(s, priority, identifier, buf, ucred, tv); + if (s->forward_to_kmsg) server_forward_kmsg(s, priority, identifier, buf, ucred); diff --git a/src/journal/journald-syslog.h b/src/journal/journald-syslog.h index 3774ebd..b239825 100644 --- a/src/journal/journald-syslog.h +++ b/src/journal/journald-syslog.h @@ -26,8 +26,10 @@ int syslog_fixup_facility(int priority) _const_; size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid); +void format_rfc3339_timestamp(const struct timeval *tv, char *header_time, size_t header_size); void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv); +void server_forward_syslog_network(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv); void server_process_syslog_message(Server *s, const char *buf, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len); int server_open_syslog_socket(Server *s); -- 2.1.0 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel