&o
else
++c; // set to next valid char
+// printf("ADD: %s\n", arg);
out.push_back(std::string(arg));
} while (!done);
}
+/**
+ * Start the child process and waits until it finishes in the given time period.
+ * If 'ms' is zero then we wait forever until the process finishes.
+ *
+ * Internally this is the sequence of
+ * - start()
+ * - waitFor()
+ * - getRc()
+ *
+ * Returns:
+ * -1 : if ms is larger than zero and the process did not finish yet
+ * negative values: process terminated with signal / exception
+ * other : normal return code of the process
+ */
+int ChildProcess::run(unsigned ms)
+{
+ start();
+ if (waitFor(ms))
+ return getRc();
+ return -1;
+}
+
/**
* Now really start the child process.
*/
@@ -492,11 +515,7 @@ void ChildProcess::tryToStart()
else {
// PID < 0: start of process failed ...
active = false;
-
- int err = errno;
- char buffer[2048];
- sprintf(buffer, "Start of '%s' failed", programAndArgs[0].c_str());
- throw utils::RuntimeError(buffer, err);
+ throw utils::IOException("execv", programAndArgs[0].c_str());
}
}
@@ -540,7 +559,6 @@ void ChildProcess::validateRedirect()
{
if (active)
throw Exception("Child process already active, cannot change redirect any more!");
-
}
/**
diff --git a/src/utils/ChildProcess.h b/src/utils/ChildProcess.h
index 284aeac..c12809d 100644
--- a/src/utils/ChildProcess.h
+++ b/src/utils/ChildProcess.h
@@ -1,7 +1,7 @@
/*
* ChildProcess.h
*
- * Copyright (C) 2013 Holger Grosenick
+ * Copyright (C) 2013-2022 Holger Grosenick
*
* 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
@@ -36,6 +36,15 @@ struct Redirect;
/**
* This class is used for starting other processes asynchronously.
* The status of the process can be asked via the class methods.
+ *
+ * Usual way to start a ChildProcess is the call sequence:
+ * - start()
+ * - optionally use redirect methods like "stdout()"
+ * - waitFor()
+ * - getRc
+ *
+ * OR just
+ * - run()
*/
class ChildProcess {
@@ -64,12 +73,32 @@ class ChildProcess {
setWorkDir(workDir.c_str());
}
+ /**
+ * Start the child process and waits until it finishes in the given time period.
+ * If 'ms' is zero then we wait forever until the process finishes.
+ *
+ * When run() is used, redirecting stdout / stderr is not possible !
+ *
+ * Internally this is the sequence of
+ * - start()
+ * - waitFor()
+ * - getRc()
+ *
+ * Returns:
+ * -1 : if ms is larger than zero and the process did not finish yet
+ * negative values: process terminated with signal / exception
+ * other : normal return code of the process
+ */
+ int run(unsigned ms = 0);
+
/**
* Start the child process asynchronously and return. If starting the process fails, an
* exception is thrown.
*
* If a child has finished, the start() method maybe called again. Be aware that existing
* redirects get invalidated when the child terminates.
+ *
+ * After start() you have to use waitFor() to check if the process has finished.
*/
void start();
diff --git a/src/utils/Compatibility.h b/src/utils/Compatibility.h
index 30e742c..9e6f251 100644
--- a/src/utils/Compatibility.h
+++ b/src/utils/Compatibility.h
@@ -25,6 +25,7 @@
*/
#ifdef _WIN32
+//--- Windows ---
#include
// Compatibility implemented in DateTime.cpp
@@ -33,8 +34,11 @@ int gettimeofday(struct timeval* tp, struct timezone* tzp);
}
#define SSCANF sscanf_s
+#define strncasecmp _strnicmp
+#define strcasecmp _stricmp
#else
+//--- Linux ---
#define SSCANF sscanf
diff --git a/src/utils/DateTime.cpp b/src/utils/DateTime.cpp
index 2b15668..466743b 100644
--- a/src/utils/DateTime.cpp
+++ b/src/utils/DateTime.cpp
@@ -49,6 +49,18 @@ int gettimeofday(struct timeval* tp, struct timezone*)
#include "Exceptions.h"
+// Be aware that these sources are in UTF-8 !!
+// the LCD has a different charset: 0xE1 is 'ä'
+static const char MONTH_DE[13][6] = {
+ "Jan", "Feb", "Mär", "Apr", "Mai", "Jun",
+ "Jul", "Aug", "Sep", "Okt", "Nov", "Dez", "???"
+};
+//static const char MONTH_EN[13][6] = {
+// "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+// "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???"
+//};
+
+
namespace utils {
/**
@@ -119,11 +131,33 @@ DateTime::DateTime(const std::chrono::system_clock::time_point &now)
*
* If secondOffset is for example 86400, then one day is added.
*/
-DateTime::DateTime(const DateTime &other, int secondOffset)
- : secondsSinceEpoch(other.secondsSinceEpoch + secondOffset)
+DateTime::DateTime(const DateTime &other, int offset, OffsetType ot)
+ : secondsSinceEpoch(other.secondsSinceEpoch)
, microsecs(other.microsecs)
{
+ if (ot == OffsetType::SECONDS)
+ secondsSinceEpoch += offset;
+ else if (ot == OffsetType::DAYS)
+ secondsSinceEpoch += offset * (24 * 60 * 60);
+
makeTime();
+
+ if (ot == OffsetType::YEARS) {
+ init(time.tm_year + offset, time.tm_mon, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec);
+ }
+ else if (ot == OffsetType::MONTH) {
+ int yo = offset / 12;
+ int mo = offset % 12;
+ if (time.tm_mon + offset >= 13) {
+ ++yo;
+ mo -= 12;
+ }
+ else if (time.tm_mon + offset < 0) {
+ --yo;
+ mo += 12;
+ }
+ init(time.tm_year + yo, time.tm_mon + mo, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec);
+ }
}
/**
@@ -237,6 +271,28 @@ void DateTime::makeTime()
time.tm_mon += 1; // 1 .. 12
}
+
+/**
+ * Returns a month name with 3 chars
+ */
+const char* DateTime::monthName3() const
+{
+ Month mon = month();
+ if (mon >= JAN && mon <= DEC)
+ return MONTH_DE[mon - 1];
+ throw Exception("Invalid month value: %d", static_cast(mon));
+}
+
+/**
+ * Returns a month name with 3 chars
+ */
+const char* DateTime::monthName3(Month mon)
+{
+ if (mon >= JAN && mon <= DEC)
+ return MONTH_DE[mon - 1];
+ throw Exception("Invalid month value: %d", static_cast(mon));
+}
+
/**
* Is this DateTime before another DateTime (microseconds included!).
*/
diff --git a/src/utils/DateTime.h b/src/utils/DateTime.h
index 117dd2a..4410e6e 100644
--- a/src/utils/DateTime.h
+++ b/src/utils/DateTime.h
@@ -49,6 +49,13 @@ class DateTime {
void init(unsigned year, unsigned month, unsigned dayOfMonth, unsigned hour = 0, unsigned minute = 0, unsigned second = 0);
public:
+ /**
+ * Type of offset for the constructor which supports and offset
+ */
+ enum class OffsetType {
+ SECONDS, DAYS, MONTH, YEARS
+ };
+
/**
* Determine current date time with microseconds.
*/
@@ -78,7 +85,7 @@ class DateTime {
*
* If secondOffset is for example 86400, then one day is added.
*/
- explicit DateTime(const DateTime &other, int secondOffset);
+ explicit DateTime(const DateTime &other, int offset, OffsetType ot = OffsetType::SECONDS);
/**
* Init with single fields:
@@ -138,6 +145,16 @@ class DateTime {
return static_cast(time.tm_mon);
}
+ /**
+ * Returns a month name with 3 chars
+ */
+ const char* monthName3() const;
+
+ /**
+ * Returns a month name with 3 chars
+ */
+ static const char* monthName3(Month m);
+
/**
* Returns the day in month 1 .. 31
*/
diff --git a/src/utils/Exceptions.cpp b/src/utils/Exceptions.cpp
index db30edc..fd49bad 100644
--- a/src/utils/Exceptions.cpp
+++ b/src/utils/Exceptions.cpp
@@ -17,6 +17,7 @@
* along with this program. If not, see .
*/
+#include
#include
#include
@@ -25,6 +26,9 @@
namespace utils {
+// max message length for exceptions
+#define __MSG_LEN 1024
+
//--------------------------------------------------------------------------
// utils::Exception
//--------------------------------------------------------------------------
@@ -32,36 +36,35 @@ Exception::Exception(const std::string &m)
: msg(m)
{ }
-Exception::Exception(const char *m)
- : msg(m)
-{ }
+Exception::Exception(const char *m, ...)
+{
+ va_list args;
+ va_start(args, m);
+ setMessage(m, args);
+ va_end(args);
+}
-Exception::Exception(const char *m, int arg)
- : msg()
+Exception::~Exception() { }
+
+
+/**
+ * set message via printf style format
+ */
+void Exception::setMessage(const char *m, va_list args)
{
- char buffer[1024];
+ char buffer[__MSG_LEN];
- snprintf(buffer, sizeof(buffer), m, arg);
+ ::vsnprintf(buffer, sizeof(buffer), m, args);
buffer[sizeof(buffer) - 1] = 0;
msg = buffer;
}
-Exception::~Exception() { }
-
const char *Exception::what() const noexcept
{
return msg.c_str();
}
-//--------------------------------------------------------------------------
-// utils::ConnectionLostException
-//--------------------------------------------------------------------------
-ConnectionLostException::ConnectionLostException(const std::string &m)
-: Exception(m)
-{ }
-
-
//--------------------------------------------------------------------------
// utils::FileOpenError
//--------------------------------------------------------------------------
@@ -94,9 +97,9 @@ KeyNotFound::KeyNotFound(const std::string &messagePrefix, const std::string &ke
OutOfRange::OutOfRange(const std::string &hint, unsigned current, unsigned _max)
: Exception()
{
- char buffer[512];
- snprintf(buffer, sizeof(buffer), "%s: %u is out of range, max = %u",
- hint.length() > 0 && hint.length() < 150 ? "" : "Value", current, _max);
+ char buffer[__MSG_LEN];
+ ::snprintf(buffer, sizeof(buffer), "%s: %u is out of range, max = %u",
+ hint.length() > 0 && hint.length() < 150 ? "" : "Value", current, _max);
buffer[sizeof(buffer) - 1] = 0;
setMessage(hint + buffer);
}
@@ -104,9 +107,9 @@ OutOfRange::OutOfRange(const std::string &hint, unsigned current, unsigned _max)
OutOfRange::OutOfRange(const std::string &hint, unsigned current, unsigned _min, unsigned _max)
: Exception()
{
- char buffer[512];
- snprintf(buffer, sizeof(buffer), "%s: %u is out of range, expected range is %u .. %u",
- hint.length() > 0 && hint.length() < 150 ? "" : "Value", current, _min, _max);
+ char buffer[__MSG_LEN];
+ ::snprintf(buffer, sizeof(buffer), "%s: %u is out of range, expected range is %u .. %u",
+ hint.length() > 0 && hint.length() < 150 ? "" : "Value", current, _min, _max);
buffer[sizeof(buffer) - 1] = 0;
setMessage(hint + buffer);
}
@@ -133,10 +136,13 @@ RuntimeError::RuntimeError(const char *msg, int _errno)
//--------------------------------------------------------------------------
// utils::ValueFormatError
//--------------------------------------------------------------------------
-ValueFormatError::ValueFormatError(const std::string &m)
- : Exception(m)
-{ }
-
+ValueFormatError::ValueFormatError(const char *m, ...)
+{
+ va_list args;
+ va_start(args, m);
+ setMessage(m, args);
+ va_end(args);
+}
//--------------------------------------------------------------------------
// utils::IOException
diff --git a/src/utils/Exceptions.h b/src/utils/Exceptions.h
index f5a32c1..f2c4153 100644
--- a/src/utils/Exceptions.h
+++ b/src/utils/Exceptions.h
@@ -1,7 +1,7 @@
/*
* Exceptions.h
*
- * Copyright (C) 2013 Holger Grosenick
+ * Copyright (C) 2013-2022 Holger Grosenick
*
* 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
@@ -22,11 +22,18 @@
#include
#include
+#include
#include "Compatibility.h"
namespace utils {
+#ifdef __GNUC__
+#define __PRINTF_CHECK __attribute__ ((__format__ (__printf__, 2, 3)))
+#else
+#define __PRINTF_CHECK
+#endif
+
/**
* Base exception class which holds a message text: this is returned
@@ -42,6 +49,9 @@ class Exception : public std::exception
protected:
Exception() { }
+ // set message via printf style format in derived classes
+ void setMessage(const char *msg, va_list args);
+
void setMessage(const std::string &m) {
msg = m;
}
@@ -52,8 +62,11 @@ class Exception : public std::exception
public:
explicit Exception(const std::string &msg);
- explicit Exception(const char *msg);
- explicit Exception(const char *msg, int arg);
+
+ /**
+ * Use a printf style format to build the error message
+ */
+ explicit Exception(const char *msg, ...) __PRINTF_CHECK;
// std::~exception is already virtual ...
virtual ~Exception();
@@ -68,7 +81,7 @@ class Exception : public std::exception
class ConnectionLostException : public Exception
{
public:
- explicit ConnectionLostException(const std::string &msg);
+ explicit ConnectionLostException(const std::string &msg) : Exception(msg) { }
};
@@ -81,6 +94,7 @@ class ConnectionLostException : public Exception
class IOException : public Exception
{
public:
+ // message: func + '(' + args + "): " + errno
IOException(const std::string &func, const std::string &args);
IOException(int _errno, const std::string &func, const std::string &args);
};
@@ -113,9 +127,9 @@ class FileOpenError : public IOException
class ValueFormatError : public Exception
{
public:
- explicit ValueFormatError(const std::string &msg);
- explicit ValueFormatError(const char *msg, int arg)
- : Exception(msg, arg) { }
+ explicit ValueFormatError(const std::string &msg) : Exception(msg) { }
+
+ explicit ValueFormatError(const char *msg, ...) __PRINTF_CHECK;
};
/**
@@ -125,8 +139,8 @@ class ValueFormatError : public Exception
class RuntimeError : public Exception
{
public:
- explicit RuntimeError(const char *msg);
explicit RuntimeError(const std::string &msg);
+ explicit RuntimeError(const char *msg);
explicit RuntimeError(const char *msg, int _errno);
};
diff --git a/src/utils/File.cpp b/src/utils/File.cpp
index d73f17f..ea19b7e 100644
--- a/src/utils/File.cpp
+++ b/src/utils/File.cpp
@@ -331,9 +331,16 @@ std::string File::getAbsolutePath() const
if (!exists())
throw std::logic_error("Cannot determine the absolute path of a non-existing file");
- std::filesystem::path p;
- p = fullname;
- return std::filesystem::absolute(p).string();
+ char actualpath [PATH_MAX+1];
+#ifdef _WIN32
+ char* ptr = _fullpath(actualpath, fullname.c_str(), PATH_MAX);
+#else
+ char *ptr = realpath(fullname.c_str(), actualpath);
+#endif
+ if (!ptr) {
+ throw std::logic_error("realpath() returned an error");
+ }
+ return actualpath;
}
/**
diff --git a/src/utils/File.h b/src/utils/File.h
index 425b698..b87368c 100644
--- a/src/utils/File.h
+++ b/src/utils/File.h
@@ -330,7 +330,7 @@ class File : public Object
*
* If the file is not found, an exception is thrown.
*
- * @param envName - name of the environment variable
+ * @param envName - name of the environment variable which holds the search path (e.g. "PATH")
* @param filename - name of the file to look for
* @throws utils::KeyNotFound if the environment variable does not exist or the specified
* file was not found along the path.
diff --git a/src/utils/FileVisitor.cpp b/src/utils/FileVisitor.cpp
index 39f841b..bad7c29 100644
--- a/src/utils/FileVisitor.cpp
+++ b/src/utils/FileVisitor.cpp
@@ -1,7 +1,7 @@
/*
* FileVisitor.cpp
*
- * Copyright (C) 2014 Holger Grosenick
+ * Copyright (C) 2014-2022 Holger Grosenick
*
* 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
@@ -18,6 +18,7 @@
*/
#include
+#include "Compatibility.h"
#include "File.h"
#include "FileFilter.h"
#include "FileVisitor.h"
@@ -62,4 +63,56 @@ FileVisitor::VisitResult FileCollector::afterVisitDirectory(const File &)
return VisitResult::CONTINUE;
}
+// ---------------------------------------------------------------------------------------------------
+
+/**
+ * Search the file without wildcards, filename is handled case sensitive by default.
+ */
+FileFinder::FileFinder(const std::string &toFind, bool caseSensitive)
+ : toFind(toFind)
+ , caseSensitive(caseSensitive)
+ , visitCount(0)
+{
+}
+
+/**
+ * Search the file without wildcards, filename is handled case sensitive by default.
+ */
+FileFinder::FileFinder(const char *toFind, bool caseSensitive)
+ : toFind(toFind)
+ , caseSensitive(caseSensitive)
+ , visitCount(0)
+{
+}
+
+FileFinder::VisitResult FileFinder::visitFile(const File &f)
+{
+ ++visitCount;
+ if (caseSensitive) {
+ if (f.getName() == toFind) {
+ result = f.getAbsolutePath();
+ return FileFinder::VisitResult::STOP;
+ }
+ }
+ else {
+ if (strcasecmp(f.getName().c_str(), toFind.c_str()) == 0) {
+ result = f.getAbsolutePath();
+ return FileFinder::VisitResult::STOP;
+ }
+ }
+ return VisitResult::CONTINUE;
+}
+
+FileFinder::VisitResult FileFinder::visitDirectory(const File &dir)
+{
+ ++visitCount;
+ return VisitResult::CONTINUE;
+}
+
+FileFinder::VisitResult FileFinder::afterVisitDirectory(const File &dir)
+{
+ return VisitResult::CONTINUE;
+}
+
+
} /* namespace utils */
diff --git a/src/utils/FileVisitor.h b/src/utils/FileVisitor.h
index 97c2f04..c15b830 100644
--- a/src/utils/FileVisitor.h
+++ b/src/utils/FileVisitor.h
@@ -137,6 +137,56 @@ class FileCollector : public FileVisitor
virtual VisitResult afterVisitDirectory(const File &dir) override;
};
+
+/**
+ * Recurses into all directories and searches the given file.
+ */
+class FileFinder : public FileVisitor
+{
+ const std::string toFind;
+ const bool caseSensitive;
+ unsigned visitCount;
+ std::string result;
+
+public:
+ /**
+ * Search the file without wildcards, filename is handled case sensitive by default.
+ */
+ explicit FileFinder(const std::string &toFind, bool caseSensitive = true);
+
+ /**
+ * Search the file without wildcards, filename is handled case sensitive by default.
+ */
+ explicit FileFinder(const char *toFind, bool caseSensitive = true);
+
+ /**
+ * Return the full path to the file or an empty string if not found.
+ */
+ const std::string& getResult() const {
+ return result;
+ }
+
+ /**
+ * If the result is not empty, then we found the file.
+ */
+ bool found() const {
+ return ! result.empty();
+ }
+
+ /**
+ * Returns the number of files and directories checked (more for test purpose).
+ */
+ unsigned getVisitCount() const {
+ return visitCount;
+ }
+
+ virtual VisitResult visitFile(const File &f) override;
+
+ virtual VisitResult visitDirectory(const File &dir) override;
+
+ virtual VisitResult afterVisitDirectory(const File &dir) override;
+};
+
} /* namespace utils */
#endif /* FILEVISITOR_H_ */
diff --git a/src/utils/Log.h b/src/utils/Log.h
index 28f9869..baf1f53 100644
--- a/src/utils/Log.h
+++ b/src/utils/Log.h
@@ -138,8 +138,11 @@ class Log {
static void error(const std::string& msg, const char *arg);
static void error(const std::string& msg, int arg);
- // exception trace
- static void error(const char* msg, const std::exception &ex);
+ /**
+ * Exception trace:
+ * Exception of type 't' in: 'func': ex.what()
+ */
+ static void error(const char* func, const std::exception &ex);
static void error(const std::string& func, const std::exception &ex);
/**
diff --git a/src/utils/Properties.cpp b/src/utils/Properties.cpp
index 0797ea2..8e535c6 100644
--- a/src/utils/Properties.cpp
+++ b/src/utils/Properties.cpp
@@ -1,7 +1,7 @@
/*
* Properties.cpp
*
- * Copyright (C) 2013-2021 Holger Grosenick
+ * Copyright (C) 2013-2022 Holger Grosenick
*
* 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
@@ -97,7 +97,7 @@ Properties::Properties(const std::string &data, char separator)
if (c == '\\') {
c = data[++i];
if (c == 'x' && i+2 < last) {
- c = hexValue(data[i+1], data[i+2]);
+ c = hex2int(data[i+1], data[i+2]);
i+=2;
}
os << c;
@@ -341,10 +341,7 @@ bool Properties::getBool(const std::string &key) const
return true;
if (strcmp(v, "false") != 0)
{
- char buffer[256];
- snprintf(buffer, sizeof(buffer), "Invalid bool value: '%s', key was '%s'", v, key.c_str());
- buffer[sizeof(buffer) - 1] = 0;
- throw ValueFormatError(buffer);
+ throw ValueFormatError("Invalid bool value: '%s', key was '%s'", v, key.c_str());
}
return false;
}
@@ -422,14 +419,16 @@ double Properties::getDouble(const std::string &key, double defaultValue) const
*/
void Properties::put(const char *key, const char *value)
{
- std::pair res = values.insert(std::make_pair(key, value));
+ // std::pair
+ auto res = values.insert(std::make_pair(key, value));
if (false == res.second)
(res.first)->second = value;
}
void Properties::put(const std::string &key, const std::string &value)
{
- std::pair res = values.insert(std::make_pair(key, value));
+ // std::pair
+ auto res = values.insert(std::make_pair(key, value));
if (false == res.second)
(res.first)->second = value;
}
diff --git a/src/utils/SignalHandlers.cpp b/src/utils/SignalHandlers.cpp
index 21377c4..de60940 100644
--- a/src/utils/SignalHandlers.cpp
+++ b/src/utils/SignalHandlers.cpp
@@ -30,24 +30,28 @@ namespace utils {
// global flag to indicate that the application should finish
#define MAX_SINGALS 64
static bool calledSignal[MAX_SINGALS];
-
+static int signalFromPid;
/**
* Signal handler to terminate the application if 'shouldFinish' is checked regularly.
*/
-static void sigTermHandler(int sig, siginfo_t *, void *)
+#ifndef _WIN32
+static void sigTermHandler(int sig, siginfo_t *info, void *)
{
static unsigned count = 0;
if (++count == 5) {
- Log() << "sigTermHandler(" << sig << ") called 5 times => hard exit ...";
+ Log() << "sigTermHandler(" << sig << ") triggered 5 times => hard exit ...";
exit(16);
}
+ signalFromPid = info ? info->si_pid : 0;
if (count == 1)
- Log() << "sigTermHandler(" << sig << ") called => finish ...";
+ Log() << "sigTermHandler(" << sig << ") triggered from pid "
+ << signalFromPid << " => should finish ...";
else
- Log() << "sigTermHandler(" << sig << ") called (" << count << " times) => finish ...";
+ Log() << "sigTermHandler(" << sig << ") triggered (" << count << " times) from pid "
+ << signalFromPid << " => should finish ...";
calledSignal[SIGTERM] = true;
}
@@ -56,9 +60,11 @@ static void sigTermHandler(int sig, siginfo_t *, void *)
/**
* Signal handler for all other signals that should just set a flag.
*/
-static void anySignalHandler(int sig, siginfo_t *, void *)
+static void anySignalHandler(int sig, siginfo_t *info, void *)
{
- // Log() << "anySignalHandler(" << sig << ") called";
+ Log() << "anySignalHandler(" << sig << ") triggered from pid " << signalFromPid;
+
+ signalFromPid = info ? info->si_pid : 0;
if (sig > 0 && sig < MAX_SINGALS)
calledSignal[sig] = true;
}
@@ -68,9 +74,10 @@ static void anySignalHandler(int sig, siginfo_t *, void *)
*/
static void sigKillHandler(int sig, siginfo_t *, void *)
{
- Log() << "CAUGHT CRITICAL SIGNAL: signal=" << sig << '(' << SignalHandlers::signal2char(sig) << ") => abort!";
+ Log() << "CAUGHT CRITICAL SIGNAL " << sig << " (" << SignalHandlers::signal2char(sig) << ") => abort!";
exit(16);
}
+#endif
/**
* Activate some defaults for signal handlers:
@@ -171,6 +178,14 @@ bool SignalHandlers::gotSignal(Signal sigNum)
return calledSignal[osSignal];
}
+/**
+ * Returns the process id (PID) which sent the latest signal to this process.
+ */
+int SignalHandlers::pidOfLatestSignal()
+{
+ return signalFromPid;
+}
+
// set signal handler for example for SIGUSR1 or similar
void SignalHandlers::setHandler(Signal sigNum, void (*function)(int, void *sigInfo, void *u_context))
@@ -194,31 +209,36 @@ void SignalHandlers::setHandler(Signal sigNum, void (*function)(int, void *sigIn
*/
const char *SignalHandlers::signal2char(int signum)
{
- if (signum == SIGBUS)
- return "SIGBUS";
- if (signum == SIGSEGV)
- return "SIGSEGV";
- if (signum == SIGPIPE)
- return "SIGPIPE";
+ static char sigStr[64];
+
if (signum == SIGILL)
return "SIGILL";
if (signum == SIGFPE)
return "SIGFPE";
+ if (signum == SIGSEGV)
+ return "SIGSEGV";
+ if (signum == SIGINT)
+ return "SIGINT";
+ if (signum == SIGTERM)
+ return "SIGTERM";
+#ifndef _WIN32
+ if (signum == SIGBUS)
+ return "SIGBUS";
+ if (signum == SIGPIPE)
+ return "SIGPIPE";
if (signum == SIGKILL)
return "SIGKILL";
if (signum == SIGCHLD)
return "SIGCHLD";
if (signum == SIGUSR1)
return "SIGUSR1";
-#ifdef SIGUSR2
if (signum == SIGUSR2)
return "SIGUSR2";
#endif
- if (signum == SIGINT)
- return "SIGINT";
- if (signum == SIGTERM)
- return "SIGTERM";
- return "unknown SIG";
+
+ snprintf(sigStr, sizeof(sigStr), "SIG(%d)", signum);
+ sigStr[sizeof(sigStr) - 1] = 0;
+ return sigStr;
}
/**
@@ -228,16 +248,18 @@ const char *SignalHandlers::signal2char(int signum)
int SignalHandlers::signal2osSignal(Signal s)
{
switch (s) {
- case SIG_HANGUP:
- return SIGHUP;
case SIG_INT:
return SIGINT;
+ case SIG_TERM:
+ return SIGTERM;
+#ifndef _WIN32
+ case SIG_HANGUP:
+ return SIGHUP;
case SIG_USER1:
return SIGUSR1;
case SIG_USER2:
return SIGUSR2;
- case SIG_TERM:
- return SIGTERM;
+#endif
}
Log::log("SignalHandlers::signal2osSignal: not supported signal", s);
return -1;
diff --git a/src/utils/SignalHandlers.h b/src/utils/SignalHandlers.h
index 8dee805..a6dc073 100644
--- a/src/utils/SignalHandlers.h
+++ b/src/utils/SignalHandlers.h
@@ -100,6 +100,11 @@ class SignalHandlers {
*/
static int signal2osSignal(Signal s);
+ /**
+ * Returns the process id (PID) which sent the latest signal to this process.
+ */
+ static int pidOfLatestSignal();
+
/**
* Returns true if Ctrl-C was pressed / SIGINT was triggered: soft exit.
* This method is equal to "hasSignal(SIG_TERM) || hasSignal(SIG_INT)".
diff --git a/src/utils/StringUtil.cpp b/src/utils/StringUtil.cpp
index 1e6ae4b..0ccbb72 100644
--- a/src/utils/StringUtil.cpp
+++ b/src/utils/StringUtil.cpp
@@ -274,7 +274,7 @@ void toString(char buf[512], double d, unsigned digits, char sep)
char fmt[32];
if (!buf)
- throw RuntimeError("buffer may not be NULL");
+ throw std::invalid_argument("buffer may not be NULL");
if (digits > 20)
throw OutOfRange("Number of digits may not be larger than 20", digits, 20);
diff --git a/src/utils/ValueProvider.cpp b/src/utils/ValueProvider.cpp
index c55336f..c410abc 100644
--- a/src/utils/ValueProvider.cpp
+++ b/src/utils/ValueProvider.cpp
@@ -408,9 +408,7 @@ size_t StoredValueProvider::checkSequence()
if (it.timeOffset < act)
{
- char error[128];
- snprintf(error, sizeof(error), "Relative time in row %u is lower than previous one!", row);
- throw Exception(error);
+ throw Exception("Relative time in row %u is lower than previous one!", row);
}
act = it.timeOffset;
}
@@ -541,9 +539,7 @@ void StoredValueProvider::parseLine(std::string &line, unsigned lineNo)
value = strtol(val.c_str(), &endPtr, base);
if (endPtr == val.c_str())
{
- char error[128];
- snprintf(error, sizeof(error), "Invalid line format in value of line %u", lineNo);
- throw Exception(error);
+ throw Exception("Invalid line format in value of line %u", lineNo);
}
// add more offset to all following values?
diff --git a/src/utils/ValueProviderCSV.cpp b/src/utils/ValueProviderCSV.cpp
index c55bb46..26e9c76 100644
--- a/src/utils/ValueProviderCSV.cpp
+++ b/src/utils/ValueProviderCSV.cpp
@@ -86,7 +86,6 @@ CSVValueProvider::CSVValueProvider(const std::string &filename)
unsigned numCols = csv.getNumCols();
const std::vector< const char* > &cols = csv.getColumns();
std::vector columnData;
- char msg[512];
uint64_t offset = 0;
//printf("NumCols = %u in %s\n", numCols, filename);
@@ -94,9 +93,7 @@ CSVValueProvider::CSVValueProvider(const std::string &filename)
while (csv.loadLine()) {
unsigned size = static_cast(cols.size());
if (size != numCols) {
- snprintf(msg, sizeof(msg), "Invalid number of columns (%u, expected is %u) in line %u of %s", size, numCols, csv.getLine(), filename.c_str());
- msg[sizeof(msg) - 1] = 0;
- throw utils::ValueFormatError(msg);
+ throw utils::ValueFormatError("Invalid number of columns (%u, expected is %u) in line %u of %s", size, numCols, csv.getLine(), filename.c_str());
}
if (values.empty() && strchr(cols[0], ':') != NULL) {
@@ -116,13 +113,11 @@ CSVValueProvider::CSVValueProvider(const std::string &filename)
uint64_t v = parseTimestamp(cols[0]);
if (v > 0) {
v -= offset;
- columnData.push_back(v);
+ columnData.push_back(static_cast(v));
// printf("Store offset %ld in line %d\n", v, csv.getLine());
}
else {
- snprintf(msg, sizeof(msg), "Invalid timestamp conversion in line %u of %s", csv.getLine(), filename.c_str());
- msg[sizeof(msg) - 1] = 0;
- throw utils::ValueFormatError(msg);
+ throw utils::ValueFormatError("Invalid timestamp conversion in line %u of %s", csv.getLine(), filename.c_str());
}
}
else {
@@ -151,10 +146,7 @@ size_t CSVValueProvider::checkSequence()
++row;
if (it[0] < act)
{
- char msg[200];
- snprintf(msg, sizeof(msg), "Relative time in data row %u is lower than previous one (%d < %d)!", row, it[0], act);
- msg[sizeof(msg) - 1] = 0;
- throw Exception(msg);
+ throw Exception("Relative time in data row %u is lower than previous one (%d < %d)!", row, it[0], act);
}
act = it[0];
}
diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp
index f05c998..1d23e05 100644
--- a/src/utils/utils.cpp
+++ b/src/utils/utils.cpp
@@ -22,6 +22,8 @@
#include
#else
#include
+#include
+#include
#include
#include
#include
@@ -30,9 +32,14 @@
#include
#include
#include
+#include
+#include
+#include
#include
+#include
#include "Log.h"
+#include "StringUtil.h"
#include "utils.h"
@@ -41,6 +48,7 @@
static const char BASE58_ALPHABET[] = \
"123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
+namespace fs = std::filesystem;
namespace utils {
@@ -151,8 +159,105 @@ unsigned int base58Decode(const char *str)
}
+/**
+ * Create a shell file which contains the current environment and the commandline
+ * that was called.
+ *
+ * argPos - the position of the command line option that was used to trigger
+ * dumping the command line, this argument and the next one are skipped
+ * in the dump (use -1 to skip nothing)
+ * argc, argv, env - as passed to main()
+ *
+ * returns true if the dump was OK, false in case of file errors.
+ */
+bool dumpAsShell(int argPos, int argc, char const* const* argv, char const* const* env)
+{
+ const char *file = argv[argPos + 1];
+ if (!file || !(*file))
+ return false;
+
+ // wrong arguments
+ if (!argv)
+ return false;
+
+ // start writing the file
+ std::ofstream os(file, std::ofstream::out);
+ if (!os.is_open()) {
+ Log::perror(file);
+ return false;
+ }
+
+ // make the file readable + exec for the owner only, no other user
+ fs::permissions(file, fs::perms::owner_all, fs::perm_options::replace);
+
+ if (env) {
+ // these variables should not be dumped, they are re-created in normal ssh shell
+ std::list excludes;
+ excludes.push_back("_");
+ excludes.push_back("HOME"); // user specific
+ excludes.push_back("HOST"); // current host
+ excludes.push_back("LESS"); // only for 'less'
+ excludes.push_back("LS_"); // only for 'less'
+ excludes.push_back("LOG_NAME"); // user specific
+ excludes.push_back("SHLVL"); // shell specific
+ excludes.push_back("SSH_"); // ssh, never overwrite !
+ excludes.push_back("USER"); // user specific
+ excludes.push_back("XTERM"); // user specific
+
+ // sort the env list and remove vars which are mentioned in the exclude list
+ const char *shell = nullptr;
+ std::list envList;
+
+ for (int i = 0; env[i]; ++i) {
+ bool skip = false;
+ std::string e = env[i];
+
+ // find the shell name
+ if (strings::startsWith(e, "SHELL=")) {
+ shell = strchr(env[i], '=');
+ if (shell)
+ ++shell;
+ }
+
+ // check for excludes
+ for (auto &s : excludes) {
+ if (strings::startsWith(e, s)) {
+ skip = true;
+ break;
+ }
+ }
+ if (!skip)
+ envList.push_back(env[i]);
+ }
+
+ envList.sort();
+ if (shell) {
+ os << "#!" << shell << std::endl;
+
+ }
+ os << std::endl;
+ for (auto e : envList) {
+ os << "export " << e << std::endl;
+ }
+ }
+
+ os << std::endl;
+ for (int i = 0; i < argc && argv[i]; ++i) {
+ if (i == argPos) {
+ // skip the arg to dump the environment and call, so that if the shell
+ // is started again, this should not dump again.
+ ++i;
+ }
+ else {
+ os << argv[i] << ' ';
+ }
+ }
+ os << std::endl << std::endl;
+ return true;
+}
+
// returns the current process id like "getpid()" on Linux
-uint64_t getProcessId()
+int getProcessId()
{
return getpid();
}
@@ -163,7 +268,7 @@ uint64_t getProcessId()
*
* If a non hex char was input: an exception is thrown !
*/
-int hexValue(char c1)
+int hex2int(char c1)
{
if (c1 >= '0' && c1 <= '9')
return c1 - '0';
@@ -180,8 +285,94 @@ int hexValue(char c1)
/**
* Return the hexValue of two hex chars: hexValue(c1)*16 + hexValue(c2)
*/
-int hexValue(char c1, char c2) {
- return hexValue(c1)*16 + hexValue(c2);
+int hex2int(char c1, char c2) {
+ return hex2int(c1)*16 + hex2int(c2);
+}
+
+/**
+ * Return the hexValue of a string with hex sequence chars
+ */
+int64_t hex2int(const char *in)
+{
+ if (!in)
+ return 0;
+
+ int64_t result = 0;
+ while (*in) {
+ result *= 16;
+ result += hex2int(*in);
+ ++in;
+ }
+ return result;
+}
+
+/**
+ * Convert a numeric value with base 10 into a string.
+ */
+template char* convertToAscii(char *dest, int size, T value)
+{
+ if (value == 0) {
+ dest[0] = '0';
+ dest[1] = 0;
+ return dest;
+ }
+
+ int i = 0;
+ bool sign = false;
+ char buffer[128];
+
+ // only check if the type is signed
+ if (std::is_signed::value) {
+ sign = value < 0;
+ }
+
+ // now convert
+ while (value) {
+ int mod = (value % 10);
+ buffer[i] = '0' + (mod < 0 ? -mod : mod);
+ ++i;
+ value /= 10;
+
+ if (i >= size) {
+ char msg[256];
+ sprintf(msg, "convertToAscii: value exceeds '%u' chars", size);
+ throw std::overflow_error(msg);
+ }
+ }
+
+ // copy back in reverse (correct order).
+ int d = 0;
+ if (sign) {
+ // value was negative: start with minus
+ dest[d++] = '-';
+ }
+
+ // copy back in correct order
+ while (i > 0) {
+ dest[d++] = buffer[--i];
+ }
+ dest[d] = 0;
+
+ return dest;
+}
+
+/**
+ * Integer to ascii
+ */
+char* itoa(char dest[16], int32_t value) {
+ return convertToAscii(dest, 16, value);
+}
+
+char* uitoa(char dest[16], uint32_t value) {
+ return convertToAscii(dest, 16, value);
+}
+
+char* ltoa(char dest[32], int64_t value) {
+ return convertToAscii(dest, 32, value);
+}
+
+char* ultoa(char dest[32], uint64_t value) {
+ return convertToAscii(dest, 32, value);
}
// sleep some milliseconds
@@ -240,4 +431,28 @@ bool redirectStdout(const char *logName)
return result;
}
+/**
+ * Similar to the standard snprintf, but with one difference:
+ * if the buffer is too small, an exception is thrown.
+ */
+int snprintf(char *str, size_t size, const char *format, ...)
+{
+ str[size-1] = 0;
+
+ va_list args;
+ va_start(args, format);
+ size_t res = ::vsnprintf(str, size, format, args);
+ va_end(args);
+
+ if (str[size-1] != 0 || res >= size) {
+ char value1[32];
+ char value2[32];
+ ltoa(value1, size);
+ ltoa(value2, res);
+ throw std::length_error(std::string("utils::snprintf overflow, size=") + value1 + " result=" + value2);
+ }
+
+ return res;
+}
+
}
diff --git a/src/utils/utils.h b/src/utils/utils.h
index 570fcd9..eeae0a5 100644
--- a/src/utils/utils.h
+++ b/src/utils/utils.h
@@ -1,7 +1,7 @@
/*
* utils.h
*
- * Copyright (C) 2013 Holger Grosenick
+ * Copyright (C) 2013-2022 Holger Grosenick
*
* 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
@@ -31,7 +31,19 @@ void msleep(int ms);
void usleep(int us);
// returns the current process id, like "getpid()" on Linux
-uint64_t getProcessId();
+int getProcessId();
+
+// convert long to char with 10 a base, use a 32 byte buffer which will be null terminated at the end
+char* ltoa(char dest[32], int64_t value);
+char* ultoa(char dest[32], uint64_t value);
+char* itoa(char dest[16], int32_t value);
+char* uitoa(char dest[16], uint32_t value);
+
+/**
+ * Similar to the standard snprintf, but with one difference:
+ * if the buffer is too small, an exception is thrown.
+ */
+int snprintf(char *str, size_t size, const char *format, ...);
/**
* Convert an int into a base58 encoded string.
@@ -39,6 +51,19 @@ uint64_t getProcessId();
const std::string base58Encode(unsigned int value);
unsigned int base58Decode(const char *value);
+/**
+ * Create a shell file which contains the current environment and the commandline
+ * that was called.
+ *
+ * argPos - the position of the command line option that was used to trigger
+ * dumping the command line, this argument and the next one are skipped
+ * in the dump (use -1 to skip nothing)
+ * argc, argv, env - as passed to main()
+ *
+ * returns true if the dump was OK, false in case of file errors.
+ */
+bool dumpAsShell(int argPos, int argc, char const* const* argv, char const* const* env);
+
/**
* Open a log-file: redirect stdout + stderr into a file.
* Do nothing if the log-name is NULL or empty.
@@ -64,12 +89,23 @@ void bits2bool(unsigned statesIn, bool statesOut[], unsigned num);
*
* If a non hex char was input: an exception is thrown !
*/
-int hexValue(char c1);
+int hex2int(char c1);
/**
* Return the hexValue of two hex chars: hexValue(c1)*16 + hexValue(c2)
*/
-int hexValue(char c1, char c2);
+int hex2int(char c1, char c2);
+
+/**
+ * Return the hexValue of a string with hex sequence chars.
+ *
+ * Be aware:
+ * the result is int64 => ffffffff is a positive value.
+ */
+int64_t hex2int(const char *in);
+inline int64_t hex2int(const std::string &in) {
+ return hex2int(in.c_str());
+}
}