From 7fecac297303be09810c6b9642c675c29eb345c1 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 18 Dec 2024 16:41:05 +0100 Subject: [PATCH] package/podman: Add syslog support The only reliable way of getting logs from the container at all times is to let conmon be the logging agent. Unfortunately, the k8s-file format does not handle multiline output very well, which is something we often see on the stdout/stderr of containers. Therefore, add proper syslog support to conmon instead, and make sure that podman knows enough about it to pass the information along to conmon. --- .../0001-logging-Add-syslog-support.patch | 196 ++++++++++++++++++ .../4.5.0/0001-Disable-pull-retry.patch | 38 ++++ .../4.5.0/0001-disable-pull-retry.patch | 22 -- .../0002-Add-log-driver-for-syslog.patch | 98 +++++++++ 4 files changed, 332 insertions(+), 22 deletions(-) create mode 100644 patches/conmon/2.1.8/0001-logging-Add-syslog-support.patch create mode 100644 patches/podman/4.5.0/0001-Disable-pull-retry.patch delete mode 100644 patches/podman/4.5.0/0001-disable-pull-retry.patch create mode 100644 patches/podman/4.5.0/0002-Add-log-driver-for-syslog.patch diff --git a/patches/conmon/2.1.8/0001-logging-Add-syslog-support.patch b/patches/conmon/2.1.8/0001-logging-Add-syslog-support.patch new file mode 100644 index 000000000..be1031763 --- /dev/null +++ b/patches/conmon/2.1.8/0001-logging-Add-syslog-support.patch @@ -0,0 +1,196 @@ +From 075fcae6b463d67149fbf2adc36c6bb423364e24 Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz +Date: Wed, 18 Dec 2024 15:08:57 +0100 +Subject: [PATCH] logging: Add syslog support +Organization: Addiva Elektronik + +Signed-off-by: Tobias Waldekranz +--- + src/ctr_logging.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 125 insertions(+) + +diff --git a/src/ctr_logging.c b/src/ctr_logging.c +index e5f03f3..3bf8fc2 100644 +--- a/src/ctr_logging.c ++++ b/src/ctr_logging.c +@@ -26,10 +26,12 @@ static inline int sd_journal_sendv(G_GNUC_UNUSED const struct iovec *iov, G_GNUC + static gboolean use_journald_logging = FALSE; + static gboolean use_k8s_logging = FALSE; + static gboolean use_logging_passthrough = FALSE; ++static gboolean use_logging_syslog = FALSE; + + /* Value the user must input for each log driver */ + static const char *const K8S_FILE_STRING = "k8s-file"; + static const char *const JOURNALD_FILE_STRING = "journald"; ++static const char *const SYSLOG_FILE_STRING = "syslog"; + + /* Max log size for any log file types */ + static int64_t log_size_max = -1; +@@ -41,6 +43,9 @@ static int64_t log_global_size_max = -1; + static int k8s_log_fd = -1; + static char *k8s_log_path = NULL; + ++/* syslog parameters */ ++static int syslog_facility = LOG_LOCAL1; ++ + /* journald log file parameters */ + // short ID length + #define TRUNC_ID_LEN 12 +@@ -80,6 +85,7 @@ static void parse_log_path(char *log_config); + static const char *stdpipe_name(stdpipe_t pipe); + static int write_journald(int pipe, char *buf, ssize_t num_read); + static int write_k8s_log(stdpipe_t pipe, const char *buf, ssize_t buflen); ++static int write_syslog(int pipe, char *buf, ssize_t num_read); + static bool get_line_len(ptrdiff_t *line_len, const char *buf, ssize_t buflen); + static ssize_t writev_buffer_append_segment(int fd, writev_buffer_t *buf, const void *data, ssize_t len); + static ssize_t writev_buffer_append_segment_no_flush(writev_buffer_t *buf, const void *data, ssize_t len); +@@ -158,6 +164,10 @@ void configure_log_drivers(gchar **log_drivers, int64_t log_size_max_, int64_t l + syslog_identifier_len = strlen(syslog_identifier); + } + } ++ ++ if (use_logging_syslog) { ++ openlog(tag ? : (name_ ? : "conmon"), LOG_PID, syslog_facility); ++ } + } + + /* +@@ -213,6 +223,11 @@ static void parse_log_path(char *log_config) + return; + } + ++ if (!strcmp(driver, SYSLOG_FILE_STRING)) { ++ use_logging_syslog = TRUE; ++ return; ++ } ++ + // If no : was found, use the entire log-path as a filename to k8s-file. + if (path == NULL && delim == NULL) { + use_k8s_logging = TRUE; +@@ -234,6 +249,10 @@ bool write_to_logs(stdpipe_t pipe, char *buf, ssize_t num_read) + nwarn("write_journald failed"); + return G_SOURCE_CONTINUE; + } ++ if (use_logging_syslog && write_syslog(pipe, buf, num_read) < 0) { ++ nwarn("write_syslog failed"); ++ return G_SOURCE_CONTINUE; ++ } + return true; + } + +@@ -436,6 +455,112 @@ static int write_k8s_log(stdpipe_t pipe, const char *buf, ssize_t buflen) + return 0; + } + ++struct log_buf { ++ char data[STDIO_BUF_SIZE]; ++ ++ FILE *put; ++ char *get; ++}; ++ ++static int log_buf_reset(struct log_buf *buf) ++{ ++ if (buf->put) { ++ rewind(buf->put); ++ } else { ++ buf->put = fmemopen(buf->data, sizeof(buf->data) - 1, "a"); ++ if (!buf->put) ++ return -1; ++ ++ setbuf(buf->put, NULL); ++ } ++ ++ buf->data[0] = '\0'; ++ buf->get = buf->data; ++ return 0; ++} ++ ++static const char *log_buf_gets(struct log_buf *buf) ++{ ++ char *line, *nl; ++ ++ nl = strchr(buf->get, '\n'); ++ if (!nl) ++ return NULL; ++ ++ *nl = '\0'; ++ ++ line = buf->get; ++ buf->get = nl + 1; ++ return line; ++} ++ ++static const char *log_buf_drain(struct log_buf *buf) ++{ ++ char *text, *end; ++ ++ end = buf->data + ftell(buf->put); ++ *end = '\0'; ++ ++ text = buf->get; ++ buf->get = end; ++ return text; ++} ++ ++static int log_buf_push(struct log_buf *buf, const char *data, size_t len) ++{ ++ /* If the last log_buf_gets() completely drained the buffer, we can safely reset. */ ++ if (buf->get != buf->data ++ && ftell(buf->put) == (buf->data - buf->get) ++ && log_buf_reset(buf)) ++ return -1; ++ ++ return (fwrite(data, len, 1, buf->put) == 1) ? 0 : -1; ++} ++ ++/* write to syslog(3). If the pipe is stdout, write with notice priority, ++ * otherwise, write with error priority. Partial lines (that don't end in a newline) are buffered ++ * between invocations. A 0 buflen argument forces a buffered partial line to be flushed. ++ */ ++static int write_syslog(int pipe, char *buf, ssize_t buflen) ++{ ++ static struct log_buf stdout = { 0 }, stderr = { 0 }; ++ struct log_buf *b; ++ const char *line; ++ int prio; ++ ++ switch (pipe) { ++ case STDOUT_PIPE: ++ b = &stdout; ++ prio = LOG_NOTICE; ++ break; ++ case STDERR_PIPE: ++ b = &stderr; ++ prio = LOG_ERR; ++ break; ++ default: ++ return -1; ++ } ++ ++ if (!b->put && log_buf_reset(b)) ++ return -1; ++ ++ if (!buflen) { ++ line = log_buf_drain(b); ++ if (*line) { ++ syslog(prio, line); ++ return 0; ++ } ++ } ++ ++ if (log_buf_push(b, buf, buflen)) ++ return -1; ++ ++ while ((line = log_buf_gets(b))) { ++ syslog(prio, line); ++ } ++ return 0; ++} ++ + /* Find the end of the line, or alternatively the end of the buffer. + * Returns false in the former case (it's a whole line) or true in the latter (it's a partial) + */ +-- +2.43.0 + diff --git a/patches/podman/4.5.0/0001-Disable-pull-retry.patch b/patches/podman/4.5.0/0001-Disable-pull-retry.patch new file mode 100644 index 000000000..1a3dc472f --- /dev/null +++ b/patches/podman/4.5.0/0001-Disable-pull-retry.patch @@ -0,0 +1,38 @@ +From 3c8de9439a5718018a97fb4369c02d69f88853f8 Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz +Date: Wed, 18 Dec 2024 16:33:38 +0100 +Subject: [PATCH 1/2] Disable pull retry +Organization: Addiva Elektronik + +This patch disables the default "podman pull" retry value, which is +not used by Infix. Instead, the container wrapper script retries on +network related changes, or every 60 seconds. + +As of podman v5.0.0 a '--retry=NUM' has been added to the podman +create, run, and pull commands. However, CNI is no longer supported, +and a lot of other breaking changes have been made, eg., output of +podman inspect. So there's a lot of work to upgrade. + + -- Joachim + +Signed-off-by: Tobias Waldekranz +--- + vendor/github.com/containers/common/libimage/copier.go | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/vendor/github.com/containers/common/libimage/copier.go b/vendor/github.com/containers/common/libimage/copier.go +index 5f277a69e..af980af3b 100644 +--- a/vendor/github.com/containers/common/libimage/copier.go ++++ b/vendor/github.com/containers/common/libimage/copier.go +@@ -24,7 +24,7 @@ import ( + ) + + const ( +- defaultMaxRetries = 3 ++ defaultMaxRetries = 0 + defaultRetryDelay = time.Second + ) + +-- +2.43.0 + diff --git a/patches/podman/4.5.0/0001-disable-pull-retry.patch b/patches/podman/4.5.0/0001-disable-pull-retry.patch deleted file mode 100644 index 2bbbdf370..000000000 --- a/patches/podman/4.5.0/0001-disable-pull-retry.patch +++ /dev/null @@ -1,22 +0,0 @@ -This patch disables the default "podman pull" retry value, which is not -used by Infix. Instead, the container wrapper script retries on network -related changes, or every 60 seconds. - -As of podman v5.0.0 a '--retry=NUM' has been added to the podman create, -run, and pull commands. However, CNI is no longer supported, and a lot -of other breaking changes have been made, eg., output of podman inspect. -So there's a lot of work to upgrade. - - -- Joachim - ---- a/vendor/github.com/containers/common/libimage/copier.go 2023-04-14 15:28:20.000000000 +0200 -+++ b/vendor/github.com/containers/common/libimage/copier.go 2024-11-16 13:05:42.207641898 +0100 -@@ -24,7 +24,7 @@ - ) - - const ( -- defaultMaxRetries = 3 -+ defaultMaxRetries = 0 - defaultRetryDelay = time.Second - ) - diff --git a/patches/podman/4.5.0/0002-Add-log-driver-for-syslog.patch b/patches/podman/4.5.0/0002-Add-log-driver-for-syslog.patch new file mode 100644 index 000000000..9d06e8599 --- /dev/null +++ b/patches/podman/4.5.0/0002-Add-log-driver-for-syslog.patch @@ -0,0 +1,98 @@ +From 81b3c16e6f1a3aac7ab31157e3a199af52faeec8 Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz +Date: Wed, 18 Dec 2024 16:25:29 +0100 +Subject: [PATCH 2/2] Add log driver for syslog +Organization: Addiva Elektronik + +Signed-off-by: Tobias Waldekranz +--- + cmd/podman/common/completion.go | 2 +- + libpod/container_log_linux.go | 2 +- + libpod/define/config.go | 3 +++ + libpod/oci_conmon_common.go | 2 ++ + libpod/options.go | 2 +- + libpod/runtime_ctr.go | 2 +- + 6 files changed, 9 insertions(+), 4 deletions(-) + +diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go +index 6c7400228..69195558f 100644 +--- a/cmd/podman/common/completion.go ++++ b/cmd/podman/common/completion.go +@@ -957,7 +957,7 @@ func AutocompleteImageVolume(cmd *cobra.Command, args []string, toComplete strin + // -> "journald", "none", "k8s-file", "passthrough" + func AutocompleteLogDriver(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // don't show json-file +- logDrivers := []string{define.JournaldLogging, define.NoLogging, define.KubernetesLogging} ++ logDrivers := []string{define.JournaldLogging, define.NoLogging, define.KubernetesLogging, define.SyslogLogging} + if !registry.IsRemote() { + logDrivers = append(logDrivers, define.PassthroughLogging) + } +diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go +index a708ad46d..b675f2e8d 100644 +--- a/libpod/container_log_linux.go ++++ b/libpod/container_log_linux.go +@@ -28,7 +28,7 @@ const ( + ) + + func init() { +- logDrivers = append(logDrivers, define.JournaldLogging) ++ logDrivers = append(logDrivers, define.JournaldLogging, define.SyslogLogging) + } + + func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOptions, +diff --git a/libpod/define/config.go b/libpod/define/config.go +index 7295f1425..66abfd0d9 100644 +--- a/libpod/define/config.go ++++ b/libpod/define/config.go +@@ -82,6 +82,9 @@ const NoLogging = "none" + // PassthroughLogging is the string conmon expects when specifying to use the passthrough driver + const PassthroughLogging = "passthrough" + ++// SyslogLogging is the string conmon expects when specifying to use the syslog driver ++const SyslogLogging = "syslog" ++ + // DefaultRlimitValue is the value set by default for nofile and nproc + const RLimitDefaultValue = uint64(1048576) + +diff --git a/libpod/oci_conmon_common.go b/libpod/oci_conmon_common.go +index 6dd54a8bc..f3696d8af 100644 +--- a/libpod/oci_conmon_common.go ++++ b/libpod/oci_conmon_common.go +@@ -1318,6 +1318,8 @@ func (r *ConmonOCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, p + switch logDriver { + case define.JournaldLogging: + logDriverArg = define.JournaldLogging ++ case define.SyslogLogging: ++ logDriverArg = define.SyslogLogging + case define.NoLogging: + logDriverArg = define.NoLogging + case define.PassthroughLogging: +diff --git a/libpod/options.go b/libpod/options.go +index bc70e4a32..35047bade 100644 +--- a/libpod/options.go ++++ b/libpod/options.go +@@ -1137,7 +1137,7 @@ func WithLogDriver(driver string) CtrCreateOption { + switch driver { + case "": + return fmt.Errorf("log driver must be set: %w", define.ErrInvalidArg) +- case define.JournaldLogging, define.KubernetesLogging, define.JSONLogging, define.NoLogging, define.PassthroughLogging: ++ case define.JournaldLogging, define.KubernetesLogging, define.JSONLogging, define.NoLogging, define.PassthroughLogging, define.SyslogLogging: + break + default: + return fmt.Errorf("invalid log driver: %w", define.ErrInvalidArg) +diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go +index 648118c37..70582635f 100644 +--- a/libpod/runtime_ctr.go ++++ b/libpod/runtime_ctr.go +@@ -543,7 +543,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai + } + + switch ctr.config.LogDriver { +- case define.NoLogging, define.PassthroughLogging, define.JournaldLogging: ++ case define.NoLogging, define.PassthroughLogging, define.JournaldLogging, define.SyslogLogging: + break + default: + if ctr.config.LogPath == "" { +-- +2.43.0 +