static int limiter_apply_to_all_buffers;
static _Atomic log_priority priority_filter_level = DLOG_VERBOSE;
-/* Cache pid and tid to avoid up to two syscalls per log. The update function
- * is registered to happen when new threads are made (including a full fork). */
+/* Cache pid and tid to avoid up to two syscalls per log.
+ * PID is reset at fork() via pthread_atfork().
+ * TID is reset by being a thread local var. */
static int32_t cached_pid = 0;
_Thread_local int32_t cached_tid = 0;
static inline int32_t get_cached_pid()
{
- return (cached_pid = cached_pid ?: getpid());
+ return cached_pid;
}
static inline int32_t get_cached_tid()
return (cached_tid = cached_tid ?: gettid());
}
-static void update_thread_local_ids(void)
+static void update_cached_pid()
{
- cached_pid = 0;
- cached_tid = 0;
+ cached_pid = getpid();
+ // tid is reset passively (to 0) by virtue of being thread-local
}
/* Here, static_config is the original config from /etc/dlog.conf{,.d} which can be overriden,
__attribute__((cleanup(log_config_free))) struct log_config static_config = {};
__attribute__((cleanup(log_config_free))) struct log_config both_config = {};
+ cached_pid = getpid();
+
if (log_config_read(&static_config) < 0)
return false;
log_config_copy(&both_config, &static_config);
__configure_deduplicate(&both_config);
__configure_limiter(&static_config, &both_config);
- /* The fork handler also runs when a new thread is made (not just
- * when the whole process forks), so we can cache both pid and tid. */
- pthread_atfork(NULL, NULL, update_thread_local_ids);
+ pthread_atfork(NULL, NULL, update_cached_pid);
return true;
}
return DLOG_ERROR_NONE;
}
-static int __write_to_log_critical_section(log_id_t log_id, int prio, const char *tag, const char *fmt, va_list ap, bool check_should_log)
+static int __write_to_log_critical_section(log_id_t log_id, int prio, const char *tag, size_t buf_size, char buf[static buf_size], int buf_len, bool check_should_log)
{
if ((check_should_log || limiter_apply_to_all_buffers) && (dlog_check_limiter(log_id, prio, tag) < 0))
return DLOG_ERROR_NONE;
log_id = get_log_id_from_sink(log_id);
- char buf[LOG_MAX_PAYLOAD_SIZE];
- int len = vsnprintf(buf, sizeof buf, fmt, ap);
- if (len < 0)
- return DLOG_ERROR_NONE;
- else if (len >= sizeof buf)
- len = sizeof buf - 1;
// Temporary workaround, see temporary.c
- prepend_container_tag_if_in_container(sizeof buf, buf, &len);
+ prepend_container_tag_if_in_container(buf_size, buf, &buf_len);
struct timespec tp;
int r;
if (deduplicate_func && !clock_gettime(CLOCK_MONOTONIC, &tp)) {
- dlog_deduplicate_e ret = deduplicate_func(buf, len, &tp);
+ dlog_deduplicate_e ret = deduplicate_func(buf, buf_len, &tp);
if (ret == DLOG_DEDUPLICATE)
return DLOG_ERROR_NONE;
else if (ret == DLOG_DO_NOT_DEDUPLICATE_BUT_WARN)
- deduplicate_warn(buf, sizeof buf, len);
+ deduplicate_warn(buf, buf_size, buf_len);
r = write_to_log(log_id, prio, tag, buf, &tp, get_cached_pid(), get_cached_tid());
} else
r = write_to_log(log_id, prio, tag, buf, NULL, get_cached_pid(), get_cached_tid());
static int __write_to_log(log_id_t log_id, int prio, const char *tag, const char *fmt, va_list ap, bool check_should_log, bool secure_log)
{
+ /* Write down the log first, before all other checks. This
+ * is because validity checks may involve syscalls, which
+ * could overwrite `errno` and ruin the `%m` specifier if
+ * the client specifies it in format. */
+ char buf[LOG_MAX_PAYLOAD_SIZE];
+ int len = vsnprintf(buf, sizeof buf, fmt, ap);
+ if (len < 0)
+ return DLOG_ERROR_NONE; // not really, but no better choice and not too realistic
+ else if (len >= sizeof buf)
+ len = sizeof buf - 1;
+
+
int ret = dlog_check_validity(log_id, prio, tag, check_should_log);
if (ret < 0)
return ret;
else if (secure_log && !enable_secure_logs)
ret = 0;
else
- ret = __write_to_log_critical_section(log_id, prio, tag, fmt, ap, check_should_log);
+ ret = __write_to_log_critical_section(log_id, prio, tag, sizeof buf, buf, len, check_should_log);
if (should_disable_cancels)
pthread_setcancelstate(old_cancel_state, NULL);
__critical_log_child(main_pid, main_tid, log_id, prio, tag, fmt, ap);
}
+// LCOV_EXCL_START : stashing is essentially error handling.
static void stash_critical_inner(log_id_t log_id, log_priority prio, const char *tag, const char *fmt, ...)
{
va_list ap;
stash_critical_inner(log_id, prio, tag, "FAILED TO LOG: %s", msg);
return 0;
}
+// LCOV_EXCL_STOP
EXPORT_API int __dlog_critical_print(log_id_t log_id, int prio, const char *tag, const char *fmt, ...)
{