aboutsummaryrefslogtreecommitdiff
path: root/nxcomp
diff options
context:
space:
mode:
Diffstat (limited to 'nxcomp')
-rw-r--r--nxcomp/.gitignore4
-rw-r--r--nxcomp/Makefile.am2
-rw-r--r--nxcomp/configure.ac26
l---------nxcomp/m4/ax_cxx_compile_stdcxx.m41
l---------nxcomp/m4/ax_cxx_compile_stdcxx_11.m41
-rw-r--r--nxcomp/m4/ax_pthread.m4485
-rw-r--r--nxcomp/src/Log.cpp121
-rw-r--r--nxcomp/src/Log.h545
-rw-r--r--nxcomp/src/Loop.cpp97
-rw-r--r--nxcomp/src/Makefile.am9
-rw-r--r--nxcomp/test/Makefile.am22
-rw-r--r--nxcomp/test/logging_test.cpp224
-rw-r--r--nxcomp/test/logging_test.h121
13 files changed, 1656 insertions, 2 deletions
diff --git a/nxcomp/.gitignore b/nxcomp/.gitignore
index 019202342..1d00b755b 100644
--- a/nxcomp/.gitignore
+++ b/nxcomp/.gitignore
@@ -19,3 +19,7 @@ m4/lt~obsolete.m4
nxcomp.pc
src/Makefile
src/Makefile.in
+test/.deps/
+test/Makefile
+test/Makefile.in
+test/logging_test
diff --git a/nxcomp/Makefile.am b/nxcomp/Makefile.am
index ae5991c3d..c1f6226bc 100644
--- a/nxcomp/Makefile.am
+++ b/nxcomp/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = src
+SUBDIRS = src test
pkgconfig_DATA = nxcomp.pc
diff --git a/nxcomp/configure.ac b/nxcomp/configure.ac
index 0b30915c2..3ae81f0d1 100644
--- a/nxcomp/configure.ac
+++ b/nxcomp/configure.ac
@@ -63,9 +63,34 @@ if test "$FreeBSD" = yes; then
CPPFLAGS="$CPPFLAGS -I/usr/local/include"
fi
+AX_PTHREAD([], AC_MSG_ERROR([no POSIX threads support detected]))
+
# If in_addr_t is not defined use unsigned int.
AC_CHECK_TYPES([in_addr_t], [], [], [[#include <netinet/in.h>]])
+AC_ARG_ENABLE([cxx11],
+ [AS_HELP_STRING([--enable-cxx11],
+ [enable optional features requiring C++11 support (disabled by default)])],
+ [AS_IF([test x$enableval = xyes],
+ [AX_CXX_COMPILE_STDCXX_11([], [mandatory])])])
+
+# Check if std::put_time is available.
+AC_MSG_CHECKING([if std::put_time is available])
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[
+#include <iomanip>
+#include <ctime>
+]],
+[[
+std::time_t t = std::time(NULL);
+std::tm tm = *std::localtime(&t);
+(void) std::put_time(&tm, "%c");
+]])],
+ [AC_MSG_RESULT([yes])
+ AC_DEFINE(HAVE_STD_PUT_TIME, [1],
+ [Use std::put_time to format times, must be made available by the compiler if turned on.])],
+ [AC_MSG_RESULT([no])])
+
AC_ARG_ENABLE([debug],
[AS_HELP_STRING([--enable-debug],
[enable to get info session log output (disabled by default)])],
@@ -83,6 +108,7 @@ AC_ARG_ENABLE([valgrind],
AC_CONFIG_FILES([
Makefile
src/Makefile
+test/Makefile
nxcomp.pc
])
diff --git a/nxcomp/m4/ax_cxx_compile_stdcxx.m4 b/nxcomp/m4/ax_cxx_compile_stdcxx.m4
new file mode 120000
index 000000000..28ebfd1a6
--- /dev/null
+++ b/nxcomp/m4/ax_cxx_compile_stdcxx.m4
@@ -0,0 +1 @@
+../../m4/ax_cxx_compile_stdcxx.m4 \ No newline at end of file
diff --git a/nxcomp/m4/ax_cxx_compile_stdcxx_11.m4 b/nxcomp/m4/ax_cxx_compile_stdcxx_11.m4
new file mode 120000
index 000000000..5fbe8790d
--- /dev/null
+++ b/nxcomp/m4/ax_cxx_compile_stdcxx_11.m4
@@ -0,0 +1 @@
+../../m4/ax_cxx_compile_stdcxx_11.m4 \ No newline at end of file
diff --git a/nxcomp/m4/ax_pthread.m4 b/nxcomp/m4/ax_pthread.m4
new file mode 100644
index 000000000..5fbf9fe0d
--- /dev/null
+++ b/nxcomp/m4/ax_pthread.m4
@@ -0,0 +1,485 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_pthread.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+#
+# DESCRIPTION
+#
+# This macro figures out how to build C programs using POSIX threads. It
+# sets the PTHREAD_LIBS output variable to the threads library and linker
+# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
+# flags that are needed. (The user can also force certain compiler
+# flags/libs to be tested by setting these environment variables.)
+#
+# Also sets PTHREAD_CC to any special C compiler that is needed for
+# multi-threaded programs (defaults to the value of CC otherwise). (This
+# is necessary on AIX to use the special cc_r compiler alias.)
+#
+# NOTE: You are assumed to not only compile your program with these flags,
+# but also to link with them as well. For example, you might link with
+# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
+#
+# If you are only building threaded programs, you may wish to use these
+# variables in your default LIBS, CFLAGS, and CC:
+#
+# LIBS="$PTHREAD_LIBS $LIBS"
+# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+# CC="$PTHREAD_CC"
+#
+# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
+# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
+# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+#
+# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
+# PTHREAD_PRIO_INHERIT symbol is defined when compiling with
+# PTHREAD_CFLAGS.
+#
+# ACTION-IF-FOUND is a list of shell commands to run if a threads library
+# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
+# is not found. If ACTION-IF-FOUND is not specified, the default action
+# will define HAVE_PTHREAD.
+#
+# Please let the authors know if this macro fails on any platform, or if
+# you have any other suggestions or comments. This macro was based on work
+# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
+# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
+# Alejandro Forero Cuervo to the autoconf macro repository. We are also
+# grateful for the helpful feedback of numerous users.
+#
+# Updated for Autoconf 2.68 by Daniel Richard G.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
+# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program 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 General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 24
+
+AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
+AC_DEFUN([AX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_REQUIRE([AC_PROG_CC])
+AC_REQUIRE([AC_PROG_SED])
+AC_LANG_PUSH([C])
+ax_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on Tru64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
+ ax_pthread_save_CC="$CC"
+ ax_pthread_save_CFLAGS="$CFLAGS"
+ ax_pthread_save_LIBS="$LIBS"
+ AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
+ AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes])
+ AC_MSG_RESULT([$ax_pthread_ok])
+ if test "x$ax_pthread_ok" = "xno"; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+ CC="$ax_pthread_save_CC"
+ CFLAGS="$ax_pthread_save_CFLAGS"
+ LIBS="$ax_pthread_save_LIBS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try. Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important. Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+# other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64
+# (Note: HP C rejects this with "bad form for `-t' option")
+# -pthreads: Solaris/gcc (Note: HP C also rejects)
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+# doesn't hurt to check since this sometimes defines pthreads and
+# -D_REENTRANT too), HP C (must be checked before -lpthread, which
+# is present but should not be used directly; and before -mthreads,
+# because the compiler interprets this as "-mt" + "-hreads")
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case $host_os in
+
+ freebsd*)
+
+ # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+ # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+
+ ax_pthread_flags="-kthread lthread $ax_pthread_flags"
+ ;;
+
+ hpux*)
+
+ # From the cc(1) man page: "[-mt] Sets various -D flags to enable
+ # multi-threading and also sets -lpthread."
+
+ ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags"
+ ;;
+
+ openedition*)
+
+ # IBM z/OS requires a feature-test macro to be defined in order to
+ # enable POSIX threads at all, so give the user a hint if this is
+ # not set. (We don't define these ourselves, as they can affect
+ # other portions of the system API in unpredictable ways.)
+
+ AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING],
+ [
+# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS)
+ AX_PTHREAD_ZOS_MISSING
+# endif
+ ],
+ [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])])
+ ;;
+
+ solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+ # tests will erroneously succeed. (N.B.: The stubs are missing
+ # pthread_cleanup_push, or rather a function called by this macro,
+ # so we could check for that, but who knows whether they'll stub
+ # that too in a future libc.) So we'll check first for the
+ # standard Solaris way of linking pthreads (-mt -lpthread).
+
+ ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags"
+ ;;
+esac
+
+# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
+
+AS_IF([test "x$GCC" = "xyes"],
+ [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"])
+
+# The presence of a feature test macro requesting re-entrant function
+# definitions is, on some systems, a strong hint that pthreads support is
+# correctly enabled
+
+case $host_os in
+ darwin* | hpux* | linux* | osf* | solaris*)
+ ax_pthread_check_macro="_REENTRANT"
+ ;;
+
+ aix*)
+ ax_pthread_check_macro="_THREAD_SAFE"
+ ;;
+
+ *)
+ ax_pthread_check_macro="--"
+ ;;
+esac
+AS_IF([test "x$ax_pthread_check_macro" = "x--"],
+ [ax_pthread_check_cond=0],
+ [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"])
+
+# Are we compiling with Clang?
+
+AC_CACHE_CHECK([whether $CC is Clang],
+ [ax_cv_PTHREAD_CLANG],
+ [ax_cv_PTHREAD_CLANG=no
+ # Note that Autoconf sets GCC=yes for Clang as well as GCC
+ if test "x$GCC" = "xyes"; then
+ AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
+ [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
+# if defined(__clang__) && defined(__llvm__)
+ AX_PTHREAD_CC_IS_CLANG
+# endif
+ ],
+ [ax_cv_PTHREAD_CLANG=yes])
+ fi
+ ])
+ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
+
+ax_pthread_clang_warning=no
+
+# Clang needs special handling, because older versions handle the -pthread
+# option in a rather... idiosyncratic way
+
+if test "x$ax_pthread_clang" = "xyes"; then
+
+ # Clang takes -pthread; it has never supported any other flag
+
+ # (Note 1: This will need to be revisited if a system that Clang
+ # supports has POSIX threads in a separate library. This tends not
+ # to be the way of modern systems, but it's conceivable.)
+
+ # (Note 2: On some systems, notably Darwin, -pthread is not needed
+ # to get POSIX threads support; the API is always present and
+ # active. We could reasonably leave PTHREAD_CFLAGS empty. But
+ # -pthread does define _REENTRANT, and while the Darwin headers
+ # ignore this macro, third-party headers might not.)
+
+ PTHREAD_CFLAGS="-pthread"
+ PTHREAD_LIBS=
+
+ ax_pthread_ok=yes
+
+ # However, older versions of Clang make a point of warning the user
+ # that, in an invocation where only linking and no compilation is
+ # taking place, the -pthread option has no effect ("argument unused
+ # during compilation"). They expect -pthread to be passed in only
+ # when source code is being compiled.
+ #
+ # Problem is, this is at odds with the way Automake and most other
+ # C build frameworks function, which is that the same flags used in
+ # compilation (CFLAGS) are also used in linking. Many systems
+ # supported by AX_PTHREAD require exactly this for POSIX threads
+ # support, and in fact it is often not straightforward to specify a
+ # flag that is used only in the compilation phase and not in
+ # linking. Such a scenario is extremely rare in practice.
+ #
+ # Even though use of the -pthread flag in linking would only print
+ # a warning, this can be a nuisance for well-run software projects
+ # that build with -Werror. So if the active version of Clang has
+ # this misfeature, we search for an option to squash it.
+
+ AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
+ [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
+ [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
+ # Create an alternate version of $ac_link that compiles and
+ # links in two steps (.c -> .o, .o -> exe) instead of one
+ # (.c -> exe), because the warning occurs only in the second
+ # step
+ ax_pthread_save_ac_link="$ac_link"
+ ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
+ ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"`
+ ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
+ ax_pthread_save_CFLAGS="$CFLAGS"
+ for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
+ AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
+ CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
+ ac_link="$ax_pthread_save_ac_link"
+ AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
+ [ac_link="$ax_pthread_2step_ac_link"
+ AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
+ [break])
+ ])
+ done
+ ac_link="$ax_pthread_save_ac_link"
+ CFLAGS="$ax_pthread_save_CFLAGS"
+ AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
+ ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
+ ])
+
+ case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
+ no | unknown) ;;
+ *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
+ esac
+
+fi # $ax_pthread_clang = yes
+
+if test "x$ax_pthread_ok" = "xno"; then
+for ax_pthread_try_flag in $ax_pthread_flags; do
+
+ case $ax_pthread_try_flag in
+ none)
+ AC_MSG_CHECKING([whether pthreads work without any flags])
+ ;;
+
+ -mt,pthread)
+ AC_MSG_CHECKING([whether pthreads work with -mt -lpthread])
+ PTHREAD_CFLAGS="-mt"
+ PTHREAD_LIBS="-lpthread"
+ ;;
+
+ -*)
+ AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag])
+ PTHREAD_CFLAGS="$ax_pthread_try_flag"
+ ;;
+
+ pthread-config)
+ AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
+ AS_IF([test "x$ax_pthread_config" = "xno"], [continue])
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag])
+ PTHREAD_LIBS="-l$ax_pthread_try_flag"
+ ;;
+ esac
+
+ ax_pthread_save_CFLAGS="$CFLAGS"
+ ax_pthread_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
+# if $ax_pthread_check_cond
+# error "$ax_pthread_check_macro must be defined"
+# endif
+ static void routine(void *a) { a = 0; }
+ static void *start_routine(void *a) { return a; }],
+ [pthread_t th; pthread_attr_t attr;
+ pthread_create(&th, 0, start_routine, 0);
+ pthread_join(th, 0);
+ pthread_attr_init(&attr);
+ pthread_cleanup_push(routine, 0);
+ pthread_cleanup_pop(0) /* ; */])],
+ [ax_pthread_ok=yes],
+ [])
+
+ CFLAGS="$ax_pthread_save_CFLAGS"
+ LIBS="$ax_pthread_save_LIBS"
+
+ AC_MSG_RESULT([$ax_pthread_ok])
+ AS_IF([test "x$ax_pthread_ok" = "xyes"], [break])
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$ax_pthread_ok" = "xyes"; then
+ ax_pthread_save_CFLAGS="$CFLAGS"
+ ax_pthread_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+
+ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+ AC_CACHE_CHECK([for joinable pthread attribute],
+ [ax_cv_PTHREAD_JOINABLE_ATTR],
+ [ax_cv_PTHREAD_JOINABLE_ATTR=unknown
+ for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
+ [int attr = $ax_pthread_attr; return attr /* ; */])],
+ [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break],
+ [])
+ done
+ ])
+ AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \
+ test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \
+ test "x$ax_pthread_joinable_attr_defined" != "xyes"],
+ [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE],
+ [$ax_cv_PTHREAD_JOINABLE_ATTR],
+ [Define to necessary symbol if this constant
+ uses a non-standard name on your system.])
+ ax_pthread_joinable_attr_defined=yes
+ ])
+
+ AC_CACHE_CHECK([whether more special flags are required for pthreads],
+ [ax_cv_PTHREAD_SPECIAL_FLAGS],
+ [ax_cv_PTHREAD_SPECIAL_FLAGS=no
+ case $host_os in
+ solaris*)
+ ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS"
+ ;;
+ esac
+ ])
+ AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \
+ test "x$ax_pthread_special_flags_added" != "xyes"],
+ [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS"
+ ax_pthread_special_flags_added=yes])
+
+ AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
+ [ax_cv_PTHREAD_PRIO_INHERIT],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
+ [[int i = PTHREAD_PRIO_INHERIT;]])],
+ [ax_cv_PTHREAD_PRIO_INHERIT=yes],
+ [ax_cv_PTHREAD_PRIO_INHERIT=no])
+ ])
+ AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \
+ test "x$ax_pthread_prio_inherit_defined" != "xyes"],
+ [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])
+ ax_pthread_prio_inherit_defined=yes
+ ])
+
+ CFLAGS="$ax_pthread_save_CFLAGS"
+ LIBS="$ax_pthread_save_LIBS"
+
+ # More AIX lossage: compile with *_r variant
+ if test "x$GCC" != "xyes"; then
+ case $host_os in
+ aix*)
+ AS_CASE(["x/$CC"],
+ [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
+ [#handle absolute path differently from PATH based program lookup
+ AS_CASE(["x$CC"],
+ [x/*],
+ [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
+ [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
+ ;;
+ esac
+ fi
+fi
+
+test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
+
+AC_SUBST([PTHREAD_LIBS])
+AC_SUBST([PTHREAD_CFLAGS])
+AC_SUBST([PTHREAD_CC])
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test "x$ax_pthread_ok" = "xyes"; then
+ ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
+ :
+else
+ ax_pthread_ok=no
+ $2
+fi
+AC_LANG_POP
+])dnl AX_PTHREAD
diff --git a/nxcomp/src/Log.cpp b/nxcomp/src/Log.cpp
new file mode 100644
index 000000000..951c10a04
--- /dev/null
+++ b/nxcomp/src/Log.cpp
@@ -0,0 +1,121 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <iomanip>
+#include <unistd.h>
+
+#include "Log.h"
+#include "config.h"
+
+NXLog nx_log;
+
+
+bool NXLog::will_log() const
+{
+ std::map<std::string, NXLogLevel>::const_iterator item = per_file_levels_.find(current_file());
+
+ if ( item != per_file_levels_.end() )
+ {
+ return current_level() <= item->second;
+ }
+ else
+ {
+ return current_level() <= level();
+ }
+}
+
+
+std::string NXLog::stamp_to_string(const NXLogStamp& stamp) const
+{
+ std::ostringstream oss;
+
+ static const char* level_names[] = {
+ "FATAL",
+ "ERROR",
+ "WARN ",
+ "INFO ",
+ "DEBUG"
+ };
+
+ if ( log_level() )
+ oss << ((stamp.level() >=0 && stamp.level() < NXLOG_LEVEL_COUNT ) ? level_names[stamp.level()] : "???") << " ";
+
+ if ( log_time() )
+ {
+ struct timeval timestamp = stamp.timestamp();
+ struct tm timeinfo;
+
+ localtime_r(&timestamp.tv_sec, &timeinfo);
+
+ if ( log_unix_time() )
+ {
+ oss << timestamp.tv_sec;
+ }
+ else
+ {
+ #if HAVE_STD_PUT_TIME
+ oss << " " << std::put_time(&timeinfo, "%Y/%m/%d %H:%M:%S");
+ #else
+ oss << timestamp.tv_sec;
+ #endif
+ }
+
+ oss << "." << std::setw(3) << std::setfill('0') << (int)(timestamp.tv_usec / 1000) << " ";
+ }
+
+ if ( log_location() )
+ oss << stamp.file() << "/" << stamp.function() << ":" << stamp.line() << " ";
+
+ if ( log_thread_id() )
+ {
+ if ( thread_name().empty() )
+ oss << getpid() << "/" << pthread_self() << " ";
+ else
+ oss << "[" << thread_name() << "] ";
+ }
+
+ return oss.str();
+}
+
+NXLog& operator<< (NXLog& out, const NXLogStamp& value)
+{
+ out.current_level( value.level() );
+ out.current_file( value.file() );
+
+ // Writing an NXLogStamp to the stream indicates the start of a new entry.
+ // If there's any content in the buffer, create a new entry in the output
+ // queue.
+ if ( out.synchronized() )
+ out.new_stack_entry();
+
+ out << out.stamp_to_string(value);
+
+ return out;
+}
diff --git a/nxcomp/src/Log.h b/nxcomp/src/Log.h
new file mode 100644
index 000000000..aed929b31
--- /dev/null
+++ b/nxcomp/src/Log.h
@@ -0,0 +1,545 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+
+#ifndef NXLog_H
+#define NXLog_H
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <sys/time.h>
+
+#include <map>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <assert.h>
+#include <stack>
+
+/** Log severity level */
+enum NXLogLevel
+{
+ NXFATAL,
+ NXERROR,
+ NXWARNING,
+ NXINFO,
+ NXDEBUG,
+ NXLOG_LEVEL_COUNT
+};
+
+
+/**
+ * Log timestamp class
+ *
+ * Stores the timestamp, file, function, line number and log level.
+ * Acts as a manipulator on the NXLog class, telling it a new log
+ * severity level. For instance:
+ *
+ * nx_log << NXLogStamp(...,NXINFO)
+ *
+ * Tells nx_log that now NXINFO type messages are being logged. This
+ * will be applied until a new NXLogStamp with a different level
+ * is sent to the NXLog.
+ */
+class NXLogStamp
+{
+ private:
+ std::string file_;
+ std::string function_;
+ size_t line_;
+ NXLogLevel level_;
+ struct timeval timestamp_;
+
+ public:
+ /** File where the event occurred */
+ std::string file() const
+ {
+ return file_;
+ }
+
+ /** Function where the event occurred */
+ std::string function() const
+ {
+ return function_;
+ }
+
+ /** Line where the event occurred */
+ size_t line() const
+ {
+ return line_;
+ }
+
+ /** Severity level of the event */
+ NXLogLevel level() const
+ {
+ return level_;
+ }
+
+ /** Time of the event */
+ struct timeval timestamp() const
+ {
+ return timestamp_;
+ }
+
+
+ NXLogStamp(const char *file, const char *function, int line, NXLogLevel level)
+ {
+ file_ = std::string(file);
+ function_ = std::string(function);
+ line_ = line;
+ level_ = level;
+ gettimeofday(&timestamp_, NULL);
+ }
+
+};
+
+
+/**
+ * Log class
+ *
+ * Logs events to a stream, filters by file/level
+ */
+class NXLog
+{
+#ifdef INTERNAL_LOGGING_TEST
+ protected:
+#endif
+ NXLogLevel level_;
+
+ std::ostream *stream_;
+ std::map< std::string, NXLogLevel > per_file_levels_;
+ bool synchronized_;
+ size_t thread_buffer_size_;
+ pthread_mutex_t output_lock_;
+ pthread_key_t tls_key_;
+
+ bool log_level_;
+ bool log_time_;
+ bool log_unix_time_;
+ bool log_location_;
+ bool log_thread_id_;
+
+ typedef struct per_thread_data_s
+ {
+ NXLogLevel current_level;
+ std::string* current_file;
+ std::string* thread_name;
+ std::stack<std::stringstream*> buffer;
+ NXLog* log_obj;
+ } per_thread_data;
+
+
+ static void free_thread_data(void* arg)
+ {
+ per_thread_data *pdt = (per_thread_data*)arg;
+
+ if ( !pdt )
+ return;
+
+ if ( pdt->log_obj ) {
+ // Ensure the buffer is flushed before thread deletion
+ pdt->log_obj->flush(pdt);
+ }
+
+ delete pdt->current_file;
+ delete pdt->thread_name;
+
+ while (!pdt->buffer.empty()) {
+ (void) pdt->buffer.pop ();
+ }
+
+ delete pdt;
+ }
+
+ per_thread_data* get_data_int() const
+ {
+ per_thread_data *ret = NULL;
+
+ if ( (ret = (per_thread_data*)pthread_getspecific(tls_key_)) == NULL )
+ {
+ ret = new per_thread_data;
+ ret->current_level = NXDEBUG;
+ ret->current_file = new std::string();
+ ret->thread_name = new std::string();
+ ret->log_obj = const_cast<NXLog*>(this);
+ pthread_setspecific(tls_key_, ret);
+ }
+
+ return ret;
+ }
+
+ per_thread_data* get_data()
+ {
+ return get_data_int();
+ }
+
+ const per_thread_data* get_data() const
+ {
+ return get_data_int();
+ }
+
+ /** Convert NXLogStamp to string according to the current configuration */
+ std::string stamp_to_string(const NXLogStamp& stamp) const;
+
+ void new_stack_entry()
+ {
+ per_thread_data *pdt = get_data();
+ pdt->buffer.push(new std::stringstream());
+ }
+
+ /**
+ * Internal flush function
+ *
+ * When a thread is being terminated and free_thread_data gets called,
+ * the TLS key gets set to NULL before the call to free_thread_data,
+ * and the destructor function gets the old value.
+ *
+ * This means that get_data() stops working correctly, and we need
+ * to be able to pass the old pointer.
+ */
+ virtual /* Note: this function needs to be virtual for the logging test application. Don't remove. */
+ void flush(per_thread_data *pdt)
+ {
+ /*
+ * Block all signals until we are dong printing data.
+ * Ensures that a signal handler won't interrupt us
+ * and overwrite the buffer data mid-print, leading
+ * to confusing output.
+ */
+ sigset_t orig_signal_mask,
+ tmp_signal_mask;
+ sigemptyset(&orig_signal_mask);
+
+ /* Set up new mask to block all signals. */
+ sigfillset(&tmp_signal_mask);
+
+ /* Block all signals. */
+ pthread_sigmask(SIG_BLOCK, &tmp_signal_mask, &orig_signal_mask);
+
+ if (!pdt->buffer.empty ()) {
+ const std::string str = pdt->buffer.top()->str();
+
+ if (!str.empty())
+ {
+ pthread_mutex_lock(&output_lock_);
+ (*stream()) << str;
+ pthread_mutex_unlock(&output_lock_);
+ }
+
+ /* Remove from stack. */
+ pdt->buffer.pop();
+ }
+
+ /* Restore old signal mask. */
+ pthread_sigmask(SIG_SETMASK, &orig_signal_mask, NULL);
+ }
+
+
+ public:
+ NXLog()
+ {
+ stream_ = &std::cerr;
+ level_ = NXWARNING;
+ synchronized_ = true;
+ thread_buffer_size_ = 1024;
+ log_level_ = false;
+ log_time_ = false;
+ log_unix_time_ = false;
+ log_location_ = false;
+ log_thread_id_ = false;
+
+ if ( pthread_key_create(&tls_key_, free_thread_data) != 0 )
+ {
+ std::cerr << "pthread_key_create failed" << std::endl;
+ abort();
+ }
+
+ }
+
+ ~NXLog()
+ {
+ per_thread_data *pdt = get_data();
+
+ // Flush any remaining output and delete TLS data
+ free_thread_data(pdt);
+
+ pthread_key_delete(tls_key_);
+
+ if ((stream_) && (stream_ != &std::cerr)) {
+ delete stream_;
+ }
+ }
+
+ /** Minimum severity level to output */
+ NXLogLevel level() const
+ {
+ return level_;
+ }
+
+ void level(NXLogLevel level)
+ {
+ level_ = level;
+ }
+
+
+ /** Current severity level */
+ NXLogLevel current_level() const
+ {
+ return get_data()->current_level;
+ }
+
+ void current_level(NXLogLevel level)
+ {
+ get_data()->current_level = level;
+ }
+
+ /** Source file from which messages are currently originating */
+ std::string current_file() const
+ {
+ return *get_data()->current_file;
+ }
+
+ void current_file(std::string val)
+ {
+ *get_data()->current_file = val;
+ }
+
+ std::ostream* stream() const
+ {
+ return stream_;
+ }
+
+ void stream(std::ostream *stream)
+ {
+ flush();
+ stream_ = stream;
+ }
+
+ bool synchronized() const {
+ return synchronized_;
+ }
+
+ void synchronized(bool val) {
+ synchronized_ = val;
+ }
+
+ bool log_level() const
+ {
+ return log_level_;
+ }
+
+ void log_level(bool val)
+ {
+ log_level_ = val;
+ }
+
+ bool log_time() const
+ {
+ return log_time_;
+ }
+
+ void log_time(bool val)
+ {
+ log_time_ = val;
+ }
+
+ bool log_unix_time() const
+ {
+ return log_unix_time_;
+ }
+
+ void log_unix_time(bool val)
+ {
+ log_unix_time_ = val;
+ }
+
+ bool log_location() const
+ {
+ return log_location_;
+ }
+
+ void log_location(bool val)
+ {
+ log_location_ = val;
+ }
+
+ bool log_thread_id() const
+ {
+ return log_thread_id_;
+ }
+
+ void log_thread_id(bool val)
+ {
+ log_thread_id_ = val;
+ }
+
+
+ void flush()
+ {
+ per_thread_data *pdt = get_data();
+ flush(pdt);
+ }
+
+ std::string thread_name() const
+ {
+ return *get_data()->thread_name;
+ }
+
+ void thread_name(std::string str)
+ {
+ *get_data()->thread_name = str;
+ }
+
+ void thread_name(const char *str)
+ {
+ *get_data()->thread_name = str;
+ }
+
+ /**
+ * True if a message sent to the NXLog object will be sent to the output
+ *
+ * This considers two things:
+ *
+ * If there's a per-file log level, then it is used
+ * Otherwise the global log level is used.
+ *
+ * If the log level permits the current message to be output, then the
+ * return value is true.
+ */
+ bool will_log() const;
+
+
+ /**
+ * This catches std::flush
+ */
+ NXLog& operator<<(std::ostream& (*F)(std::ostream&))
+ {
+ if ( will_log() )
+ {
+ if ( synchronized() )
+ {
+ per_thread_data *pdt = get_data();
+ assert (!pdt->buffer.empty ());
+ (*pdt->buffer.top()) << F;
+ flush();
+ }
+ else
+ {
+ *(stream()) << F;
+ }
+ }
+
+ return *this;
+ }
+
+ template<typename T>
+ friend NXLog& operator<<(NXLog& out, const T& value);
+
+ friend NXLog& operator<< (NXLog& out, const NXLogStamp& value);
+};
+
+
+extern NXLog nx_log;
+
+
+#define nxstamp(l) NXLogStamp(__FILE__, __func__, __LINE__, l)
+
+
+#define nxdbg nx_log << nxstamp(NXDEBUG)
+#define nxinfo nx_log << nxstamp(NXINFO)
+#define nxwarn nx_log << nxstamp(NXWARNING)
+#define nxerr nx_log << nxstamp(NXERROR)
+#define nxfatal nx_log << nxstamp(NXFATAL)
+
+
+NXLog& operator<< (NXLog& out, const NXLogStamp& value);
+
+
+template <typename T>
+bool has_newline(T value)
+{
+ return false;
+}
+
+template <char*>
+static bool has_newline(char *value)
+{
+ return strstr(value, "\n") != NULL;
+}
+
+template <char>
+static bool has_newline(char value)
+{
+ return value == '\n';
+}
+
+template <std::string&>
+static bool has_newline(std::string &value)
+{
+ return value.find_first_of("\n") != std::string::npos;
+}
+
+static size_t ss_length(std::stringstream *ss)
+{
+ size_t pos = ss->tellg();
+ size_t ret = 0;
+ ss->seekg(0, std::ios::end);
+ ret = ss->tellg();
+ ss->seekg(pos, std::ios::beg);
+ return ret;
+}
+
+template <typename T>
+NXLog& operator<<(NXLog& out, const T& value)
+{
+ if ( out.will_log() )
+ {
+ if ( out.synchronized() )
+ {
+ // In synchronized mode, we buffer data until a newline, std::flush, or the buffer
+ // gets full. Then we dump the whole thing at once to the output stream, synchronizing
+ // with a mutex.
+ NXLog::per_thread_data *pdt = out.get_data();
+ assert (!pdt->buffer.empty ());
+ (*pdt->buffer.top()) << value;
+
+ if ( ss_length(pdt->buffer.top()) >= out.thread_buffer_size_ || has_newline(value) )
+ out.flush();
+
+ }
+ else
+ {
+ // In async mode we just dump data on the output stream as-is.
+ // Multithreaded code will have ugly output.
+ *(out.stream()) << value;
+ }
+
+ }
+
+ return out;
+}
+
+#endif
diff --git a/nxcomp/src/Loop.cpp b/nxcomp/src/Loop.cpp
index 4292e7b9a..681a5a1ef 100644
--- a/nxcomp/src/Loop.cpp
+++ b/nxcomp/src/Loop.cpp
@@ -101,6 +101,7 @@ typedef int socklen_t;
#include "Message.h"
#include "ChannelEndPoint.h"
+#include "Log.h"
//
// System specific defines.
@@ -9339,6 +9340,102 @@ int ParseCommandLineOptions(int argc, const char **argv)
return -1;
}
+ case 'd':
+ {
+ if ( argi+1 >= argc )
+ {
+ PrintUsageInfo(nextArg, 0);
+ return -1;
+ }
+
+ int level = 0;
+ errno = 0;
+ level = strtol(argv[argi+1], NULL, 10);
+
+ if ( errno && (level == 0) )
+ {
+ cerr << "Warning: Failed to parse log level. Ignoring option." << std::endl;
+ }
+ if ( level < 0 )
+ {
+ cerr << "Warning: Log level must be a positive integer. Ignoring option." << std::endl;
+ level = nx_log.level();
+ }
+ else if ( level >= NXLOG_LEVEL_COUNT )
+ {
+ cerr << "Warning: Log level is greater than the maximum " << NXLOG_LEVEL_COUNT-1 << ". Setting to the maximum." << std::endl;
+ level = NXLOG_LEVEL_COUNT-1;
+ }
+
+ nx_log.level( (NXLogLevel)level );
+
+ argi++;
+ break;
+
+ }
+ case 'o':
+ {
+ if ( argi + 1 >= argc )
+ {
+ PrintUsageInfo(nextArg, 0);
+ return -1;
+ }
+
+ std::ofstream *logfile = new std::ofstream();
+
+ // Unbuffered output
+ logfile->rdbuf()->pubsetbuf(0, 0);
+ logfile->open(argv[argi+1], std::ofstream::app);
+
+ if ( logfile->is_open() )
+ {
+ nx_log.stream(logfile);
+ }
+ else
+ {
+ cerr << "Failed to open log file " << argv[argi+1] << endl;
+ return -1;
+ }
+
+ argi++;
+ break;
+ }
+ case 'f':
+ {
+ if ( argi + 1 >= argc )
+ {
+ PrintUsageInfo(nextArg, 0);
+ return -1;
+ }
+
+ const char *format = argv[argi+1];
+ size_t pos = 0;
+
+ nx_log.log_level(false);
+ nx_log.log_time(false);
+ nx_log.log_unix_time(false);
+ nx_log.log_location(false);
+ nx_log.log_thread_id(false);
+
+ for(pos =0;pos<strlen(format);pos++)
+ {
+ switch(format[pos])
+ {
+ case '0': break;
+ case 't': nx_log.log_time(true); break;
+ case 'u': nx_log.log_time(true); nx_log.log_unix_time(true); break;
+ case 'l': nx_log.log_level(true); break;
+ case 'T': nx_log.log_thread_id(true); break;
+ case 'L': nx_log.log_location(true); break;
+ default : cerr << "Unrecognized format specifier: " << format[pos] << endl; break;
+ }
+ }
+
+ argi++;
+ break;
+ }
+
+
default:
{
PrintUsageInfo(nextArg, 1);
diff --git a/nxcomp/src/Makefile.am b/nxcomp/src/Makefile.am
index 2264cb347..682ddbaca 100644
--- a/nxcomp/src/Makefile.am
+++ b/nxcomp/src/Makefile.am
@@ -114,12 +114,14 @@ libXcomp_la_SOURCES = \
WriteBuffer.cpp \
XidCache.cpp \
Z.cpp \
+ Log.cpp \
$(NULL)
libXcomp_la_LIBADD = \
@JPEG_LIBS@ \
@PNG_LIBS@ \
@Z_LIBS@ \
+ @PTHREAD_LIBS@ \
$(NULL)
AM_CXXFLAGS = \
@@ -127,13 +129,18 @@ AM_CXXFLAGS = \
$(JPEG_CFLAGS) \
$(PNG_CFLAGS) \
$(Z_CFLAGS) \
+ $(PTHREAD_CFLAGS) \
$(NULL)
AM_CPPFLAGS = \
-I$(top_srcdir)/include \
$(NULL)
-libXcomp_la_LDFLAGS = -version-number @LT_COMP_VERSION@ -no-undefined
+libXcomp_la_LDFLAGS = \
+ -version-number @LT_COMP_VERSION@ \
+ -no-undefined \
+ $(PTHREAD_LDFLAGS) \
+ $(NULL)
libXcompincludedir = $(includedir)/nx
libXcompinclude_HEADERS = \
diff --git a/nxcomp/test/Makefile.am b/nxcomp/test/Makefile.am
new file mode 100644
index 000000000..412b8ea94
--- /dev/null
+++ b/nxcomp/test/Makefile.am
@@ -0,0 +1,22 @@
+NULL =
+
+noinst_PROGRAMS = logging_test
+EXTRA_DIST = logging_test
+
+AM_CPPFLAGS = -I$(top_srcdir)/src
+AM_CXXFLAGS = \
+ @PTHREAD_CFLAGS@ \
+ $(NULL)
+
+logging_test_SOURCES = logging_test.cpp
+logging_test_LDADD = \
+ $(top_srcdir)/src/.libs/libXcomp.a \
+ @PTHREAD_LIBS@ \
+ $(NULL)
+
+logging_test_LDFLAGS = \
+ $(PTHREAD_LDFLAGS) \
+ $(NULL)
+
+check: all
+ ./logging_test
diff --git a/nxcomp/test/logging_test.cpp b/nxcomp/test/logging_test.cpp
new file mode 100644
index 000000000..7e2d7d213
--- /dev/null
+++ b/nxcomp/test/logging_test.cpp
@@ -0,0 +1,224 @@
+#include <cstddef>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <cstdlib>
+#include <ctime>
+#include <climits>
+#include <vector>
+#include <cstring>
+
+#include "logging_test.h"
+
+Faulty_Logger faulty_logger;
+NXLog good_logger;
+
+void print_sigmask () {
+ sigset_t orig_mask;
+ sigemptyset (&orig_mask);
+
+ pthread_sigmask (SIG_SETMASK, NULL, &orig_mask);
+
+ bool empty = true;
+ for (std::size_t i = 0; i < NSIG; ++i) {
+ if (sigismember (&orig_mask, i)) {
+ nxdbg_good << "Signal i (" << i << ") in signal mask." << std::endl;
+ empty = false;
+ }
+ }
+
+ if (empty) {
+ nxdbg_good << "Signal mask empty.";
+ }
+}
+
+void* log_task (void* /* unused */) {
+ /* print_sigmask (); */
+
+ for (std::size_t i = 0; i < 10; ++i) {
+ nxinfo << "Log message " << i << std::endl;
+ }
+}
+
+void sig_handler (int signo) {
+ nxinfo << "Received signal " << signo << std::endl;
+}
+
+void setup_faulty_logger () {
+ faulty_logger.log_level (true);
+ faulty_logger.log_unix_time (false);
+ faulty_logger.log_time (true);
+ faulty_logger.log_location (true);
+ faulty_logger.log_thread_id (true);
+ faulty_logger.level (NXDEBUG);
+}
+
+void setup_good_logger () {
+ good_logger.log_level (true);
+ good_logger.log_unix_time (false);
+ good_logger.log_time (true);
+ good_logger.log_location (true);
+ good_logger.log_thread_id (true);
+ good_logger.level (NXDEBUG);
+}
+
+pthread_t spawn_thread () {
+ pthread_t thread_id;
+ int pthread_ret;
+
+ sigset_t block_mask, orig_mask;
+ sigemptyset (&orig_mask);
+ sigfillset (&block_mask);
+
+ pthread_sigmask (SIG_BLOCK, &block_mask, &orig_mask);
+
+ pthread_ret = pthread_create (&thread_id, NULL, log_task, NULL);
+
+ pthread_sigmask (SIG_SETMASK, &orig_mask, NULL);
+
+ return (thread_id);
+}
+
+void install_signal_handler () {
+ struct sigaction sa;
+ sa.sa_handler = sig_handler;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+ if (-1 == sigaction (SIGUSR1, &sa, NULL)) {
+ nxerr_good << "Unable to install signal handler!" << std::endl;
+ }
+ else {
+ nxdbg_good << "Signal handler registered successfully for SIGUSR1." << std::endl;
+ }
+}
+
+void killing_process_work (pid_t parent_pid) {
+ /* Seed PRNG. */
+ std::srand (std::time (0));
+
+ for (std::size_t i = 0; i < 25; ++i) {
+ /* Sleep for 4 seconds + some random number up to a second. */
+ std::size_t rand_add = (std::rand () % 1000000);
+ usleep (4000000 + rand_add);
+
+ /* Send SIGUSR1 to parent process. */
+ nxdbg_good << "Sending SIGUSR1 (" << SIGUSR1 << ") to parent_pid (" << parent_pid << ")" << std::endl;
+
+ if (kill (parent_pid, SIGUSR1)) {
+ int saved_errno = errno;
+ nxerr_good << "Failed to deliver signal to parent, aborting." << std::endl;
+ nxerr_good << "Error " << saved_errno << ": " << strerror (saved_errno) << std::endl;
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ exit (EXIT_SUCCESS);
+}
+
+void killing_process_init (int argc, char **argv) {
+ /* We're in the "killing process". */
+ pid_t parent_pid = getppid ();
+
+ setup_good_logger ();
+
+ for (std::size_t i = 0; i < argc; ++i) {
+ nxdbg_good << "argv[" << i << "]: " << argv[i] << std::endl;
+ }
+
+ char *end = NULL;
+
+ errno = 0;
+ long parent_pid_check = std::strtol (argv[1], &end, 0);
+
+ if ((errno == ERANGE) && (parent_pid_check == LONG_MAX)) {
+ /* Overflow, handle gracefully. */
+ parent_pid_check = 1;
+ }
+
+ if ((errno == ERANGE) && (parent_pid_check == LONG_MIN)) {
+ /* Underflow, handle gracefully. */
+ parent_pid_check = 1;
+ }
+
+ if (*end) {
+ /* Conversion error (for inputs like "<number>X", end will point to X.) */
+ parent_pid_check = 1;
+ }
+
+ if (parent_pid != parent_pid_check) {
+ nxinfo_good << "Parent PID verification via first argument failed, trusting getppid ()." << std::endl;
+ }
+
+ killing_process_work (parent_pid);
+}
+
+int main (int argc, char **argv) {
+ if (argc > 1) {
+ killing_process_init (argc, argv);
+ }
+ else {
+ /* That's the main process. */
+
+ /* First, fork and create the "killing process". */
+ pid_t pid = fork ();
+
+ if (0 == pid) {
+ /* Child process. */
+ pid_t parent_pid = getppid ();
+
+ /* Prepare to pass-through parent PID. */
+ std::stringstream ss;
+ ss << parent_pid;
+
+ std::vector<std::string> new_argv;
+ new_argv.push_back (std::string (argv[0]));
+ new_argv.push_back (ss.str ());
+
+ std::vector<char *> new_argv_c_str;
+ for (std::vector<std::string>::iterator it = new_argv.begin (); it != new_argv.end (); ++it) {
+ const char *elem = (*it).c_str ();
+ new_argv_c_str.push_back (strndup (elem, std::strlen (elem)));
+ }
+
+ /* Add null pointer as last element. */
+ new_argv_c_str.push_back (0);
+
+ /* Relaunch, with argv[1] containing the ppid. */
+ if (0 != execvp (new_argv_c_str.front (), &(new_argv_c_str.front ()))) {
+ const int saved_errno = errno;
+ std::cerr << "Failed to start \"killing process\"! Panic!" << std::endl;
+ std::cerr << "System error: " << std::strerror (saved_errno) << std::endl;
+ exit (EXIT_FAILURE);
+ }
+ }
+ else if (0 > pid) {
+ const int saved_errno = errno;
+ std::cerr << "Error while forking main process! Panic!" << std::endl;
+ std::cerr << "System error: " << std::strerror (saved_errno) << std::endl;
+ exit (EXIT_FAILURE);
+ }
+ else {
+ /* Main process. */
+ /* Falls through to general code below. */
+ }
+ }
+
+ setup_faulty_logger ();
+
+ pthread_t thread_id = spawn_thread ();
+
+ setup_good_logger ();
+
+ install_signal_handler ();
+
+ /* print_sigmask (); */
+
+ log_task (NULL);
+
+ pthread_join (thread_id, NULL);
+
+ exit (EXIT_SUCCESS);
+}
diff --git a/nxcomp/test/logging_test.h b/nxcomp/test/logging_test.h
new file mode 100644
index 000000000..239fbfe2b
--- /dev/null
+++ b/nxcomp/test/logging_test.h
@@ -0,0 +1,121 @@
+#ifndef LOGGING_TEST_H
+#define LOGGING_TEST_H
+
+#include <unistd.h>
+
+#define INTERNAL_LOGGING_TEST
+#include "Log.h"
+
+class Faulty_Logger : public NXLog {
+ /* Copied from base class, inserted "fault" within critical section. */
+ using NXLog::flush;
+ void flush(per_thread_data *pdt)
+ {
+ sigset_t orig_signal_mask,
+ tmp_signal_mask;
+ sigemptyset(&orig_signal_mask);
+
+ sigfillset(&tmp_signal_mask);
+
+ pthread_sigmask(SIG_BLOCK, &tmp_signal_mask, &orig_signal_mask);
+
+ if (!pdt->buffer.empty ()) {
+ const std::string str = pdt->buffer.top()->str();
+
+ if (!str.empty())
+ {
+ pthread_mutex_lock(&output_lock_);
+ usleep (3000000);
+ (*stream()) << str;
+ pthread_mutex_unlock(&output_lock_);
+ }
+
+ pdt->buffer.pop();
+ }
+
+ pthread_sigmask(SIG_SETMASK, &orig_signal_mask, NULL);
+ }
+
+ template<typename T>
+ friend Faulty_Logger& operator<<(Faulty_Logger& out, const T& value);
+
+ friend Faulty_Logger& operator<< (Faulty_Logger& out, const NXLogStamp& value);
+};
+
+template <typename T>
+Faulty_Logger& operator<<(Faulty_Logger& out, const T& value) {
+ if ( out.will_log() ) {
+ if ( out.synchronized() ) {
+ // In synchronized mode, we buffer data until a newline, std::flush, or the buffer
+ // gets full. Then we dump the whole thing at once to the output stream, synchronizing
+ // with a mutex.
+ Faulty_Logger::per_thread_data *pdt = out.get_data();
+ assert (!pdt->buffer.empty ());
+ usleep (1000000);
+ (*pdt->buffer.top()) << value;
+
+ if ( ss_length(pdt->buffer.top()) >= out.thread_buffer_size_ || has_newline(value) )
+ out.flush();
+ }
+ else {
+ // In async mode we just dump data on the output stream as-is.
+ // Multithreaded code will have ugly output.
+ *(out.stream()) << value;
+ }
+ }
+
+ return out;
+}
+
+Faulty_Logger& operator<< (Faulty_Logger& out, const NXLogStamp& value)
+{
+ out.current_level( value.level() );
+ out.current_file( value.file() );
+
+ // Writing an NXLogStamp to the stream indicates the start of a new entry.
+ // If there's any content in the buffer, create a new entry in the output
+ // queue.
+ if ( out.synchronized() )
+ out.new_stack_entry();
+
+ out << out.stamp_to_string(value);
+
+ return out;
+}
+
+#undef nxdbg
+#undef nxinfo
+#undef nxwarn
+#undef nxerr
+#undef nxfatal
+
+#define nxdbg faulty_logger << nxstamp(NXDEBUG)
+#define nxinfo faulty_logger << nxstamp(NXINFO)
+#define nxwarn faulty_logger << nxstamp(NXWARNING)
+#define nxerr faulty_logger << nxstamp(NXERROR)
+#define nxfatal faulty_logger << nxstamp(NXFATAL)
+
+#define nxdbg_good good_logger << nxstamp(NXDEBUG)
+#define nxinfo_good good_logger << nxstamp(NXINFO)
+#define nxwarn_good good_logger << nxstamp(NXWARNING)
+#define nxerr_good good_logger << nxstamp(NXERROR)
+#define nxfatal_good good_logger << nxstamp(NXFATAL)
+
+/* Helper functions used by all component. */
+void print_sigmask ();
+void setup_faulty_logger ();
+void setup_good_logger ();
+
+/* Functions used by both main and auxiliary threads. */
+void* log_task (void* /* unused */);
+
+/* Functions used in main thread only. */
+pthread_t spawn_thread ();
+void install_signal_handler ();
+void sig_handler (int signo);
+
+/* Functions used by "killing" process. */
+void killing_process_init (int argc, char **argv);
+void killing_process_work (pid_t parent_pid);
+
+#endif /* !defined (LOGGING_TEST_H) */