Hi Susant,
On Thu, Feb 19, 2015 at 8:58 AM, Susant Sahani <sus...@redhat.com> wrote: > 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 Double "the". > + > <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); What happens if forwarding is not enabled? > + 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++; When can forwarding fail that we are keeping track of it? In the end we just broadcast messages on UDP. > + > + 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(); Do you cover the case where hostname changes after we open the socket? > + 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); Don't you want to embed syslog_network_fd in to this function? If you don't, I think the name is a bit confusing as we also have s->syslog_network_fd. > + 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 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel