Unverified Commit de3d9ff4 authored by Mike Gabriel's avatar Mike Gabriel

Merge branch 'theqvd-improved-logging' into 3.6.x

parents 39ee56f5 ceac0776
# =============================================================================
# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
# =============================================================================
#
# SYNOPSIS
#
# AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional])
#
# DESCRIPTION
#
# Check for baseline language coverage in the compiler for the C++11
# standard; if necessary, add switches to CXX and CXXCPP to enable
# support.
#
# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX
# macro with the version set to C++11. The two optional arguments are
# forwarded literally as the second and third argument respectively.
# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for
# more information. If you want to use this macro, you also need to
# download the ax_cxx_compile_stdcxx.m4 file.
#
# LICENSE
#
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 18
AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX])
AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])])
......@@ -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
SUBDIRS = src
SUBDIRS = src test
pkgconfig_DATA = nxcomp.pc
......
......@@ -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
])
......
../../m4/ax_cxx_compile_stdcxx.m4
\ No newline at end of file
../../m4/ax_cxx_compile_stdcxx_11.m4
\ No newline at end of file
/**************************************************************************/
/* */
/* 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;
}
......@@ -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);
......
......@@ -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 = \
......
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
#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);
}
#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) */
......@@ -44,6 +44,12 @@ AC_LANG([C++])
NX_COMPILER_BRAND
NX_DEFAULT_OPTIONS
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])])])
AC_CONFIG_FILES([
Makefile
src/Makefile
......
../../m4/ax_cxx_compile_stdcxx.m4
\ No newline at end of file
../../m4/ax_cxx_compile_stdcxx_11.m4
\ No newline at end of file
......@@ -23,6 +23,12 @@ AC_LANG([C])
NX_COMPILER_BRAND
NX_DEFAULT_OPTIONS
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])])])
AC_CONFIG_FILES([
Makefile
man/Makefile
......
../../m4/ax_cxx_compile_stdcxx.m4
\ No newline at end of file
../../m4/ax_cxx_compile_stdcxx_11.m4
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment