Verified Commit 64718709 authored by Karel Koci's avatar Karel Koci 🤘
Browse files

Add logging support for subprocess

This is code that hooks logging to subprocesses.
parent 936e5b8e
......@@ -153,3 +153,65 @@ enum log_level log_level_get(const char *level) {
}
return LL_UNKNOWN;
}
static const char *type_string[] = {
[LST_PKG_SCRIPT] = "pkg-script",
[LST_HOOK] = "hook"
};
// log_subproc cookie
struct c_log_subproc {
bool err; // Is this out or err
struct log_subproc *lsp;
};
static ssize_t c_log_subproc_write(void *cookie, const char *buf, size_t size) {
struct c_log_subproc *cls = (struct c_log_subproc*)cookie;
int len = fwrite(buf, sizeof(char), size, cls->err ? stderr : stdout);
// This is memory buffer so there should be no problem to match system output
ASSERT(fwrite(buf, sizeof(char), len, cls->lsp->buffer.f) == len);
return len;
}
static int c_log_subproc_close(void *cookie) {
struct c_log_subproc *cls = (struct c_log_subproc*)cookie;
free(cls);
return 0;
}
void log_subproc_open(struct log_subproc *lsp, enum log_subproc_type type, const char *message) {
lsp->type = type;
lsp->buffer.f = open_memstream(&lsp->buffer.buf, &lsp->buffer.size);
cookie_io_functions_t fncs = {
// LS_FAIL.read = NULL,
.write = c_log_subproc_write,
.seek = NULL,
.close = c_log_subproc_close
};
// out
struct c_log_subproc *cls = malloc(sizeof *cls);
cls->err = false;
cls->lsp = lsp;
lsp->out = fopencookie(cls, "w", fncs);
// err
// cppcheck-suppress memleak ;; (cls is passed as cookie and freed on stream close)
cls = malloc(sizeof *cls);
cls->err = true;
cls->lsp = lsp;
lsp->err = fopencookie(cls, "w", fncs);
// Print info
INFO("%s: %s", type_string[type], message);
}
void log_subproc_close(struct log_subproc *lsp, int exit_code, char **output) {
fclose(lsp->out);
fclose(lsp->err);
fclose(lsp->buffer.f);
if (output)
*output = lsp->buffer.buf;
else
free(lsp->buffer.buf);
if (exit_code)
WARN("%s exited with exit code: %d", type_string[lsp->type], exit_code);
}
......@@ -19,6 +19,7 @@
#ifndef UPDATER_LOGGING_H
#define UPDATER_LOGGING_H
#define _GNU_SOURCE
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
......@@ -98,4 +99,30 @@ void log_syslog_name(const char *name);
void log_stderr_level(enum log_level level);
void setup_logging(enum log_level tty, enum log_level syslog);
// Following functions are intended to be used with subprocess functions to log
// out of subprocess in consistent way.
// You can pass pointer to char* variable to receive complete output from
// subprocess when log_subproc_close is called. Passing NULL results to buffer to
// be dropped. Note that it's your responsibility to free used memory by output
// afterward.
enum log_subproc_type {
LST_PKG_SCRIPT, // This is post/pre install/rm script
LST_HOOK // This is updater's hook
};
struct log_subproc {
// Use following streams
FILE *out, *err;
// Don't edit following fields
enum log_subproc_type type;
struct {
FILE *f;
char *buf;
size_t size;
} buffer; // Buffer for all output/input
};
void log_subproc_open(struct log_subproc *ls, enum log_subproc_type type, const char *message) __attribute__((nonnull));
void log_subproc_close(struct log_subproc *ls, int exit_code, char **output) __attribute__((nonnull(1)));
#endif
......@@ -29,7 +29,6 @@
#include <sys/select.h>
#include <time.h>
#include <sys/wait.h>
#include "logging.h"
static int kill_timeout = 3;
......@@ -76,6 +75,16 @@ int subprocl(int timeout, const char *cmd, const char *args[]) {
}
int subproclo(int timeout, FILE *fd[2], const char *cmd, const char *args[]) {
struct log_buffer log;
log_buffer_init(&log, LL_TRACE);
if (log.f) {
fprintf(log.f, "Running subprocess: %s", cmd);
for (const char **p = args; *p; p++)
fprintf(log.f, " %s", *p);
fclose(log.f);
TRACE("%s", log.char_buffer);
free(log.char_buffer);
}
// Prepare pipes for stdout and stderr
int p_err[2], p_out[2];
pipe2(p_err, O_NONBLOCK);
......@@ -160,3 +169,29 @@ int vsubprocvo(int timeout, FILE *fd[2], const char *cmd, va_list args) {
void subproc_kill_t(int timeout) {
kill_timeout = timeout;
}
int lsubprocv(enum log_subproc_type type, const char *message, char **output, int timeout, const char *cmd, ...) {
va_list va_args;
va_start(va_args, cmd);
int ec = lvsubprocv(type, message, output, timeout, cmd, va_args);
va_end(va_args);
return ec;
}
int lsubprocl(enum log_subproc_type type, const char *message, char **output, int timeout, const char *cmd, const char *args[]) {
struct log_subproc lsp;
log_subproc_open(&lsp, type, message);
FILE *fds[] = {lsp.out, lsp.err};
int ec = subproclo(timeout, fds, cmd, args);
log_subproc_close(&lsp, ec, output);
return ec;
}
int lvsubprocv(enum log_subproc_type type, const char *message, char **output, int timeout, const char *cmd, va_list args) {
struct log_subproc lsp;
log_subproc_open(&lsp, type, message);
FILE *fds[] = {lsp.out, lsp.err};
int ec = vsubprocvo(timeout, fds, cmd, args);
log_subproc_close(&lsp, ec, output);
return ec;
}
......@@ -21,6 +21,7 @@
#include <stdarg.h>
#include <stdio.h>
#include "logging.h"
/*
This runs non-interactive programs as subprocess. It closes stdin and pipes stdout
......@@ -39,9 +40,16 @@ int subprocl(int timeout, const char *command, const char *args[]);
int subproclo(int timeout, FILE *fd[2], const char *command, const char *args[]);
int vsubprocv(int timeout, const char *command, va_list args);
int vsubprocvo(int timeout, FILE *fd[2], const char *command, va_list args);
// TODO probably allow stdin passtrough?
// TODO allow specifying env
// Set subproc kill timeout. This is timeout used when primary timeout runs out
// and SIGTERM is send but process still doesn't dies.
void subproc_kill_t(int timeout);
// Following functions integrate log_subproc with subproc to enable logging of subprocess output.
int lsubprocv(enum log_subproc_type type, const char *message, char **output, int timeout, const char *command, ...);
int lsubprocl(enum log_subproc_type type, const char *message, char **output, int timeout, const char *command, const char *args[]);
int lvsubprocv(enum log_subproc_type type, const char *message, char **output, int timeout, const char *command, va_list args);
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment