1799fc0be2
- Make -devel package dependency on the main package arch-qualified Signed-off-by: Jan Pokorný <jpokorny@redhat.com>
632 lines
29 KiB
Diff
632 lines
29 KiB
Diff
From 260768d94017023771af9e7f1bddcff91883617a Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= <jpokorny@redhat.com>
|
|
Date: Fri, 6 Oct 2017 17:17:26 +0200
|
|
Subject: [PATCH 4/6] Med: add extra run-time (client, libqb) checks that
|
|
logging will work
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Now that the previous commit provides a foundation for what exactly can
|
|
go wrong with ld.bfd = 2.29+ linker, let's start reconciling that with
|
|
some reasonable assurance that logging is not silently severed, because
|
|
realizing the logs are missing is otherwise bound to happen when the
|
|
logs are suddently pretty crucial analytical resource :-)
|
|
We'll proceed in two steps as detailed.
|
|
|
|
* * *
|
|
|
|
As a first step, the table below concludes how the test matrix overview
|
|
introduced with a message for the preceding commit (also introducing
|
|
log_test_mock.sh runner which got reused here) looks as of this
|
|
refreshed sanity check, once QB_LOG_INIT_DATA macro at hand gets applied
|
|
(meaning "for non-libqb logging participants" so as not complicate the
|
|
matrix further). That macro is nothing triggered directly, it will just
|
|
plant a constructor-like function (to be invoked automatically early in
|
|
the execution) that will run through the checks (one original and couple
|
|
of new ones as of this changeset).
|
|
|
|
Note that for libqb users, this implies a new link dependency on libdl,
|
|
because they may opt-in for refreshed QB_LOG_INIT_DATA sanity check that
|
|
calls out to dlopen/dlsym/dladdr directly in case of "attribute section"
|
|
being available for the particular platform, and hence immediately needs
|
|
those symbols resolved in link time. Hence, add this conditional link
|
|
dependency to libqb.pc pkg-config file under Libs variable -- we
|
|
actually restore the occurrence of "-ldl" there as it used to be present
|
|
until commit 56754d0. While doing so, also move immediate link
|
|
dependencies of libqb (if any, currently not but that may be
|
|
a regression arising from the cleanup related to the mentioned commit)
|
|
represented with the LIBS autoconf variable under Libs.private variable
|
|
in libqb.pc, where it belongs per pkg-config documentation.
|
|
|
|
The promised table follows, but first as a recap, "X(Y)" denotes
|
|
"X linked with linker Y":
|
|
X(a) .. ld.bfd < 2.29
|
|
X(b) .. ld.bfd = 2.29 (and only 2.29)
|
|
|
|
goes like this (values in <angle brackets> denote non-trivial change
|
|
[not mere rewording] introduced as of this commit, in comparison to
|
|
the table stated in the preceding commit):
|
|
|
|
+=========+=========+=========+=========+=========+=========+=========+
|
|
#client(x)# libqb(a) usage # libqb(b) usage #
|
|
# vvv #---------+---------+---------+---------+---------+---------+
|
|
# V # direct | libX(a) : libX(b) # direct | libX(a) : libX(b) #
|
|
+=========+=========+=========+=========+=========+=========+=========+
|
|
# x = a # OK | OK : BAD[*2] #<BAD[*E]>|<BAD[*F]>:<BAD[*G]>#
|
|
# x = b # BAD[*A] | BAD[*B] : BAD[*C] #<BAD[*E]>|<BAD[*F]>:<BAD[*G]>#
|
|
+=========+=========+=========+=========+=========+=========+=========+
|
|
|
|
Woefully, nothing changes if we swap binutils 2.29 for 2.29.1, i.e.,
|
|
X(b) .. ld.bfd = 2.29.1, compared to previous state, i.e., the second
|
|
table from the previous commit is still applicable for that situation.
|
|
The added sanity checks are useful nonetheless, consider for example,
|
|
that attribute-section-less libqb is what gets run-time linked to an
|
|
attribute-section-full target. The most precise check we could use
|
|
-- a custom logger function applied in a self-test scheme -- is not
|
|
available at the point the macro-defined function gets invoked, simply
|
|
because qb_log_init hasn't been invoked by the time that constructor
|
|
gets triggered. However, what we can do is to add a non-trapping
|
|
libqb-residing reverse-testing of the client space that (and once it)
|
|
voluntarily initiates qb_log_init (delivering abruption all of a sudden
|
|
at some unanticipated, as opposed to a well-timed like with
|
|
constructors, execution point, seems pretty bad idea + libqb as
|
|
a library is a mere helper, not an undertaker :) -- this check then
|
|
only announces, via syslog (the only pre-enabled logging target),
|
|
the target's logging may be severed.
|
|
|
|
* * *
|
|
|
|
Hence, as a promised second step, after incorporating the syslog
|
|
change (and extending log_test_mock.sh so as to capture syslog
|
|
stream within the container), not much changes with the table above,
|
|
i.e., X(b) .. ld.bfd = 2.29:
|
|
[*A] in addition, unless QB_LOG_INIT_DATA used on client side,
|
|
syslog carries this warning:
|
|
"(LOG@QB) target chain supplied section not observed by libqb,
|
|
target's and/or libqb's linkage at fault and logging of the target
|
|
will not work reliably (unless qb_log_init function used unexpectedly
|
|
in no-logging context, or target chain built purposefully without
|
|
callsite section)"
|
|
logged by libqb proper
|
|
[*C] in addition, unless QB_LOG_INIT_DATA used on interlib side,
|
|
syslog carries this warning:
|
|
"(LOG@QB) target chain supplied section not observed by libqb,
|
|
target's and/or libqb's linkage at fault and logging of the target
|
|
will not work reliably (unless qb_log_init function used unexpectedly
|
|
in no-logging context, or target chain built purposefully without
|
|
callsite section)"
|
|
logged by libqb proper
|
|
[*E] in addition, unless QB_LOG_INIT_DATA used on client side,
|
|
syslog carries this warning:
|
|
"(LOG@QB) target chain supplied section empty, target's and/or
|
|
libqb's linkage at fault and logging of the target will not
|
|
work reliably (unless qb_log_init function used unexpectedly
|
|
in no-logging context)"
|
|
logged by libqb proper
|
|
[*F] in addition, unless QB_LOG_INIT_DATA used on interlib side,
|
|
syslog carries this warning:
|
|
"(LOG@QB) target chain supplied section empty, target's and/or
|
|
libqb's linkage at fault and logging of the target will not
|
|
work reliably (unless qb_log_init function used unexpectedly
|
|
in no-logging context)"
|
|
logged by libqb proper
|
|
[*G] in addition, unless QB_LOG_INIT_DATA used on interlib side,
|
|
syslog carries this warning:
|
|
"(LOG@QB) target chain supplied section empty, target's and/or
|
|
libqb's linkage at fault and logging of the target will not
|
|
work reliably (unless qb_log_init function used unexpectedly
|
|
in no-logging context)"
|
|
logged by libqb proper
|
|
|
|
but desirably changes with "X(b) .. ld.bfd = 2.29.1" one
|
|
(DEP ~ "depends"):
|
|
|
|
+=========+=========+=========+=========+=========+=========+=========+
|
|
#client(x)# libqb(a) usage # libqb(b) usage #
|
|
# vvv #---------+---------+---------+---------+---------+---------+
|
|
# V # direct | libX(a) : libX(b) # direct | libX(a) : libX(b) #
|
|
+=========+=========+=========+=========+=========+=========+=========+
|
|
# x = a # OK | OK : DEP[*J] #<BAD[*M]>|<BAD[*M]>:<BAD[*L]>#
|
|
# x = b #<DEP[*N]>| DEP[*I] :<DEP[*O]>#<BAD[*M]>|<BAD[*M]>:<BAD[*L]>#
|
|
+=========+=========+=========+=========+=========+=========+=========+
|
|
|
|
* * *
|
|
|
|
[*1] client logging not working
|
|
[*2] interlib logging not working
|
|
[*3] both client and interlib logging not working
|
|
|
|
[*A] boils down to [*1], unless QB_LOG_INIT_DATA used on client side,
|
|
which then fails on
|
|
"non-empty implicit callsite section, otherwise target's linkage at
|
|
fault and logging would not work reliably"
|
|
assertion
|
|
[*B] boils down to [*1], unless QB_LOG_INIT_DATA used on interlib side,
|
|
which then fails on
|
|
"non-empty implicit callsite section, otherwise target's linkage at
|
|
fault and logging would not work reliably"
|
|
assertion
|
|
[*C] boils down to [*3], unless QB_LOG_INIT_DATA used on interlib side,
|
|
which then fails on
|
|
"non-empty implicit callsite section, otherwise target's linkage at
|
|
fault and logging would not work reliably"
|
|
assertion
|
|
[*E] boils down to [*1], unless QB_LOG_INIT_DATA used on client side,
|
|
which then fails on
|
|
"implicit callsite section self-observable, otherwise target's and/or
|
|
libqb's linkage at fault and logging would not work reliably"
|
|
assertion
|
|
[*F] boils down to [*3], unless QB_LOG_INIT_DATA used on interlib side,
|
|
which then fails on
|
|
"libqb's callsite section non-empty, otherwise libqb's linkage at
|
|
fault and logging would not work reliably"
|
|
assertion
|
|
[*G] boils down to [*3], unless QB_LOG_INIT_DATA used on interlib side,
|
|
which then fails on
|
|
"implicit callsite section self-observable, otherwise target's and/or
|
|
libqb's linkage at fault and logging would not work reliably"
|
|
assertion
|
|
|
|
[*I] boils down to [*1], unless QB_LOG_INIT_DATA used on client side,
|
|
which makes it, likely through self-reference keepalive (see
|
|
below), work OK
|
|
[*J] boils down to [*2], unless QB_LOG_INIT_DATA used on interlib side,
|
|
which makes it, likely through self-reference keepalive (see
|
|
below), work OK
|
|
[*K] boils down to [*3]
|
|
in addition, syslog carries this warning:
|
|
"(LOG@QB) target chain supplied section not observed by libqb,
|
|
target's and/or libqb's linkage at fault and logging of the target
|
|
will not work reliably (unless qb_log_init function used unexpectedly
|
|
in no-logging context, or target chain built purposefully without
|
|
callsite section)"
|
|
logged by libqb proper
|
|
[*L] boils down to [*3], unless QB_LOG_INIT_DATA used on interlib side
|
|
(sufficient?), which makes it, likely through self-reference
|
|
keepalive (see below), boil down just to [*1];
|
|
in addition, syslog carries this warning:
|
|
"(LOG@QB) target chain supplied section not observed by libqb,
|
|
target's and/or libqb's linkage at fault and logging of the target
|
|
will not work reliably (unless qb_log_init function used unexpectedly
|
|
in no-logging context, or target chain built purposefully without
|
|
callsite section)"
|
|
logged by libqb proper
|
|
[*M] boils down to [*1];
|
|
in addition, syslog carries this warning:
|
|
"(LOG@QB) target chain supplied section not observed by libqb,
|
|
target's and/or libqb's linkage at fault and logging of the target
|
|
will not work reliably (unless qb_log_init function used unexpectedly
|
|
in no-logging context, or target chain built purposefully without
|
|
callsite section)"
|
|
logged by libqb proper
|
|
[*N] boils down to [*M], unless QB_LOG_INIT_DATA used on client side,
|
|
which makes it, likely through self-reference keepalive (see
|
|
below), work OK
|
|
[*O] boils down to [*K], unless QB_LOG_INIT_DATA used on both client
|
|
and interlib side, which makes it, likely through self-reference
|
|
keepalive (see below), work OK (it's expected that this a mere
|
|
composite of situations [*I] and [*J] with consequences as stated)
|
|
|
|
* * *
|
|
|
|
Note: the only problematic (i.e. not captured automatically by the
|
|
QB_LOG_INIT_DATA macro presumably utilized at every non-libqb logging
|
|
system participant in the form of a discrete compilation unit)
|
|
combination with 2.29 is the one intersecting at "BAD[*2]" pertaining
|
|
"everything but interlib compiled with ld.bfd < 2.29". It would, of
|
|
course, be solvable as well, but presumably not in an easy way, and
|
|
that use case should not be as frequent.
|
|
|
|
Takeway: whenever your target (library or client program) actively
|
|
utilizes logging (meaning it emits at least a single log message,
|
|
otherwise there's an imminent danger of possibly even run-terminating
|
|
false positive in the self-check mechanism!),
|
|
|
|
YOU ARE strongly ENCOURAGED TO USE QB_LOG_INIT_DATA macro function
|
|
at (exactly) one of the source code files (presumably the main one)
|
|
per respective target's compilation unit.
|
|
|
|
It will alleviate the hassles possibly caused by downgrading libqb
|
|
to the linker-vs-libqb incompatibly compiled one or in similar
|
|
circumstances arising merely from the linker behaviour change,
|
|
which the current build system/code shake is all about.
|
|
|
|
Signed-off-by: Jan Pokorný <jpokorny@redhat.com>
|
|
---
|
|
configure.ac | 1 +
|
|
include/qb/qblog.h | 111 ++++++++++++++++++++++--------
|
|
lib/libqb.pc.in | 3 +-
|
|
lib/log.c | 53 ++++++++++++++
|
|
tests/functional/Makefile.am | 3 +-
|
|
tests/functional/log.am | 2 +-
|
|
tests/functional/log_external/Makefile.am | 8 ++-
|
|
tests/functional/log_test_mock.sh | 13 +++-
|
|
tests/functional/syslog-stdout.py | 51 ++++++++++++++
|
|
9 files changed, 209 insertions(+), 36 deletions(-)
|
|
create mode 100755 tests/functional/syslog-stdout.py
|
|
|
|
diff --git a/configure.ac b/configure.ac
|
|
index 8c588cb..fdcd93c 100644
|
|
--- a/configure.ac
|
|
+++ b/configure.ac
|
|
@@ -655,6 +655,7 @@ if test "x${GCC}" = xyes; then
|
|
AC_DEFINE([QB_HAVE_ATTRIBUTE_SECTION], 1,
|
|
[Enabling code using __attribute__((section))])
|
|
PACKAGE_FEATURES="$PACKAGE_FEATURES attribute-section"
|
|
+ AC_SUBST([client_dlopen_LIBS],[$dlopen_LIBS])
|
|
fi
|
|
fi
|
|
|
|
diff --git a/include/qb/qblog.h b/include/qb/qblog.h
|
|
index 4954932..a9a9ef2 100644
|
|
--- a/include/qb/qblog.h
|
|
+++ b/include/qb/qblog.h
|
|
@@ -1,9 +1,10 @@
|
|
/*
|
|
- * Copyright (C) 2010 Red Hat, Inc.
|
|
+ * Copyright 2017 Red Hat, Inc.
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Author: Angus Salkeld <asalkeld@redhat.com>
|
|
+ * Jan Pokorny <jpokorny@redhat.com>
|
|
*
|
|
* libqb is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
@@ -44,6 +45,7 @@ extern "C" {
|
|
|
|
#ifdef QB_HAVE_ATTRIBUTE_SECTION
|
|
#include <assert.h> /* possibly needed for QB_LOG_INIT_DATA */
|
|
+#include <dlfcn.h> /* dynamic linking: dlopen, dlsym, dladdr, ... */
|
|
#endif
|
|
|
|
/**
|
|
@@ -70,14 +72,14 @@ extern "C" {
|
|
*
|
|
* @note
|
|
* In practice, such a minimalistic approach hardly caters real use cases.
|
|
- * Following section discusses the customization. Moreover, it's quite
|
|
- * vital to instrument the target user of this logging subsystem with
|
|
- * @c QB_LOG_INIT_DATA() macro placed in the top file scope in an exactly
|
|
- * one source file (preferably the main one) to be mixed into the resulting
|
|
- * compilation unit. This is a self-defensive measure for when the
|
|
- * linker-assisted collection of callsite data silently fails, which
|
|
- * could otherwise go unnoticed, causing troubles down the road.
|
|
- *
|
|
+ * Following section discusses the customization. Moreover when employing
|
|
+ * the log module is bound to its active use (some log messages are assuredly
|
|
+ * emitted within the target compilation unit), it's quite vital to instrument
|
|
+ * the target side with @c QB_LOG_INIT_DATA() macro placed in the top file
|
|
+ * scope in exactly one source file (preferably the main one) to be mixed into
|
|
+ * the resulting compilation unit. This is a self-defensive measure for when
|
|
+ * the linker-assisted collection of callsite data silently fails, which could
|
|
+ * otherwise go unnoticed, causing troubles down the road.
|
|
*
|
|
* @par Configuring log targets.
|
|
* A log target can be syslog, stderr, the blackbox, stdout, or a text file.
|
|
@@ -283,35 +285,86 @@ typedef void (*qb_log_filter_fn)(struct qb_log_callsite * cs);
|
|
extern struct qb_log_callsite QB_ATTR_SECTION_START[];
|
|
extern struct qb_log_callsite QB_ATTR_SECTION_STOP[];
|
|
|
|
-/* optional on-demand self-check of 1/ toolchain sanity (prerequisite for
|
|
- the logging subsystem to work properly) and 2/ non-void active use of
|
|
- logging (satisfied with a justifying existence of a logging callsite as
|
|
- defined with a @c qb_logt invocation) at the target (but see below), which
|
|
- is supposedly assured by it's author(!) as of relying on this very macro
|
|
- [technically, the symbols that happen to be resolved under the respective
|
|
- identifiers do not necessarily originate in the same compilation unit as
|
|
- when it's not the end executable (or by induction, a library positioned
|
|
- earlier in the symbol lookup order) but a shared library, the former takes
|
|
- a precedence unless that site comes short of exercising the logging,
|
|
- making its callsite section empty and, in turn, without such boundary
|
|
- symbols, hence making the resolution continue further in the lookup order
|
|
- -- despite fuzzily targeted attestation, the check remains reasonable];
|
|
- only effective when link-time ("run-time amortizing") callsite collection
|
|
- is; as a side effect, it can ensure the boundary-denoting symbols for the
|
|
- target collection area are kept alive with some otherwise unkind linkers;
|
|
- may be extended in future for more in-depth self-validation */
|
|
+/* Related to the next macro that is -- unlike this one -- a public API */
|
|
+#ifndef _GNU_SOURCE
|
|
+#define QB_NONAPI_LOG_INIT_DATA_EXTRA_ \
|
|
+ _Pragma(QB_PP_STRINGIFY(GCC warning QB_PP_STRINGIFY( \
|
|
+ without "_GNU_SOURCE" defined (directly or not) \
|
|
+ QB_LOG_INIT_DATA cannot check sanity of libqb proper)))
|
|
+#else
|
|
+#define QB_NONAPI_LOG_INIT_DATA_EXTRA_ \
|
|
+ { Dl_info work_dli; \
|
|
+ /* libqb sanity (locating libqb by it's relatively unique \
|
|
+ -- and currently only such per-linkage global one -- \
|
|
+ non-functional symbol, due to possible confusion otherwise) */ \
|
|
+ if (dladdr(dlsym(RTLD_DEFAULT, "facilitynames"), &work_dli) \
|
|
+ && (work_handle = dlopen(work_dli.dli_fname, \
|
|
+ RTLD_LOCAL|RTLD_LAZY)) != NULL) { \
|
|
+ work_s1 = (struct qb_log_callsite *) \
|
|
+ dlsym(work_handle, QB_ATTR_SECTION_START_STR); \
|
|
+ work_s2 = (struct qb_log_callsite *) \
|
|
+ dlsym(work_handle, QB_ATTR_SECTION_STOP_STR); \
|
|
+ assert("libqb's callsite section observable, otherwise libqb's \
|
|
+linkage at fault and logging would not work reliably" \
|
|
+ && work_s1 != NULL && work_s2 != NULL); \
|
|
+ assert("libqb's callsite section non-empty, otherwise libqb's \
|
|
+linkage at fault and logging would not work reliably" \
|
|
+ && work_s1 != work_s2); \
|
|
+ dlclose(work_handle); } }
|
|
+#endif /* _GNU_SOURCE */
|
|
+
|
|
+/**
|
|
+ * Optional on-demand self-check of 1/ toolchain sanity (prerequisite for
|
|
+ * the logging subsystem to work properly) and 2/ non-void active use of
|
|
+ * logging (satisfied with a justifying existence of a logging callsite as
|
|
+ * defined with a @c qb_logt invocation) at the target (but see below), which
|
|
+ * is supposedly assured by it's author(!) as of relying on this very macro
|
|
+ * [technically, the symbols that happen to be resolved under the respective
|
|
+ * identifiers do not necessarily originate in the same compilation unit as
|
|
+ * when it's not the end executable (or by induction, a library positioned
|
|
+ * earlier in the symbol lookup order) but a shared library, the former takes
|
|
+ * a precedence unless that site comes short of exercising the logging,
|
|
+ * making its callsite section empty and, in turn, without such boundary
|
|
+ * symbols, hence making the resolution continue further in the lookup order
|
|
+ * -- despite fuzzily targeted attestation, the check remains reasonable];
|
|
+ * only effective when link-time ("run-time amortizing") callsite collection
|
|
+ * is; as a side effect, it can ensure the boundary-denoting symbols for the
|
|
+ * target collection area are kept alive with some otherwise unkind linkers.
|
|
+ *
|
|
+ * Applying this macro in the target program/library is strongly recommended
|
|
+ * whenever the logging as framed by this header file is in use.
|
|
+ * Moreover, it's important to state that using this check while not ensuring
|
|
+ * @c _GNU_SOURCE macro definition is present at compile-time means only half
|
|
+ * of the available sanity checking will be performed, possibly resulting
|
|
+ * in libqb's own internally logged messages being lost without warning.
|
|
+ */
|
|
#define QB_LOG_INIT_DATA(name) \
|
|
void name(void); \
|
|
void name(void) { \
|
|
+ void *work_handle; struct qb_log_callsite *work_s1, *work_s2; \
|
|
/* our own (target's) sanity, or possibly that of higher priority \
|
|
symbol resolution site (unless target equals end executable) \
|
|
or even the lower one if no such predecessor defines these */ \
|
|
- assert("non-empty implicit callsite section" \
|
|
- && QB_ATTR_SECTION_START != QB_ATTR_SECTION_STOP); } \
|
|
+ if ((work_handle = dlopen(NULL, RTLD_LOCAL|RTLD_LAZY)) != NULL) { \
|
|
+ work_s1 = (struct qb_log_callsite *) \
|
|
+ dlsym(work_handle, QB_ATTR_SECTION_START_STR); \
|
|
+ work_s2 = (struct qb_log_callsite *) \
|
|
+ dlsym(work_handle, QB_ATTR_SECTION_STOP_STR); \
|
|
+ assert("implicit callsite section observable, otherwise \
|
|
+target's and/or libqb's linkage at fault and logging would not work \
|
|
+reliably" \
|
|
+ && work_s1 != NULL && work_s2 != NULL); \
|
|
+ dlclose(work_handle); /* perhaps overly eager thing to do */ } \
|
|
+ /* better targeted attestations when available */ \
|
|
+ QB_NONAPI_LOG_INIT_DATA_EXTRA_; \
|
|
+ /* finally, original, straightforward check */ \
|
|
+ assert("non-empty implicit callsite section, otherwise target's \
|
|
+linkage at fault and logging would not work reliably" \
|
|
+ && QB_ATTR_SECTION_START != QB_ATTR_SECTION_STOP); } \
|
|
void __attribute__ ((constructor)) name(void);
|
|
#else
|
|
#define QB_LOG_INIT_DATA(name)
|
|
-#endif
|
|
+#endif /* QB_HAVE_ATTRIBUTE_SECTION */
|
|
|
|
/**
|
|
* Internal function: use qb_log() or qb_logt()
|
|
diff --git a/lib/libqb.pc.in b/lib/libqb.pc.in
|
|
index 8a8d0ba..37d27b7 100644
|
|
--- a/lib/libqb.pc.in
|
|
+++ b/lib/libqb.pc.in
|
|
@@ -7,5 +7,6 @@ Name: libqb
|
|
Version: @PACKAGE_VERSION@
|
|
Description: libqb
|
|
Requires:
|
|
-Libs: -L${libdir} -lqb @LIBS@
|
|
+Libs: -L${libdir} -lqb @client_dlopen_LIBS@
|
|
+Libs.private: @LIBS@
|
|
Cflags: -I${includedir}
|
|
diff --git a/lib/log.c b/lib/log.c
|
|
index bfd218f..eb35d6f 100644
|
|
--- a/lib/log.c
|
|
+++ b/lib/log.c
|
|
@@ -853,6 +853,18 @@ qb_log_init(const char *name, int32_t facility, uint8_t priority)
|
|
{
|
|
int32_t l;
|
|
enum qb_log_target_slot i;
|
|
+#ifdef QB_HAVE_ATTRIBUTE_SECTION
|
|
+ void *work_handle; struct qb_log_callsite *work_s1, *work_s2;
|
|
+ Dl_info work_dli;
|
|
+#endif /* QB_HAVE_ATTRIBUTE_SECTION */
|
|
+ /* cannot reuse single qb_log invocation in various contexts
|
|
+ through the variables (when section attribute in use),
|
|
+ hence this indirection */
|
|
+ enum {
|
|
+ preinit_err_none,
|
|
+ preinit_err_target_sec,
|
|
+ preinit_err_target_empty,
|
|
+ } preinit_err = preinit_err_none;
|
|
|
|
l = pthread_rwlock_init(&_listlock, NULL);
|
|
assert(l == 0);
|
|
@@ -871,6 +883,26 @@ qb_log_init(const char *name, int32_t facility, uint8_t priority)
|
|
|
|
qb_log_dcs_init();
|
|
#ifdef QB_HAVE_ATTRIBUTE_SECTION
|
|
+ /* sanity check that target chain supplied QB_ATTR_SECTION_ST{ART,OP}
|
|
+ symbols and hence the local references to them are not referencing
|
|
+ the proper libqb's ones (locating libqb-self by it's relatively
|
|
+ unique -- and currently only such per-linkage global one --
|
|
+ non-functional symbol, due to possible confusion otherwise) */
|
|
+ if (dladdr(dlsym(RTLD_DEFAULT, "facilitynames"), &work_dli)
|
|
+ && (work_handle = dlopen(work_dli.dli_fname,
|
|
+ RTLD_LOCAL|RTLD_LAZY)) != NULL) {
|
|
+ work_s1 = (struct qb_log_callsite *)
|
|
+ dlsym(work_handle, QB_ATTR_SECTION_START_STR);
|
|
+ work_s2 = (struct qb_log_callsite *)
|
|
+ dlsym(work_handle, QB_ATTR_SECTION_STOP_STR);
|
|
+ if (work_s1 == QB_ATTR_SECTION_START
|
|
+ || work_s2 == QB_ATTR_SECTION_STOP) {
|
|
+ preinit_err = preinit_err_target_sec;
|
|
+ } else if (work_s1 == work_s2) {
|
|
+ preinit_err = preinit_err_target_empty;
|
|
+ }
|
|
+ dlclose(work_handle); /* perhaps overly eager thing to do */
|
|
+ }
|
|
qb_log_callsites_register(QB_ATTR_SECTION_START, QB_ATTR_SECTION_STOP);
|
|
dl_iterate_phdr(_log_so_walk_callback, NULL);
|
|
_log_so_walk_dlnames();
|
|
@@ -884,6 +916,27 @@ qb_log_init(const char *name, int32_t facility, uint8_t priority)
|
|
_log_target_state_set(&conf[QB_LOG_SYSLOG], QB_LOG_STATE_ENABLED);
|
|
(void)qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD,
|
|
QB_LOG_FILTER_FILE, "*", priority);
|
|
+
|
|
+ if (preinit_err == preinit_err_target_sec)
|
|
+ qb_util_log(LOG_WARNING, "(LOG@QB) target chain supplied"
|
|
+ " section not observed by libqb,"
|
|
+ " target's and/or libqb's linkage"
|
|
+ " at fault and logging of the target"
|
|
+ " will not work reliably (unless"
|
|
+ " qb_log_init function used"
|
|
+ " unexpectedly in no-logging"
|
|
+ " context, or target chain built"
|
|
+ " purposefully without callsite"
|
|
+ " sections)");
|
|
+ else if (preinit_err == preinit_err_target_empty) {
|
|
+ qb_util_log(LOG_WARNING, "(LOG@QB) target chain supplied"
|
|
+ " section empty, target's and/or"
|
|
+ " libqb's linkage at fault and"
|
|
+ " logging of the target will not"
|
|
+ " work reliably (unless qb_log_init"
|
|
+ " function used unexpectedly in"
|
|
+ " no-logging context)");
|
|
+ }
|
|
}
|
|
|
|
void
|
|
diff --git a/tests/functional/Makefile.am b/tests/functional/Makefile.am
|
|
index 0d34ae8..522fb61 100644
|
|
--- a/tests/functional/Makefile.am
|
|
+++ b/tests/functional/Makefile.am
|
|
@@ -19,5 +19,6 @@
|
|
|
|
MAINTAINERCLEANFILES = Makefile.in
|
|
EXTRA_DIST = GNUmakefile log_test_client.err log_test_interlib_client.err \
|
|
- log_test_client.sh log_test_interlib_client.sh log_test_mock.sh
|
|
+ log_test_client.sh log_test_interlib_client.sh \
|
|
+ log_test_mock.sh syslog-stdout.py
|
|
SUBDIRS = log_external log_internal
|
|
diff --git a/tests/functional/log.am b/tests/functional/log.am
|
|
index e8e4740..80bccb0 100644
|
|
--- a/tests/functional/log.am
|
|
+++ b/tests/functional/log.am
|
|
@@ -20,7 +20,7 @@
|
|
MAINTAINERCLEANFILES = Makefile.in
|
|
CLEANFILES = log_test_client.err.real log_test_interlib_client.err.real
|
|
|
|
-AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
|
|
+AM_CPPFLAGS = -D_GNU_SOURCE -I$(top_builddir)/include -I$(top_srcdir)/include
|
|
|
|
noinst_PROGRAMS = log_client log_interlib_client
|
|
# cannot use {check,noinst}_LTLIBRARIES because it leads to solely static lib
|
|
diff --git a/tests/functional/log_external/Makefile.am b/tests/functional/log_external/Makefile.am
|
|
index 36aa0af..ca1c8a5 100644
|
|
--- a/tests/functional/log_external/Makefile.am
|
|
+++ b/tests/functional/log_external/Makefile.am
|
|
@@ -19,5 +19,9 @@
|
|
|
|
include ../log.am
|
|
|
|
-log_client_LDFLAGS = -lqb
|
|
-liblog_inter_la_LIBADD = -lqb
|
|
+# while linking with system-wide version of libqb, we are still pursuing
|
|
+# local in-tree header file, hence we need to link with dynamic linking
|
|
+# library (which is a prerequisite for using QB_LOG_INIT_DATA defined
|
|
+# in qblog.h) explicitly
|
|
+log_client_LDFLAGS = -lqb @client_dlopen_LIBS@
|
|
+liblog_inter_la_LIBADD = -lqb @client_dlopen_LIBS@
|
|
diff --git a/tests/functional/log_test_mock.sh b/tests/functional/log_test_mock.sh
|
|
index 145b9f4..cdfce1f 100755
|
|
--- a/tests/functional/log_test_mock.sh
|
|
+++ b/tests/functional/log_test_mock.sh
|
|
@@ -166,10 +166,19 @@ do_compile_and_test_client () {
|
|
-exec rm -- {} \;"
|
|
;;
|
|
esac
|
|
+ mock ${mock_args} --copyin "syslog-stdout.py" "builddir"
|
|
mock ${mock_args} --shell "( cd \"builddir/build/BUILD/$1\"; ./configure )"
|
|
mock ${mock_args} --shell \
|
|
- "make -C \"builddir/build/BUILD/$1/tests/functional/log_external\" \
|
|
- check-TESTS \"TESTS=../${_logfile}.sh\" $5" \
|
|
+ "python3 builddir/syslog-stdout.py \
|
|
+ >\"builddir/build/BUILD/$1/tests/functional/log_external/.syslog\" & \
|
|
+ { sleep 2; make -C \"builddir/build/BUILD/$1/tests/functional/log_external\" \
|
|
+ check-TESTS \"TESTS=../${_logfile}.sh\" $5; } \
|
|
+ && ! test -s \"builddir/build/BUILD/$1/tests/functional/log_external/.syslog\"; \
|
|
+ ret_ec=\$?; \
|
|
+ ( cd \"builddir/build/BUILD/$1/tests/functional/log_external\"; \
|
|
+ cat .syslog >> test-suite.log; \
|
|
+ echo SYSLOG-begin; cat .syslog; echo SYSLOG-end ); \
|
|
+ ret () { return \$1; }; ret \${ret_ec}" \
|
|
&& _result="${_result}_good" \
|
|
|| _result="${_result}_bad"
|
|
mock ${mock_args} --copyout \
|
|
diff --git a/tests/functional/syslog-stdout.py b/tests/functional/syslog-stdout.py
|
|
new file mode 100755
|
|
index 0000000..64baf4c
|
|
--- /dev/null
|
|
+++ b/tests/functional/syslog-stdout.py
|
|
@@ -0,0 +1,51 @@
|
|
+#!/usr/bin/python3
|
|
+# Copyright 2017 Red Hat, Inc.
|
|
+#
|
|
+# Author: Jan Pokorny <jpokorny@redhat.com>
|
|
+#
|
|
+# This file is part of libqb.
|
|
+#
|
|
+# libqb is free software: you can redistribute it and/or modify
|
|
+# it under the terms of the GNU Lesser General Public License as published by
|
|
+# the Free Software Foundation, either version 2.1 of the License, or
|
|
+# (at your option) any later version.
|
|
+#
|
|
+# libqb is distributed in the hope that it will be useful,
|
|
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+# GNU Lesser General Public License for more details.
|
|
+#
|
|
+# You should have received a copy of the GNU Lesser General Public License
|
|
+# along with libqb. If not, see <http://www.gnu.org/licenses/>.
|
|
+
|
|
+"""Simple /dev/log to stdout forwarding"""
|
|
+
|
|
+import socket
|
|
+from atexit import register
|
|
+from os import remove
|
|
+from sys import argv
|
|
+
|
|
+# no locking, but anyway
|
|
+try:
|
|
+ remove("/dev/log")
|
|
+except FileNotFoundError:
|
|
+ pass
|
|
+sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
|
|
+sock.bind("/dev/log")
|
|
+
|
|
+def shutdown():
|
|
+ sock.close()
|
|
+ remove("/dev/log")
|
|
+
|
|
+def main(*argv):
|
|
+ register(shutdown)
|
|
+ while True:
|
|
+ try:
|
|
+ b = sock.recv(4096)
|
|
+ # flushing is crucial here
|
|
+ print(">>> " + str(b, 'ascii').split(' ', 3)[-1], flush=True)
|
|
+ except IOError:
|
|
+ pass
|
|
+
|
|
+if __name__ == '__main__':
|
|
+ main(*argv)
|
|
--
|
|
2.15.0
|
|
|