diff --git a/src/log.c b/src/log.c index 941bea6f..ce0cf7b0 100644 --- a/src/log.c +++ b/src/log.c @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -61,6 +62,16 @@ static GHashTable *logs; static GHashTable *groupchat_logs; static GDateTime *session_started; +enum { + STDERR_BUFSIZE = 4000, + STDERR_RETRY_NR = 5, +}; +static int stderr_inited; +static log_level_t stderr_level; +static int stderr_pipe[2]; +static char *stderr_buf; +static GString *stderr_msg; + struct dated_chat_log { gchar *filename; GDateTime *date; @@ -690,3 +701,95 @@ _log_string_from_level(log_level_t level) return "LOG"; } } + +void +log_stderr_handler(void) +{ + GString * const s = stderr_msg; + char * const buf = stderr_buf; + ssize_t size; + int retry = 0; + int i; + + if (!stderr_inited) + return; + + do { + size = read(stderr_pipe[0], buf, STDERR_BUFSIZE); + if (size == -1 && errno == EINTR && retry++ < STDERR_RETRY_NR) + continue; + if (size <= 0 || retry++ >= STDERR_RETRY_NR) + break; + + for (i = 0; i < size; ++i) { + if (buf[i] == '\n') { + log_msg(stderr_level, "stderr", s->str); + g_string_assign(s, ""); + } else + g_string_append_c(s, buf[i]); + } + } while (1); + + if (s->len > 0 && s->str[0] != '\0') { + log_msg(stderr_level, "stderr", s->str); + g_string_assign(s, ""); + } +} + +void +log_stderr_init(log_level_t level) +{ + int rc; + int flags; + + rc = pipe(stderr_pipe); + if (rc != 0) + goto err; + + flags = fcntl(stderr_pipe[0], F_GETFL); + rc = fcntl(stderr_pipe[0], F_SETFL, flags | O_NONBLOCK); + if (rc != 0) + goto err_close; + + close(STDERR_FILENO); + rc = dup2(stderr_pipe[1], STDERR_FILENO); + if (rc < 0) + goto err_close; + + stderr_buf = malloc(STDERR_BUFSIZE); + stderr_msg = g_string_sized_new(STDERR_BUFSIZE); + stderr_level = level; + stderr_inited = 1; + + if (stderr_buf == NULL || stderr_msg == NULL) { + errno = ENOMEM; + goto err_free; + } + return; + +err_free: + if (stderr_msg != NULL) + g_string_free(stderr_msg, TRUE); + free(stderr_buf); +err_close: + close(stderr_pipe[0]); + close(stderr_pipe[1]); +err: + stderr_inited = 0; + log_error("Unable to init stderr log handler: %s", strerror(errno)); +} + +void +log_stderr_close(void) +{ + if (!stderr_inited) + return; + + /* handle remaining logs before close */ + log_stderr_handler(); + stderr_inited = 0; + free(stderr_buf); + g_string_free(stderr_msg, TRUE); + close(stderr_pipe[0]); + close(stderr_pipe[1]); +} diff --git a/src/log.h b/src/log.h index 3c98c3bd..df36b453 100644 --- a/src/log.h +++ b/src/log.h @@ -63,6 +63,10 @@ void log_msg(log_level_t level, const char * const area, const char * const msg); log_level_t log_level_from_string(char *log_level); +void log_stderr_init(log_level_t level); +void log_stderr_close(void); +void log_stderr_handler(void); + void chat_log_init(void); void chat_log_msg_out(const char * const barejid, const char * const msg); diff --git a/src/profanity.c b/src/profanity.c index f65d22e0..a56eb5e9 100644 --- a/src/profanity.c +++ b/src/profanity.c @@ -89,6 +89,7 @@ prof_run(const int disable_tls, char *log_level, char *account_name) char *line = NULL; while(cont) { + log_stderr_handler(); _check_autoaway(); line = ui_readline(); @@ -225,6 +226,7 @@ _init(const int disable_tls, char *log_level) log_level_t prof_log_level = log_level_from_string(log_level); prefs_load(); log_init(prof_log_level); + log_stderr_init(PROF_LEVEL_ERROR); if (strcmp(PACKAGE_STATUS, "development") == 0) { #ifdef HAVE_GIT_VERSION log_info("Starting Profanity (%sdev.%s.%s)...", PACKAGE_VERSION, PROF_GIT_BRANCH, PROF_GIT_REVISION); @@ -283,6 +285,7 @@ _shutdown(void) theme_close(); accounts_close(); cmd_uninit(); + log_stderr_close(); log_close(); prefs_close(); } diff --git a/tests/unittests/log/stub_log.c b/tests/unittests/log/stub_log.c index c25057d8..bf9d6975 100644 --- a/tests/unittests/log/stub_log.c +++ b/tests/unittests/log/stub_log.c @@ -49,6 +49,10 @@ log_level_t log_level_from_string(char *log_level) return (log_level_t)mock(); } +void log_stderr_init(log_level_t level) {} +void log_stderr_close(void) {} +void log_stderr_handler(void) {} + void chat_log_init(void) {} void chat_log_msg_out(const char * const barejid, const char * const msg) {}