Jakub, can you double-check that I did not add
a bug somewhere there?
I run-tested it, but code review will still be useful.
--
vda
diff -x '*.po' -d -urpN libreport.5/src/lib/logging.c
libreport.6/src/lib/logging.c
--- libreport.5/src/lib/logging.c 2013-02-07 18:03:32.912987379 +0100
+++ libreport.6/src/lib/logging.c 2013-02-07 17:48:44.235190248 +0100
@@ -26,40 +26,87 @@ int logmode = LOGMODE_STDIO;
int xfunc_error_retval = EXIT_FAILURE;
int g_verbose;
+/* [p]error_msg[_and_die] must be safe after fork in multi-threaded programs.
+ * Therefore we avoid stdio, fflush(), and use _exit() instead of exit().
+ *
+ */
void xfunc_die(void)
{
- exit(xfunc_error_retval);
+ _exit(xfunc_error_retval);
}
+/* If set to 0, will use malloc for long messages */
+#define USE_ALLOCA 1
+
static void verror_msg_helper(const char *s,
va_list p,
- const char* strerr,
+ const char *strerr,
int flags)
{
- char *msg;
- int prefix_len, strerr_len, msgeol_len, used;
-
if (!logmode)
return;
- used = vasprintf(&msg, s, p);
- if (used < 0)
- return;
-
/* This is ugly and costs +60 bytes compared to multiple
* fprintf's, but is guaranteed to do a single write.
* This is needed for e.g. when multiple children
* can produce log messages simultaneously. */
- prefix_len = msg_prefix[0] ? strlen(msg_prefix) + 2 : 0;
- strerr_len = strerr ? strlen(strerr) : 0;
- msgeol_len = strlen(msg_eol);
+ int prefix_len = msg_prefix[0] ? strlen(msg_prefix) + 2 : 0;
+ int strerr_len = strerr ? strlen(strerr) : 0;
+ int msgeol_len = strlen(msg_eol);
+ int used;
+
+ /* Try to format the message in a fixed buffer.
+ * This eliminates malloc.
+ * Main reason isn't the speed optimization, but to make
+ * short logging safe after fork in multithreaded libraries.
+ */
+ char buf[1024];
+ va_list p2;
+ va_copy(p2, p);
+ if (prefix_len < sizeof(buf))
+ used = vsnprintf(buf + prefix_len, sizeof(buf) - prefix_len, s, p2);
+ else
+ used = vsnprintf(buf, 0, s, p2);
+ va_end(p2);
+
+ char *msg = buf;
+
/* +3 is for ": " before strerr and for terminating NUL */
- msg = (char*) xrealloc(msg, prefix_len + used + strerr_len + msgeol_len +
3);
- /* TODO: maybe use writev instead of memmoving? Need full_writev? */
+ unsigned total_len = prefix_len + used + strerr_len + msgeol_len + 3;
+
+#if USE_ALLOCA
+ if (total_len >= sizeof(buf))
+ {
+ msg = alloca(total_len);
+ used = vsnprintf(msg + prefix_len, total_len - prefix_len, s, p);
+ }
+#else
+#define LOGMODE_DIE ((unsigned)INT_MAX + 1)
+ char *malloced = NULL;
+ if (total_len >= sizeof(buf))
+ {
+ /* Nope, need to malloc the buffer.
+ * Can't use xmalloc: it calls error_msg_and_die on failure,
+ * that will result in a recursion.
+ */
+ msg = malloced = malloc(total_len);
+ if (!msg)
+ {
+ /* Same as xmalloc error */
+ msg = strcpy(buf, "Out of memory, exiting\n");
+ used = strlen(msg) - 1;
+ msgeol_len = 1; /* '\n' */
+ prefix_len = 0;
+ flags |= LOGMODE_DIE;
+ goto send_it;
+ }
+ used = vsnprintf(msg + prefix_len, total_len - prefix_len, s, p);
+ }
+#endif
+
if (prefix_len) {
char *p;
- memmove(msg + prefix_len, msg, used);
used += prefix_len;
p = stpcpy(msg, msg_prefix);
p[0] = ':';
@@ -75,8 +122,11 @@ static void verror_msg_helper(const char
}
strcpy(&msg[used], msg_eol);
+#if !USE_ALLOCA
+ send_it:
+#endif
if (flags & LOGMODE_STDIO) {
- fflush(stdout);
+ /*fflush(stdout); - unsafe after fork! */
full_write(STDERR_FILENO, msg, used + msgeol_len);
}
msg[used] = '\0'; /* remove msg_eol (usually "\n") */
@@ -86,7 +136,13 @@ static void verror_msg_helper(const char
if ((flags & LOGMODE_CUSTOM) && g_custom_logger) {
g_custom_logger(msg + prefix_len);
}
- free(msg);
+
+#if !USE_ALLOCA
+ free(malloced);
+
+ if (flags & LOGMODE_DIE)
+ xfunc_die();
+#endif
}
void log_msg(const char *s, ...)