2019-02-25 14:48:22 +01:00
|
|
|
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
|
2012-05-10 12:06:41 +02:00
|
|
|
|
2022-08-01 17:44:05 +02:00
|
|
|
#include "base/atomic-file.hpp"
|
2014-05-25 16:23:35 +02:00
|
|
|
#include "base/utility.hpp"
|
|
|
|
#include "base/convert.hpp"
|
|
|
|
#include "base/application.hpp"
|
2024-08-21 12:08:04 +02:00
|
|
|
#include "base/defer.hpp"
|
2014-10-19 14:21:12 +02:00
|
|
|
#include "base/logger.hpp"
|
2014-05-25 16:23:35 +02:00
|
|
|
#include "base/exception.hpp"
|
2014-05-26 17:32:18 +02:00
|
|
|
#include "base/socket.hpp"
|
2014-06-12 22:51:48 +02:00
|
|
|
#include "base/utility.hpp"
|
2014-10-27 15:12:19 +01:00
|
|
|
#include "base/json.hpp"
|
2014-11-02 07:22:00 +01:00
|
|
|
#include "base/objectlock.hpp"
|
2021-01-12 17:32:28 +01:00
|
|
|
#include <algorithm>
|
2019-02-22 11:37:07 +01:00
|
|
|
#include <cstdint>
|
2012-05-09 10:15:51 +02:00
|
|
|
#include <mmatch.h>
|
2021-01-12 17:32:28 +01:00
|
|
|
#include <boost/filesystem.hpp>
|
2013-03-15 18:21:29 +01:00
|
|
|
#include <boost/lexical_cast.hpp>
|
2019-04-10 11:21:05 +02:00
|
|
|
#include <boost/system/error_code.hpp>
|
2017-11-21 12:12:58 +01:00
|
|
|
#include <boost/thread/tss.hpp>
|
2014-03-23 19:39:25 +01:00
|
|
|
#include <boost/algorithm/string/trim.hpp>
|
2014-11-02 07:22:00 +01:00
|
|
|
#include <boost/algorithm/string/replace.hpp>
|
2024-08-21 11:15:34 +02:00
|
|
|
#include <boost/numeric/conversion/cast.hpp>
|
2017-11-07 13:20:25 +01:00
|
|
|
#include <boost/uuid/uuid_io.hpp>
|
2017-12-14 18:48:58 +01:00
|
|
|
#include <boost/uuid/uuid_generators.hpp>
|
2019-08-14 11:22:55 +02:00
|
|
|
#include <boost/regex.hpp>
|
2014-10-22 19:25:29 +02:00
|
|
|
#include <ios>
|
|
|
|
#include <fstream>
|
|
|
|
#include <iostream>
|
2019-03-14 09:34:51 +01:00
|
|
|
#include <iterator>
|
2018-11-19 14:59:20 +01:00
|
|
|
#include <stdlib.h>
|
2017-11-21 11:52:55 +01:00
|
|
|
#include <future>
|
2021-01-12 17:32:28 +01:00
|
|
|
#include <set>
|
2019-03-14 09:34:51 +01:00
|
|
|
#include <utf8.h>
|
|
|
|
#include <vector>
|
2013-03-16 21:18:53 +01:00
|
|
|
|
2013-10-21 09:52:45 +02:00
|
|
|
#ifdef __FreeBSD__
|
|
|
|
# include <pthread_np.h>
|
|
|
|
#endif /* __FreeBSD__ */
|
|
|
|
|
2014-09-02 13:02:22 +02:00
|
|
|
#ifdef HAVE_CXXABI_H
|
2013-03-16 21:18:53 +01:00
|
|
|
# include <cxxabi.h>
|
2014-09-02 13:02:22 +02:00
|
|
|
#endif /* HAVE_CXXABI_H */
|
2012-04-22 16:45:31 +02:00
|
|
|
|
2014-10-30 19:52:22 +01:00
|
|
|
#ifndef _WIN32
|
2015-11-22 12:36:50 +01:00
|
|
|
# include <sys/types.h>
|
2018-12-20 15:48:10 +01:00
|
|
|
# include <sys/utsname.h>
|
2015-11-22 12:36:50 +01:00
|
|
|
# include <pwd.h>
|
|
|
|
# include <grp.h>
|
2017-02-09 09:08:48 +01:00
|
|
|
# include <errno.h>
|
2021-01-12 17:32:28 +01:00
|
|
|
# include <unistd.h>
|
2014-10-30 19:52:22 +01:00
|
|
|
#endif /* _WIN32 */
|
|
|
|
|
2015-11-22 12:36:50 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
# include <VersionHelpers.h>
|
2016-02-22 16:47:41 +01:00
|
|
|
# include <windows.h>
|
|
|
|
# include <io.h>
|
2016-03-30 18:59:23 +02:00
|
|
|
# include <msi.h>
|
2016-04-01 08:25:36 +02:00
|
|
|
# include <shlobj.h>
|
2015-11-22 12:36:50 +01:00
|
|
|
#endif /*_WIN32*/
|
2014-10-30 19:52:22 +01:00
|
|
|
|
2012-04-22 16:45:31 +02:00
|
|
|
using namespace icinga;
|
|
|
|
|
2013-03-25 18:36:15 +01:00
|
|
|
boost::thread_specific_ptr<String> Utility::m_ThreadName;
|
2013-12-06 21:46:50 +01:00
|
|
|
boost::thread_specific_ptr<unsigned int> Utility::m_RandSeed;
|
2013-03-25 18:36:15 +01:00
|
|
|
|
2016-05-31 17:09:22 +02:00
|
|
|
#ifdef I2_DEBUG
|
|
|
|
double Utility::m_DebugTime = -1;
|
|
|
|
#endif /* I2_DEBUG */
|
|
|
|
|
2012-07-11 20:55:46 +02:00
|
|
|
/**
|
2012-09-27 09:38:28 +02:00
|
|
|
* Demangles a symbol name.
|
2012-07-11 20:55:46 +02:00
|
|
|
*
|
2012-09-27 09:38:28 +02:00
|
|
|
* @param sym The symbol name.
|
|
|
|
* @returns A human-readable version of the symbol name.
|
2012-07-11 20:55:46 +02:00
|
|
|
*/
|
2012-09-27 09:38:28 +02:00
|
|
|
String Utility::DemangleSymbolName(const String& sym)
|
2012-07-11 20:55:46 +02:00
|
|
|
{
|
2012-09-27 09:38:28 +02:00
|
|
|
String result = sym;
|
2012-07-11 20:55:46 +02:00
|
|
|
|
2014-09-02 13:02:22 +02:00
|
|
|
#ifdef HAVE_CXXABI_H
|
2012-07-11 20:55:46 +02:00
|
|
|
int status;
|
2018-01-04 09:18:50 +01:00
|
|
|
char *realname = abi::__cxa_demangle(sym.CStr(), nullptr, nullptr, &status);
|
2012-07-11 20:55:46 +02:00
|
|
|
|
2017-12-14 15:37:20 +01:00
|
|
|
if (realname) {
|
2012-09-27 09:38:28 +02:00
|
|
|
result = String(realname);
|
2012-07-11 20:55:46 +02:00
|
|
|
free(realname);
|
|
|
|
}
|
2014-09-02 13:02:22 +02:00
|
|
|
#elif defined(_MSC_VER) /* HAVE_CXXABI_H */
|
2013-11-05 20:25:26 +01:00
|
|
|
CHAR output[256];
|
|
|
|
|
|
|
|
if (UnDecorateSymbolName(sym.CStr(), output, sizeof(output), UNDNAME_COMPLETE) > 0)
|
|
|
|
result = output;
|
2014-09-02 13:02:22 +02:00
|
|
|
#else /* _MSC_VER */
|
|
|
|
/* We're pretty much out of options here. */
|
2013-11-03 13:45:26 +01:00
|
|
|
#endif /* _MSC_VER */
|
2012-07-11 20:55:46 +02:00
|
|
|
|
2012-09-27 09:38:28 +02:00
|
|
|
return result;
|
2012-07-11 20:55:46 +02:00
|
|
|
}
|
|
|
|
|
2012-09-27 09:38:28 +02:00
|
|
|
/**
|
|
|
|
* Returns a human-readable type name of a type_info object.
|
|
|
|
*
|
|
|
|
* @param ti A type_info object.
|
|
|
|
* @returns The type name of the object.
|
|
|
|
*/
|
2013-03-16 21:18:53 +01:00
|
|
|
String Utility::GetTypeName(const std::type_info& ti)
|
2012-09-27 09:38:28 +02:00
|
|
|
{
|
|
|
|
return DemangleSymbolName(ti.name());
|
|
|
|
}
|
2012-07-11 20:55:46 +02:00
|
|
|
|
2014-03-23 19:39:25 +01:00
|
|
|
String Utility::GetSymbolName(const void *addr)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_DLADDR
|
|
|
|
Dl_info dli;
|
|
|
|
|
2014-08-04 08:45:38 +02:00
|
|
|
if (dladdr(const_cast<void *>(addr), &dli) > 0)
|
2014-03-23 19:39:25 +01:00
|
|
|
return dli.dli_sname;
|
|
|
|
#endif /* HAVE_DLADDR */
|
|
|
|
|
2014-03-30 01:18:30 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
char buffer[sizeof(SYMBOL_INFO)+MAX_SYM_NAME * sizeof(TCHAR)];
|
|
|
|
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
|
|
|
|
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
|
|
|
pSymbol->MaxNameLen = MAX_SYM_NAME;
|
|
|
|
|
|
|
|
DWORD64 dwAddress = (DWORD64)addr;
|
|
|
|
DWORD64 dwDisplacement;
|
|
|
|
|
|
|
|
IMAGEHLP_LINE64 line;
|
|
|
|
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
|
|
|
|
|
|
|
if (SymFromAddr(GetCurrentProcess(), dwAddress, &dwDisplacement, pSymbol)) {
|
|
|
|
char output[256];
|
|
|
|
if (UnDecorateSymbolName(pSymbol->Name, output, sizeof(output), UNDNAME_COMPLETE))
|
|
|
|
return String(output) + "+" + Convert::ToString(dwDisplacement);
|
|
|
|
else
|
|
|
|
return String(pSymbol->Name) + "+" + Convert::ToString(dwDisplacement);
|
|
|
|
}
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
|
|
|
|
return "(unknown function)";
|
2014-03-23 19:39:25 +01:00
|
|
|
}
|
|
|
|
|
2012-05-14 19:14:23 +02:00
|
|
|
/**
|
|
|
|
* Performs wildcard pattern matching.
|
|
|
|
*
|
|
|
|
* @param pattern The wildcard pattern.
|
2012-08-02 09:38:08 +02:00
|
|
|
* @param text The String that should be checked.
|
2012-05-14 19:14:23 +02:00
|
|
|
* @returns true if the wildcard pattern matches, false otherwise.
|
|
|
|
*/
|
2013-02-02 09:19:49 +01:00
|
|
|
bool Utility::Match(const String& pattern, const String& text)
|
2012-05-09 10:15:51 +02:00
|
|
|
{
|
2012-08-02 09:38:08 +02:00
|
|
|
return (match(pattern.CStr(), text.CStr()) == 0);
|
2012-05-09 10:15:51 +02:00
|
|
|
}
|
2012-07-08 21:18:35 +02:00
|
|
|
|
2015-10-14 12:11:04 +02:00
|
|
|
static bool ParseIp(const String& ip, char addr[16], int *proto)
|
|
|
|
{
|
|
|
|
if (inet_pton(AF_INET, ip.CStr(), addr + 12) == 1) {
|
|
|
|
/* IPv4-mapped IPv6 address (::ffff:<ipv4-bits>) */
|
|
|
|
memset(addr, 0, 10);
|
|
|
|
memset(addr + 10, 0xff, 2);
|
|
|
|
*proto = AF_INET;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inet_pton(AF_INET6, ip.CStr(), addr) == 1) {
|
|
|
|
*proto = AF_INET6;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-10-14 10:11:49 +02:00
|
|
|
static void ParseIpMask(const String& ip, char mask[16], int *bits)
|
|
|
|
{
|
|
|
|
String::SizeType slashp = ip.FindFirstOf("/");
|
|
|
|
String uip;
|
|
|
|
|
|
|
|
if (slashp == String::NPos) {
|
|
|
|
uip = ip;
|
|
|
|
*bits = 0;
|
|
|
|
} else {
|
|
|
|
uip = ip.SubStr(0, slashp);
|
|
|
|
*bits = Convert::ToLong(ip.SubStr(slashp + 1));
|
|
|
|
}
|
|
|
|
|
2015-10-14 12:11:04 +02:00
|
|
|
int proto;
|
|
|
|
|
|
|
|
if (!ParseIp(uip, mask, &proto))
|
|
|
|
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid IP address specified."));
|
|
|
|
|
2015-10-19 10:40:48 +02:00
|
|
|
if (proto == AF_INET) {
|
|
|
|
if (*bits > 32 || *bits < 0)
|
|
|
|
BOOST_THROW_EXCEPTION(std::invalid_argument("Mask must be between 0 and 32 for IPv4 CIDR masks."));
|
|
|
|
|
2015-10-14 10:11:49 +02:00
|
|
|
*bits += 96;
|
2015-10-19 10:40:48 +02:00
|
|
|
}
|
2015-10-14 10:11:49 +02:00
|
|
|
|
|
|
|
if (slashp == String::NPos)
|
|
|
|
*bits = 128;
|
|
|
|
|
2015-10-14 12:11:04 +02:00
|
|
|
if (*bits > 128 || *bits < 0)
|
2015-10-19 10:40:48 +02:00
|
|
|
BOOST_THROW_EXCEPTION(std::invalid_argument("Mask must be between 0 and 128 for IPv6 CIDR masks."));
|
2015-10-14 10:11:49 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < 16; i++) {
|
2015-10-19 10:40:48 +02:00
|
|
|
int lbits = std::max(0, *bits - i * 8);
|
2015-10-14 10:11:49 +02:00
|
|
|
|
|
|
|
if (lbits >= 8)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (mask[i] & (0xff >> lbits))
|
|
|
|
BOOST_THROW_EXCEPTION(std::invalid_argument("Masked-off bits must all be zero."));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool IpMaskCheck(char addr[16], char mask[16], int bits)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 16; i++) {
|
|
|
|
if (bits < 8)
|
|
|
|
return !((addr[i] ^ mask[i]) >> (8 - bits));
|
|
|
|
|
|
|
|
if (mask[i] != addr[i])
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bits -= 8;
|
|
|
|
|
|
|
|
if (bits == 0)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Utility::CidrMatch(const String& pattern, const String& ip)
|
|
|
|
{
|
2015-10-14 12:11:04 +02:00
|
|
|
char mask[16];
|
2015-10-14 10:11:49 +02:00
|
|
|
int bits;
|
|
|
|
|
|
|
|
ParseIpMask(pattern, mask, &bits);
|
|
|
|
|
2015-10-14 12:11:04 +02:00
|
|
|
char addr[16];
|
|
|
|
int proto;
|
|
|
|
|
|
|
|
if (!ParseIp(ip, addr, &proto))
|
|
|
|
return false;
|
|
|
|
|
2015-10-14 10:11:49 +02:00
|
|
|
return IpMaskCheck(addr, mask, bits);
|
|
|
|
}
|
|
|
|
|
2012-07-08 21:18:35 +02:00
|
|
|
/**
|
|
|
|
* Returns the directory component of a path. See dirname(3) for details.
|
|
|
|
*
|
|
|
|
* @param path The full path.
|
|
|
|
* @returns The directory.
|
|
|
|
*/
|
2012-08-02 09:38:08 +02:00
|
|
|
String Utility::DirName(const String& path)
|
2012-07-08 21:18:35 +02:00
|
|
|
{
|
2019-04-10 12:31:53 +02:00
|
|
|
return boost::filesystem::path(path.Begin(), path.End()).parent_path().string();
|
2012-07-08 21:18:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the file component of a path. See basename(3) for details.
|
|
|
|
*
|
|
|
|
* @param path The full path.
|
|
|
|
* @returns The filename.
|
|
|
|
*/
|
2012-08-02 09:38:08 +02:00
|
|
|
String Utility::BaseName(const String& path)
|
2012-07-08 21:18:35 +02:00
|
|
|
{
|
2019-04-10 12:31:53 +02:00
|
|
|
return boost::filesystem::path(path.Begin(), path.End()).filename().string();
|
2012-07-08 21:18:35 +02:00
|
|
|
}
|
2012-07-24 10:50:53 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Null deleter. Used as a parameter for the shared_ptr constructor.
|
|
|
|
*
|
2012-09-17 13:35:55 +02:00
|
|
|
* @param - The object that should be deleted.
|
2012-07-24 10:50:53 +02:00
|
|
|
*/
|
2012-08-07 21:02:12 +02:00
|
|
|
void Utility::NullDeleter(void *)
|
2012-07-24 10:50:53 +02:00
|
|
|
{
|
|
|
|
/* Nothing to do here. */
|
|
|
|
}
|
2012-07-25 12:59:17 +02:00
|
|
|
|
2016-05-31 17:09:22 +02:00
|
|
|
#ifdef I2_DEBUG
|
|
|
|
/**
|
|
|
|
* (DEBUG / TESTING ONLY) Sets the current system time to a static value,
|
|
|
|
* that will be be retrieved by any component of Icinga, when using GetTime().
|
|
|
|
*
|
|
|
|
* This should be only used for testing purposes, e.g. unit tests and debugging of certain functionalities.
|
|
|
|
*/
|
|
|
|
void Utility::SetTime(double time)
|
|
|
|
{
|
|
|
|
m_DebugTime = time;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* (DEBUG / TESTING ONLY) Increases the set debug system time by X seconds.
|
|
|
|
*
|
|
|
|
* This should be only used for testing purposes, e.g. unit tests and debugging of certain functionalities.
|
|
|
|
*/
|
|
|
|
void Utility::IncrementTime(double diff)
|
|
|
|
{
|
|
|
|
m_DebugTime += diff;
|
|
|
|
}
|
|
|
|
#endif /* I2_DEBUG */
|
|
|
|
|
2012-07-25 12:59:17 +02:00
|
|
|
/**
|
|
|
|
* Returns the current UNIX timestamp including fractions of seconds.
|
|
|
|
*
|
|
|
|
* @returns The current time.
|
|
|
|
*/
|
2018-01-04 04:25:35 +01:00
|
|
|
double Utility::GetTime()
|
2012-07-25 12:59:17 +02:00
|
|
|
{
|
2016-05-31 17:09:22 +02:00
|
|
|
#ifdef I2_DEBUG
|
|
|
|
if (m_DebugTime >= 0) {
|
|
|
|
// (DEBUG / TESTING ONLY) this will return a *STATIC* system time, if the value has been set!
|
|
|
|
return m_DebugTime;
|
|
|
|
}
|
|
|
|
#endif /* I2_DEBUG */
|
2012-07-27 16:05:02 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
FILETIME cft;
|
|
|
|
GetSystemTimeAsFileTime(&cft);
|
|
|
|
|
|
|
|
ULARGE_INTEGER ucft;
|
|
|
|
ucft.HighPart = cft.dwHighDateTime;
|
|
|
|
ucft.LowPart = cft.dwLowDateTime;
|
|
|
|
|
|
|
|
SYSTEMTIME est = { 1970, 1, 4, 1, 0, 0, 0, 0};
|
|
|
|
FILETIME eft;
|
|
|
|
SystemTimeToFileTime(&est, &eft);
|
|
|
|
|
|
|
|
ULARGE_INTEGER ueft;
|
|
|
|
ueft.HighPart = eft.dwHighDateTime;
|
|
|
|
ueft.LowPart = eft.dwLowDateTime;
|
|
|
|
|
|
|
|
return ((ucft.QuadPart - ueft.QuadPart) / 10000) / 1000.0;
|
|
|
|
#else /* _WIN32 */
|
2012-07-25 12:59:17 +02:00
|
|
|
struct timeval tv;
|
|
|
|
|
2017-12-14 15:37:20 +01:00
|
|
|
int rc = gettimeofday(&tv, nullptr);
|
2015-03-03 09:16:53 +01:00
|
|
|
VERIFY(rc >= 0);
|
2012-07-25 12:59:17 +02:00
|
|
|
|
|
|
|
return tv.tv_sec + tv.tv_usec / 1000000.0;
|
2012-07-27 16:05:02 +02:00
|
|
|
#endif /* _WIN32 */
|
2012-07-25 12:59:17 +02:00
|
|
|
}
|
2012-09-19 13:00:48 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the ID of the current process.
|
|
|
|
*
|
|
|
|
* @returns The PID.
|
|
|
|
*/
|
2018-01-04 04:25:35 +01:00
|
|
|
pid_t Utility::GetPid()
|
2012-09-19 13:00:48 +02:00
|
|
|
{
|
|
|
|
#ifndef _WIN32
|
|
|
|
return getpid();
|
|
|
|
#else /* _WIN32 */
|
|
|
|
return GetCurrentProcessId();
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
}
|
2012-09-25 15:41:43 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Sleeps for the specified amount of time.
|
|
|
|
*
|
|
|
|
* @param timeout The timeout in seconds.
|
|
|
|
*/
|
|
|
|
void Utility::Sleep(double timeout)
|
|
|
|
{
|
|
|
|
#ifndef _WIN32
|
2016-08-10 11:51:13 +02:00
|
|
|
unsigned long micros = timeout * 1000000u;
|
|
|
|
if (timeout >= 1.0)
|
|
|
|
sleep((unsigned)timeout);
|
|
|
|
|
|
|
|
usleep(micros % 1000000u);
|
2012-09-25 15:41:43 +02:00
|
|
|
#else /* _WIN32 */
|
2012-09-27 10:05:54 +02:00
|
|
|
::Sleep(timeout * 1000);
|
2012-09-25 15:41:43 +02:00
|
|
|
#endif /* _WIN32 */
|
|
|
|
}
|
|
|
|
|
2013-01-30 09:08:48 +01:00
|
|
|
/**
|
2013-05-03 11:26:18 +02:00
|
|
|
* Generates a new unique ID.
|
2013-01-30 09:08:48 +01:00
|
|
|
*
|
2013-05-03 11:26:18 +02:00
|
|
|
* @returns The new unique ID.
|
2013-01-30 09:08:48 +01:00
|
|
|
*/
|
2018-01-04 04:25:35 +01:00
|
|
|
String Utility::NewUniqueID()
|
2013-01-30 09:08:48 +01:00
|
|
|
{
|
2017-12-14 18:48:58 +01:00
|
|
|
return boost::lexical_cast<std::string>(boost::uuids::random_generator()());
|
2013-01-30 09:08:48 +01:00
|
|
|
}
|
|
|
|
|
2013-02-02 00:28:00 +01:00
|
|
|
#ifdef _WIN32
|
2015-08-19 07:54:06 +02:00
|
|
|
static bool GlobHelper(const String& pathSpec, int type, std::vector<String>& files, std::vector<String>& dirs)
|
|
|
|
{
|
2013-02-02 00:28:00 +01:00
|
|
|
HANDLE handle;
|
|
|
|
WIN32_FIND_DATA wfd;
|
|
|
|
|
|
|
|
handle = FindFirstFile(pathSpec.CStr(), &wfd);
|
|
|
|
|
|
|
|
if (handle == INVALID_HANDLE_VALUE) {
|
|
|
|
DWORD errorCode = GetLastError();
|
|
|
|
|
|
|
|
if (errorCode == ERROR_FILE_NOT_FOUND)
|
|
|
|
return false;
|
|
|
|
|
2013-03-11 14:03:01 +01:00
|
|
|
BOOST_THROW_EXCEPTION(win32_error()
|
2015-08-19 07:54:06 +02:00
|
|
|
<< boost::errinfo_api_function("FindFirstFile")
|
2013-03-18 22:40:40 +01:00
|
|
|
<< errinfo_win32_error(errorCode)
|
2015-08-19 07:54:06 +02:00
|
|
|
<< boost::errinfo_file_name(pathSpec));
|
2013-02-02 00:28:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
2014-04-05 23:02:48 +02:00
|
|
|
if (strcmp(wfd.cFileName, ".") == 0 || strcmp(wfd.cFileName, "..") == 0)
|
|
|
|
continue;
|
|
|
|
|
2015-08-19 07:54:06 +02:00
|
|
|
String path = Utility::DirName(pathSpec) + "/" + wfd.cFileName;
|
2013-11-22 09:03:52 +01:00
|
|
|
|
2014-05-22 13:11:59 +02:00
|
|
|
if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (type & GlobDirectory))
|
2014-05-22 21:32:08 +02:00
|
|
|
dirs.push_back(path);
|
|
|
|
else if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (type & GlobFile))
|
2014-05-22 13:11:59 +02:00
|
|
|
files.push_back(path);
|
2013-02-02 00:28:00 +01:00
|
|
|
} while (FindNextFile(handle, &wfd));
|
|
|
|
|
2013-03-11 14:03:01 +01:00
|
|
|
if (!FindClose(handle)) {
|
|
|
|
BOOST_THROW_EXCEPTION(win32_error()
|
2015-08-19 07:54:06 +02:00
|
|
|
<< boost::errinfo_api_function("FindClose")
|
|
|
|
<< errinfo_win32_error(GetLastError()));
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
|
2017-02-09 09:08:48 +01:00
|
|
|
#ifndef _WIN32
|
|
|
|
static int GlobErrorHandler(const char *epath, int eerrno)
|
|
|
|
{
|
|
|
|
if (eerrno == ENOTDIR)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return eerrno;
|
|
|
|
}
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
|
2015-08-19 07:54:06 +02:00
|
|
|
/**
|
|
|
|
* Calls the specified callback for each file matching the path specification.
|
|
|
|
*
|
|
|
|
* @param pathSpec The path specification.
|
|
|
|
* @param callback The callback which is invoked for each matching file.
|
|
|
|
* @param type The file type (a combination of GlobFile and GlobDirectory)
|
|
|
|
*/
|
2017-11-21 11:52:55 +01:00
|
|
|
bool Utility::Glob(const String& pathSpec, const std::function<void (const String&)>& callback, int type)
|
2015-08-19 07:54:06 +02:00
|
|
|
{
|
|
|
|
std::vector<String> files, dirs;
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
2018-01-04 18:24:45 +01:00
|
|
|
std::vector<String> tokens = pathSpec.Split("\\/");
|
2015-08-19 07:54:06 +02:00
|
|
|
|
|
|
|
String part1;
|
|
|
|
|
|
|
|
for (std::vector<String>::size_type i = 0; i < tokens.size() - 1; i++) {
|
|
|
|
const String& token = tokens[i];
|
|
|
|
|
|
|
|
if (!part1.IsEmpty())
|
|
|
|
part1 += "/";
|
|
|
|
|
|
|
|
part1 += token;
|
|
|
|
|
|
|
|
if (token.FindFirstOf("?*") != String::NPos) {
|
|
|
|
String part2;
|
|
|
|
|
|
|
|
for (std::vector<String>::size_type k = i + 1; k < tokens.size(); k++) {
|
|
|
|
if (!part2.IsEmpty())
|
|
|
|
part2 += "/";
|
|
|
|
|
|
|
|
part2 += tokens[k];
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<String> files2, dirs2;
|
|
|
|
|
|
|
|
if (!GlobHelper(part1, GlobDirectory, files2, dirs2))
|
|
|
|
return false;
|
|
|
|
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const String& dir : dirs2) {
|
2015-08-19 07:54:06 +02:00
|
|
|
if (!Utility::Glob(dir + "/" + part2, callback, type))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2013-03-11 14:03:01 +01:00
|
|
|
}
|
2015-08-19 07:54:06 +02:00
|
|
|
|
|
|
|
if (!GlobHelper(part1 + "/" + tokens[tokens.size() - 1], type, files, dirs))
|
|
|
|
return false;
|
2013-02-02 00:28:00 +01:00
|
|
|
#else /* _WIN32 */
|
|
|
|
glob_t gr;
|
|
|
|
|
2017-02-09 09:08:48 +01:00
|
|
|
int rc = glob(pathSpec.CStr(), GLOB_NOSORT, GlobErrorHandler, &gr);
|
2013-02-02 00:28:00 +01:00
|
|
|
|
2017-12-14 17:04:35 +01:00
|
|
|
if (rc) {
|
2013-02-02 00:28:00 +01:00
|
|
|
if (rc == GLOB_NOMATCH)
|
|
|
|
return false;
|
|
|
|
|
2013-03-11 13:45:08 +01:00
|
|
|
BOOST_THROW_EXCEPTION(posix_error()
|
2017-12-19 15:50:05 +01:00
|
|
|
<< boost::errinfo_api_function("glob")
|
|
|
|
<< boost::errinfo_errno(errno)
|
|
|
|
<< boost::errinfo_file_name(pathSpec));
|
2013-02-02 00:28:00 +01:00
|
|
|
}
|
|
|
|
|
2013-02-03 01:30:19 +01:00
|
|
|
if (gr.gl_pathc == 0) {
|
|
|
|
globfree(&gr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-02-02 00:28:00 +01:00
|
|
|
size_t left;
|
|
|
|
char **gp;
|
|
|
|
for (gp = gr.gl_pathv, left = gr.gl_pathc; left > 0; gp++, left--) {
|
2013-11-22 09:03:52 +01:00
|
|
|
struct stat statbuf;
|
|
|
|
|
2013-11-22 10:41:57 +01:00
|
|
|
if (stat(*gp, &statbuf) < 0)
|
2014-05-03 20:02:22 +02:00
|
|
|
continue;
|
2013-11-22 09:03:52 +01:00
|
|
|
|
|
|
|
if (!S_ISDIR(statbuf.st_mode) && !S_ISREG(statbuf.st_mode))
|
|
|
|
continue;
|
|
|
|
|
2014-05-22 13:11:59 +02:00
|
|
|
if (S_ISDIR(statbuf.st_mode) && (type & GlobDirectory))
|
2018-01-04 09:14:55 +01:00
|
|
|
dirs.emplace_back(*gp);
|
2014-05-22 13:11:59 +02:00
|
|
|
else if (!S_ISDIR(statbuf.st_mode) && (type & GlobFile))
|
2018-01-04 09:14:55 +01:00
|
|
|
files.emplace_back(*gp);
|
2014-05-22 13:11:59 +02:00
|
|
|
}
|
2013-11-22 09:03:52 +01:00
|
|
|
|
2014-05-22 13:11:59 +02:00
|
|
|
globfree(&gr);
|
|
|
|
#endif /* _WIN32 */
|
2013-11-22 09:03:52 +01:00
|
|
|
|
2014-05-22 13:11:59 +02:00
|
|
|
std::sort(files.begin(), files.end());
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const String& cpath : files) {
|
2014-05-22 13:11:59 +02:00
|
|
|
callback(cpath);
|
2013-02-02 00:28:00 +01:00
|
|
|
}
|
|
|
|
|
2014-05-22 13:11:59 +02:00
|
|
|
std::sort(dirs.begin(), dirs.end());
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const String& cpath : dirs) {
|
2014-05-22 13:11:59 +02:00
|
|
|
callback(cpath);
|
|
|
|
}
|
2013-02-02 00:28:00 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2013-02-06 10:53:28 +01:00
|
|
|
|
2013-11-29 10:39:48 +01:00
|
|
|
/**
|
|
|
|
* Calls the specified callback for each file in the specified directory
|
|
|
|
* or any of its child directories if the file name matches the specified
|
|
|
|
* pattern.
|
|
|
|
*
|
|
|
|
* @param path The path.
|
|
|
|
* @param pattern The pattern.
|
|
|
|
* @param callback The callback which is invoked for each matching file.
|
|
|
|
* @param type The file type (a combination of GlobFile and GlobDirectory)
|
|
|
|
*/
|
2017-11-21 11:52:55 +01:00
|
|
|
bool Utility::GlobRecursive(const String& path, const String& pattern, const std::function<void (const String&)>& callback, int type)
|
2013-11-29 10:39:48 +01:00
|
|
|
{
|
2014-05-22 13:11:59 +02:00
|
|
|
std::vector<String> files, dirs, alldirs;
|
|
|
|
|
2013-11-29 10:39:48 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
HANDLE handle;
|
|
|
|
WIN32_FIND_DATA wfd;
|
|
|
|
|
|
|
|
String pathSpec = path + "/*";
|
|
|
|
|
|
|
|
handle = FindFirstFile(pathSpec.CStr(), &wfd);
|
|
|
|
|
|
|
|
if (handle == INVALID_HANDLE_VALUE) {
|
|
|
|
DWORD errorCode = GetLastError();
|
|
|
|
|
|
|
|
if (errorCode == ERROR_FILE_NOT_FOUND)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
BOOST_THROW_EXCEPTION(win32_error()
|
2017-12-19 15:50:05 +01:00
|
|
|
<< boost::errinfo_api_function("FindFirstFile")
|
2013-11-29 10:39:48 +01:00
|
|
|
<< errinfo_win32_error(errorCode)
|
2017-12-19 15:50:05 +01:00
|
|
|
<< boost::errinfo_file_name(pathSpec));
|
2013-11-29 10:39:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
2014-04-05 23:02:48 +02:00
|
|
|
if (strcmp(wfd.cFileName, ".") == 0 || strcmp(wfd.cFileName, "..") == 0)
|
|
|
|
continue;
|
|
|
|
|
2013-11-29 10:39:48 +01:00
|
|
|
String cpath = path + "/" + wfd.cFileName;
|
|
|
|
|
|
|
|
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
2014-05-22 13:11:59 +02:00
|
|
|
alldirs.push_back(cpath);
|
2013-11-29 10:39:48 +01:00
|
|
|
|
|
|
|
if (!Utility::Match(pattern, wfd.cFileName))
|
|
|
|
continue;
|
|
|
|
|
2014-05-22 13:11:59 +02:00
|
|
|
if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (type & GlobFile))
|
|
|
|
files.push_back(cpath);
|
|
|
|
|
|
|
|
if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (type & GlobDirectory))
|
|
|
|
dirs.push_back(cpath);
|
2013-11-29 10:39:48 +01:00
|
|
|
} while (FindNextFile(handle, &wfd));
|
|
|
|
|
|
|
|
if (!FindClose(handle)) {
|
|
|
|
BOOST_THROW_EXCEPTION(win32_error()
|
2017-12-19 15:50:05 +01:00
|
|
|
<< boost::errinfo_api_function("FindClose")
|
|
|
|
<< errinfo_win32_error(GetLastError()));
|
2013-11-29 10:39:48 +01:00
|
|
|
}
|
|
|
|
#else /* _WIN32 */
|
|
|
|
DIR *dirp;
|
|
|
|
|
|
|
|
dirp = opendir(path.CStr());
|
|
|
|
|
2017-12-14 15:37:20 +01:00
|
|
|
if (!dirp)
|
2013-11-29 10:39:48 +01:00
|
|
|
BOOST_THROW_EXCEPTION(posix_error()
|
2017-12-19 15:50:05 +01:00
|
|
|
<< boost::errinfo_api_function("opendir")
|
|
|
|
<< boost::errinfo_errno(errno)
|
|
|
|
<< boost::errinfo_file_name(path));
|
2013-11-29 10:39:48 +01:00
|
|
|
|
|
|
|
while (dirp) {
|
2014-08-04 08:46:14 +02:00
|
|
|
dirent *pent;
|
2013-11-29 10:39:48 +01:00
|
|
|
|
2014-08-04 08:46:14 +02:00
|
|
|
errno = 0;
|
|
|
|
pent = readdir(dirp);
|
|
|
|
if (!pent && errno != 0) {
|
2014-02-05 13:53:56 +01:00
|
|
|
closedir(dirp);
|
|
|
|
|
2013-11-29 10:39:48 +01:00
|
|
|
BOOST_THROW_EXCEPTION(posix_error()
|
2017-12-19 15:50:05 +01:00
|
|
|
<< boost::errinfo_api_function("readdir")
|
|
|
|
<< boost::errinfo_errno(errno)
|
|
|
|
<< boost::errinfo_file_name(path));
|
2014-02-05 13:53:56 +01:00
|
|
|
}
|
2013-11-29 10:39:48 +01:00
|
|
|
|
|
|
|
if (!pent)
|
|
|
|
break;
|
|
|
|
|
2014-08-04 08:46:14 +02:00
|
|
|
if (strcmp(pent->d_name, ".") == 0 || strcmp(pent->d_name, "..") == 0)
|
2013-11-29 10:39:48 +01:00
|
|
|
continue;
|
|
|
|
|
2014-08-04 08:46:14 +02:00
|
|
|
String cpath = path + "/" + pent->d_name;
|
2013-11-29 10:39:48 +01:00
|
|
|
|
|
|
|
struct stat statbuf;
|
|
|
|
|
2016-03-22 08:21:44 +01:00
|
|
|
if (stat(cpath.CStr(), &statbuf) < 0)
|
2014-05-22 13:11:59 +02:00
|
|
|
continue;
|
2013-11-29 10:39:48 +01:00
|
|
|
|
|
|
|
if (S_ISDIR(statbuf.st_mode))
|
2014-05-22 13:11:59 +02:00
|
|
|
alldirs.push_back(cpath);
|
2013-11-29 10:39:48 +01:00
|
|
|
|
2014-08-04 08:46:14 +02:00
|
|
|
if (!Utility::Match(pattern, pent->d_name))
|
2014-05-22 13:11:59 +02:00
|
|
|
continue;
|
2014-02-05 13:53:56 +01:00
|
|
|
|
2014-05-22 13:11:59 +02:00
|
|
|
if (S_ISDIR(statbuf.st_mode) && (type & GlobDirectory))
|
|
|
|
dirs.push_back(cpath);
|
2013-11-29 10:39:48 +01:00
|
|
|
|
2014-05-22 13:11:59 +02:00
|
|
|
if (!S_ISDIR(statbuf.st_mode) && (type & GlobFile))
|
|
|
|
files.push_back(cpath);
|
|
|
|
}
|
2013-11-29 10:39:48 +01:00
|
|
|
|
2014-05-22 13:11:59 +02:00
|
|
|
closedir(dirp);
|
2013-11-29 10:39:48 +01:00
|
|
|
|
2014-05-22 13:11:59 +02:00
|
|
|
#endif /* _WIN32 */
|
2013-11-29 10:39:48 +01:00
|
|
|
|
2014-05-22 13:11:59 +02:00
|
|
|
std::sort(files.begin(), files.end());
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const String& cpath : files) {
|
2014-05-22 13:11:59 +02:00
|
|
|
callback(cpath);
|
|
|
|
}
|
2013-11-29 10:39:48 +01:00
|
|
|
|
2014-05-22 13:11:59 +02:00
|
|
|
std::sort(dirs.begin(), dirs.end());
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const String& cpath : dirs) {
|
2013-11-29 10:39:48 +01:00
|
|
|
callback(cpath);
|
|
|
|
}
|
2014-02-05 13:53:56 +01:00
|
|
|
|
2014-05-22 13:11:59 +02:00
|
|
|
std::sort(alldirs.begin(), alldirs.end());
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const String& cpath : alldirs) {
|
2014-05-22 13:11:59 +02:00
|
|
|
GlobRecursive(cpath, pattern, callback, type);
|
|
|
|
}
|
2013-12-02 11:11:40 +01:00
|
|
|
|
|
|
|
return true;
|
2013-11-29 10:39:48 +01:00
|
|
|
}
|
|
|
|
|
2014-06-12 22:51:48 +02:00
|
|
|
|
2016-02-24 11:00:44 +01:00
|
|
|
void Utility::MkDir(const String& path, int mode)
|
2014-06-13 09:10:35 +02:00
|
|
|
{
|
2016-03-03 15:54:35 +01:00
|
|
|
|
2014-06-13 09:10:35 +02:00
|
|
|
#ifndef _WIN32
|
2016-02-24 11:00:44 +01:00
|
|
|
if (mkdir(path.CStr(), mode) < 0 && errno != EEXIST) {
|
2014-06-13 09:10:35 +02:00
|
|
|
#else /*_ WIN32 */
|
|
|
|
if (mkdir(path.CStr()) < 0 && errno != EEXIST) {
|
|
|
|
#endif /* _WIN32 */
|
2016-03-03 15:54:35 +01:00
|
|
|
|
2015-07-21 15:56:57 +02:00
|
|
|
BOOST_THROW_EXCEPTION(posix_error()
|
2017-12-19 15:50:05 +01:00
|
|
|
<< boost::errinfo_api_function("mkdir")
|
|
|
|
<< boost::errinfo_errno(errno)
|
|
|
|
<< boost::errinfo_file_name(path));
|
2014-06-12 22:51:48 +02:00
|
|
|
}
|
2014-06-13 09:10:35 +02:00
|
|
|
}
|
2014-06-12 22:51:48 +02:00
|
|
|
|
2015-10-13 09:15:06 +02:00
|
|
|
void Utility::MkDirP(const String& path, int mode)
|
2014-06-13 09:10:35 +02:00
|
|
|
{
|
|
|
|
size_t pos = 0;
|
2014-06-12 22:51:48 +02:00
|
|
|
|
2015-08-24 15:11:49 +02:00
|
|
|
while (pos != String::NPos) {
|
2016-03-03 15:54:35 +01:00
|
|
|
#ifndef _WIN32
|
2014-06-13 09:10:35 +02:00
|
|
|
pos = path.Find("/", pos + 1);
|
2016-03-03 15:54:35 +01:00
|
|
|
#else /*_ WIN32 */
|
2016-03-07 09:22:07 +01:00
|
|
|
pos = path.FindFirstOf("/\\", pos + 1);
|
2016-03-03 15:54:35 +01:00
|
|
|
#endif /* _WIN32 */
|
2016-03-29 09:43:40 +02:00
|
|
|
|
|
|
|
String spath = path.SubStr(0, pos + 1);
|
|
|
|
struct stat statbuf;
|
|
|
|
if (stat(spath.CStr(), &statbuf) < 0 && errno == ENOENT)
|
|
|
|
MkDir(path.SubStr(0, pos), mode);
|
2014-06-13 09:10:35 +02:00
|
|
|
}
|
2014-06-12 22:51:48 +02:00
|
|
|
}
|
|
|
|
|
2019-04-10 14:16:39 +02:00
|
|
|
void Utility::Remove(const String& path)
|
|
|
|
{
|
|
|
|
namespace fs = boost::filesystem;
|
|
|
|
|
|
|
|
(void)fs::remove(fs::path(path.Begin(), path.End()));
|
|
|
|
}
|
|
|
|
|
2015-07-21 16:10:13 +02:00
|
|
|
void Utility::RemoveDirRecursive(const String& path)
|
|
|
|
{
|
2019-04-10 11:21:05 +02:00
|
|
|
namespace fs = boost::filesystem;
|
2015-07-21 16:10:13 +02:00
|
|
|
|
2019-04-10 12:31:53 +02:00
|
|
|
(void)fs::remove_all(fs::path(path.Begin(), path.End()));
|
2015-07-21 16:10:13 +02:00
|
|
|
}
|
|
|
|
|
2017-10-16 15:32:57 +02:00
|
|
|
/*
|
|
|
|
* Copies a source file to a target location.
|
|
|
|
* Caller must ensure that the target's base directory exists and is writable.
|
|
|
|
*/
|
2014-10-29 13:53:59 +01:00
|
|
|
void Utility::CopyFile(const String& source, const String& target)
|
2014-10-22 19:25:29 +02:00
|
|
|
{
|
2019-04-10 11:21:05 +02:00
|
|
|
namespace fs = boost::filesystem;
|
2014-10-22 19:25:29 +02:00
|
|
|
|
2020-08-20 18:25:48 +02:00
|
|
|
#if BOOST_VERSION >= 107400
|
|
|
|
fs::copy_file(fs::path(source.Begin(), source.End()), fs::path(target.Begin(), target.End()), fs::copy_options::overwrite_existing);
|
|
|
|
#else /* BOOST_VERSION */
|
2019-04-10 12:31:53 +02:00
|
|
|
fs::copy_file(fs::path(source.Begin(), source.End()), fs::path(target.Begin(), target.End()), fs::copy_option::overwrite_if_exists);
|
2020-08-20 18:25:48 +02:00
|
|
|
#endif /* BOOST_VERSION */
|
2014-10-22 19:25:29 +02:00
|
|
|
}
|
2014-06-12 22:51:48 +02:00
|
|
|
|
2019-04-10 13:44:13 +02:00
|
|
|
/*
|
|
|
|
* Renames a source file to a target location.
|
|
|
|
* Caller must ensure that the target's base directory exists and is writable.
|
|
|
|
*/
|
|
|
|
void Utility::RenameFile(const String& source, const String& target)
|
|
|
|
{
|
|
|
|
namespace fs = boost::filesystem;
|
|
|
|
|
2021-03-22 16:16:55 +01:00
|
|
|
fs::path sourcePath(source.Begin(), source.End()), targetPath(target.Begin(), target.End());
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
fs::rename(sourcePath, targetPath);
|
|
|
|
#else /* _WIN32 */
|
|
|
|
/*
|
|
|
|
* Renaming files can be tricky on Windows, especially if your application is built around POSIX filesystem
|
|
|
|
* semantics. For example, the quite common pattern of replacing a file by writing a new version to a temporary
|
|
|
|
* location and then moving it to the final location can fail if the destination file already exists and any
|
|
|
|
* process has an open file handle to it.
|
|
|
|
*
|
|
|
|
* We try to handle this situation as best as we can by retrying the rename operation a few times hoping the other
|
|
|
|
* process closes its file handle in the meantime. This is similar to what for example Go does internally in some
|
|
|
|
* situations (https://golang.org/pkg/cmd/go/internal/robustio/#Rename):
|
|
|
|
*
|
|
|
|
* robustio.Rename is like os.Rename, but on Windows retries errors that may occur if the file is concurrently
|
|
|
|
* read or overwritten. (See https://golang.org/issue/31247 and https://golang.org/issue/32188)
|
|
|
|
*/
|
|
|
|
|
|
|
|
double sleep = 0.1;
|
|
|
|
int last_error = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
for (int retries = 0, remaining = 15;; retries++, remaining--) {
|
|
|
|
try {
|
|
|
|
fs::rename(sourcePath, targetPath);
|
|
|
|
|
|
|
|
if (retries > 0) {
|
|
|
|
Log(LogWarning, "Utility") << "Renaming '" << source << "' to '" << target
|
|
|
|
<< "' succeeded after " << retries << " retries";
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
} catch (const fs::filesystem_error& ex) {
|
|
|
|
int error = ex.code().value();
|
|
|
|
bool ephemeral = error == ERROR_ACCESS_DENIED ||
|
|
|
|
error == ERROR_FILE_NOT_FOUND ||
|
|
|
|
error == ERROR_SHARING_VIOLATION;
|
|
|
|
|
|
|
|
if (remaining <= 0 || !ephemeral) {
|
|
|
|
throw; // giving up
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error != last_error) {
|
|
|
|
Log(LogWarning, "Utility") << "Renaming '" << source << "' to '" << target << "' failed: "
|
|
|
|
<< ex.code().message() << " (trying up to " << remaining << " more times)";
|
|
|
|
last_error = error;
|
|
|
|
}
|
|
|
|
|
|
|
|
Utility::Sleep(sleep);
|
|
|
|
sleep *= 1.3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* _WIN32 */
|
2019-04-10 13:44:13 +02:00
|
|
|
}
|
|
|
|
|
2014-10-30 19:52:22 +01:00
|
|
|
/*
|
|
|
|
* Set file permissions
|
|
|
|
*/
|
|
|
|
bool Utility::SetFileOwnership(const String& file, const String& user, const String& group)
|
|
|
|
{
|
|
|
|
#ifndef _WIN32
|
|
|
|
errno = 0;
|
|
|
|
struct passwd *pw = getpwnam(user.CStr());
|
|
|
|
|
|
|
|
if (!pw) {
|
|
|
|
if (errno == 0) {
|
|
|
|
Log(LogCritical, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "Invalid user specified: " << user;
|
2014-10-30 19:52:22 +01:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
Log(LogCritical, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "getpwnam() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
2014-10-30 19:52:22 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
struct group *gr = getgrnam(group.CStr());
|
|
|
|
|
|
|
|
if (!gr) {
|
|
|
|
if (errno == 0) {
|
|
|
|
Log(LogCritical, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "Invalid group specified: " << group;
|
2014-10-30 19:52:22 +01:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
Log(LogCritical, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "getgrnam() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
2014-10-30 19:52:22 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chown(file.CStr(), pw->pw_uid, gr->gr_gid) < 0) {
|
|
|
|
Log(LogCritical, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "chown() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
|
2014-10-30 19:52:22 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-02-14 10:59:01 +01:00
|
|
|
#ifndef _WIN32
|
2015-11-09 20:39:26 +01:00
|
|
|
void Utility::SetNonBlocking(int fd, bool nb)
|
2013-02-13 13:03:21 +01:00
|
|
|
{
|
2013-03-11 13:45:08 +01:00
|
|
|
int flags = fcntl(fd, F_GETFL, 0);
|
2013-02-13 13:03:21 +01:00
|
|
|
|
2013-03-11 13:45:08 +01:00
|
|
|
if (flags < 0) {
|
|
|
|
BOOST_THROW_EXCEPTION(posix_error()
|
2017-12-19 15:50:05 +01:00
|
|
|
<< boost::errinfo_api_function("fcntl")
|
|
|
|
<< boost::errinfo_errno(errno));
|
2013-03-11 13:45:08 +01:00
|
|
|
}
|
|
|
|
|
2015-11-09 20:39:26 +01:00
|
|
|
if (nb)
|
|
|
|
flags |= O_NONBLOCK;
|
|
|
|
else
|
|
|
|
flags &= ~O_NONBLOCK;
|
|
|
|
|
|
|
|
if (fcntl(fd, F_SETFL, flags) < 0) {
|
2013-03-11 13:45:08 +01:00
|
|
|
BOOST_THROW_EXCEPTION(posix_error()
|
2017-12-19 15:50:05 +01:00
|
|
|
<< boost::errinfo_api_function("fcntl")
|
|
|
|
<< boost::errinfo_errno(errno));
|
2013-03-11 13:45:08 +01:00
|
|
|
}
|
2013-02-13 13:03:21 +01:00
|
|
|
}
|
|
|
|
|
2015-11-09 20:39:26 +01:00
|
|
|
void Utility::SetCloExec(int fd, bool cloexec)
|
2013-02-13 13:03:21 +01:00
|
|
|
{
|
2013-03-11 13:45:08 +01:00
|
|
|
int flags = fcntl(fd, F_GETFD, 0);
|
2013-02-13 13:03:21 +01:00
|
|
|
|
2013-03-11 13:45:08 +01:00
|
|
|
if (flags < 0) {
|
|
|
|
BOOST_THROW_EXCEPTION(posix_error()
|
2017-12-19 15:50:05 +01:00
|
|
|
<< boost::errinfo_api_function("fcntl")
|
|
|
|
<< boost::errinfo_errno(errno));
|
2013-03-11 13:45:08 +01:00
|
|
|
}
|
|
|
|
|
2015-11-09 20:39:26 +01:00
|
|
|
if (cloexec)
|
|
|
|
flags |= FD_CLOEXEC;
|
|
|
|
else
|
|
|
|
flags &= ~FD_CLOEXEC;
|
|
|
|
|
|
|
|
if (fcntl(fd, F_SETFD, flags) < 0) {
|
2013-03-11 13:45:08 +01:00
|
|
|
BOOST_THROW_EXCEPTION(posix_error()
|
2017-12-19 15:50:05 +01:00
|
|
|
<< boost::errinfo_api_function("fcntl")
|
|
|
|
<< boost::errinfo_errno(errno));
|
2013-03-11 13:45:08 +01:00
|
|
|
}
|
2013-02-13 13:03:21 +01:00
|
|
|
}
|
2021-01-12 17:32:28 +01:00
|
|
|
|
|
|
|
void Utility::CloseAllFDs(const std::vector<int>& except, std::function<void(int)> onClose)
|
|
|
|
{
|
|
|
|
#if defined(__linux__) || defined(__APPLE__)
|
|
|
|
namespace fs = boost::filesystem;
|
|
|
|
|
|
|
|
std::set<int> fds;
|
|
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
const char *dir = "/proc/self/fd";
|
|
|
|
#endif /* __linux__ */
|
|
|
|
#ifdef __APPLE__
|
|
|
|
const char *dir = "/dev/fd";
|
|
|
|
#endif /* __APPLE__ */
|
|
|
|
|
|
|
|
for (fs::directory_iterator current {fs::path(dir)}, end; current != end; ++current) {
|
|
|
|
auto entry (current->path().filename());
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
try {
|
|
|
|
fd = boost::lexical_cast<int>(entry.c_str());
|
|
|
|
} catch (...) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
fds.emplace(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto fd : except) {
|
|
|
|
fds.erase(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto fd : fds) {
|
|
|
|
if (close(fd) >= 0 && onClose) {
|
|
|
|
onClose(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else /* __linux__ || __APPLE__ */
|
|
|
|
rlimit rl;
|
|
|
|
|
|
|
|
if (getrlimit(RLIMIT_NOFILE, &rl) >= 0) {
|
|
|
|
rlim_t maxfds = rl.rlim_max;
|
|
|
|
|
|
|
|
if (maxfds == RLIM_INFINITY) {
|
|
|
|
maxfds = 65536;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int fd = 0; fd < maxfds; ++fd) {
|
|
|
|
if (std::find(except.begin(), except.end(), fd) == except.end() && close(fd) >= 0 && onClose) {
|
|
|
|
onClose(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* __linux__ || __APPLE__ */
|
|
|
|
}
|
2013-02-14 10:59:01 +01:00
|
|
|
#endif /* _WIN32 */
|
2013-02-13 13:03:21 +01:00
|
|
|
|
2015-11-09 20:39:26 +01:00
|
|
|
void Utility::SetNonBlockingSocket(SOCKET s, bool nb)
|
2013-02-13 13:03:21 +01:00
|
|
|
{
|
|
|
|
#ifndef _WIN32
|
2015-11-09 20:39:26 +01:00
|
|
|
SetNonBlocking(s, nb);
|
2013-02-13 13:03:21 +01:00
|
|
|
#else /* _WIN32 */
|
2015-11-09 20:39:26 +01:00
|
|
|
unsigned long lflag = nb;
|
|
|
|
ioctlsocket(s, FIONBIO, &lflag);
|
2013-02-13 13:03:21 +01:00
|
|
|
#endif /* _WIN32 */
|
|
|
|
}
|
2013-02-18 14:40:24 +01:00
|
|
|
|
2018-01-04 04:25:35 +01:00
|
|
|
void Utility::QueueAsyncCallback(const std::function<void ()>& callback, SchedulerPolicy policy)
|
2013-02-18 14:40:24 +01:00
|
|
|
{
|
2014-09-11 11:45:21 +02:00
|
|
|
Application::GetTP().Post(callback, policy);
|
2013-02-18 14:40:24 +01:00
|
|
|
}
|
2013-02-28 10:27:33 +01:00
|
|
|
|
2014-04-28 09:16:27 +02:00
|
|
|
String Utility::NaturalJoin(const std::vector<String>& tokens)
|
|
|
|
{
|
|
|
|
String result;
|
|
|
|
|
2014-05-11 06:00:34 +02:00
|
|
|
for (std::vector<String>::size_type i = 0; i < tokens.size(); i++) {
|
2014-04-28 09:16:27 +02:00
|
|
|
result += tokens[i];
|
|
|
|
|
|
|
|
if (tokens.size() > i + 1) {
|
|
|
|
if (i < tokens.size() - 2)
|
|
|
|
result += ", ";
|
|
|
|
else if (i == tokens.size() - 2)
|
|
|
|
result += " and ";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-08-16 22:16:37 +02:00
|
|
|
String Utility::Join(const Array::Ptr& tokens, char separator, bool escapeSeparator)
|
2014-11-02 07:22:00 +01:00
|
|
|
{
|
|
|
|
String result;
|
|
|
|
bool first = true;
|
|
|
|
|
|
|
|
ObjectLock olock(tokens);
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const Value& vtoken : tokens) {
|
2014-11-02 07:22:00 +01:00
|
|
|
String token = Convert::ToString(vtoken);
|
2016-08-16 22:16:37 +02:00
|
|
|
|
|
|
|
if (escapeSeparator) {
|
|
|
|
boost::algorithm::replace_all(token, "\\", "\\\\");
|
|
|
|
|
|
|
|
char sep_before[2], sep_after[3];
|
|
|
|
sep_before[0] = separator;
|
|
|
|
sep_before[1] = '\0';
|
|
|
|
sep_after[0] = '\\';
|
|
|
|
sep_after[1] = separator;
|
|
|
|
sep_after[2] = '\0';
|
|
|
|
boost::algorithm::replace_all(token, sep_before, sep_after);
|
|
|
|
}
|
2014-11-02 07:22:00 +01:00
|
|
|
|
|
|
|
if (first)
|
|
|
|
first = false;
|
|
|
|
else
|
|
|
|
result += String(1, separator);
|
|
|
|
|
|
|
|
result += token;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-09-02 13:02:22 +02:00
|
|
|
String Utility::FormatDuration(double duration)
|
2014-04-28 09:16:27 +02:00
|
|
|
{
|
|
|
|
std::vector<String> tokens;
|
|
|
|
String result;
|
|
|
|
|
|
|
|
if (duration >= 86400) {
|
|
|
|
int days = duration / 86400;
|
2017-11-30 08:19:58 +01:00
|
|
|
tokens.emplace_back(Convert::ToString(days) + (days != 1 ? " days" : " day"));
|
2014-09-02 13:02:22 +02:00
|
|
|
duration = static_cast<int>(duration) % 86400;
|
2014-04-28 09:16:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (duration >= 3600) {
|
|
|
|
int hours = duration / 3600;
|
2017-11-30 08:19:58 +01:00
|
|
|
tokens.emplace_back(Convert::ToString(hours) + (hours != 1 ? " hours" : " hour"));
|
2014-09-02 13:02:22 +02:00
|
|
|
duration = static_cast<int>(duration) % 3600;
|
2014-04-28 09:16:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (duration >= 60) {
|
|
|
|
int minutes = duration / 60;
|
2017-11-30 08:19:58 +01:00
|
|
|
tokens.emplace_back(Convert::ToString(minutes) + (minutes != 1 ? " minutes" : " minute"));
|
2014-09-02 13:02:22 +02:00
|
|
|
duration = static_cast<int>(duration) % 60;
|
2014-04-28 09:16:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (duration >= 1) {
|
|
|
|
int seconds = duration;
|
2017-11-30 08:19:58 +01:00
|
|
|
tokens.emplace_back(Convert::ToString(seconds) + (seconds != 1 ? " seconds" : " second"));
|
2014-04-28 09:16:27 +02:00
|
|
|
}
|
|
|
|
|
2014-04-29 11:01:28 +02:00
|
|
|
if (tokens.size() == 0) {
|
2014-09-02 13:02:22 +02:00
|
|
|
int milliseconds = std::floor(duration * 1000);
|
2014-04-29 11:01:28 +02:00
|
|
|
if (milliseconds >= 1)
|
2017-11-30 08:19:58 +01:00
|
|
|
tokens.emplace_back(Convert::ToString(milliseconds) + (milliseconds != 1 ? " milliseconds" : " millisecond"));
|
2014-04-29 11:01:28 +02:00
|
|
|
else
|
2018-01-04 09:14:55 +01:00
|
|
|
tokens.emplace_back("less than 1 millisecond");
|
2014-04-29 11:01:28 +02:00
|
|
|
}
|
|
|
|
|
2014-04-28 09:16:27 +02:00
|
|
|
return NaturalJoin(tokens);
|
|
|
|
}
|
|
|
|
|
2024-08-21 12:31:44 +02:00
|
|
|
String Utility::FormatDateTime(const char* format, double ts)
|
2013-02-28 10:27:33 +01:00
|
|
|
{
|
2024-08-21 12:31:44 +02:00
|
|
|
// Sub-second precision is removed, strftime() has no format specifiers for that anyway.
|
|
|
|
auto tempts = boost::numeric_cast<time_t>(ts);
|
|
|
|
tm tmthen;
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
errno_t err = localtime_s(&tmthen, &tempts);
|
|
|
|
if (err) {
|
|
|
|
BOOST_THROW_EXCEPTION(posix_error()
|
|
|
|
<< boost::errinfo_api_function("localtime_s")
|
|
|
|
<< boost::errinfo_errno(err));
|
|
|
|
}
|
|
|
|
#else /* _MSC_VER */
|
|
|
|
if (!localtime_r(&tempts, &tmthen)) {
|
|
|
|
BOOST_THROW_EXCEPTION(posix_error()
|
|
|
|
<< boost::errinfo_api_function("localtime_r")
|
|
|
|
<< boost::errinfo_errno(errno));
|
|
|
|
}
|
|
|
|
#endif /* _MSC_VER */
|
|
|
|
|
|
|
|
return FormatDateTime(format, &tmthen);
|
|
|
|
}
|
|
|
|
|
|
|
|
String Utility::FormatDateTime(const char* format, const tm* t) {
|
Utility::FormatDateTime(): handle errors from strftime()
So far, the return value of strftime() was simply ignored and the output buffer
passed to the icinga::String constructor. However, there are error conditions
where strftime() returns 0 to signal an error, like if the buffer was too small
for the output. In that case, there's no guarantee on the buffer contents and
reading it can result in undefined behavior. Unfortunately, returning 0 can
also indicate success and strftime() doesn't set errno, so there's no reliable
way to distinguish both situations. Thus, the implementation now returns the
empty string in both cases.
I attempted to use std::put_time() at first as that allows for better error
handling, however, there were problems with the implementation on Windows (see
inline comment), so I put that plan on hold at left strftime() there for the
time being.
2024-08-21 11:55:19 +02:00
|
|
|
/* Known limitations of the implementation: Only works if the result is at most 127 bytes, otherwise returns an
|
|
|
|
* empty string. An empty string is also returned in all other error cases as proper error handling for strftime()
|
|
|
|
* is impossible.
|
|
|
|
*
|
|
|
|
* From strftime(3):
|
|
|
|
*
|
|
|
|
* If the output string would exceed max bytes, errno is not set. This makes it impossible to distinguish this
|
|
|
|
* error case from cases where the format string legitimately produces a zero-length output string. POSIX.1-2001
|
|
|
|
* does not specify any errno settings for strftime().
|
|
|
|
*
|
|
|
|
* https://manpages.debian.org/bookworm/manpages-dev/strftime.3.en.html#BUGS
|
|
|
|
*
|
|
|
|
* There's also std::put_time() from C++ which works with an ostream and does not have a fixed size output buffer
|
|
|
|
* and should allow using the error handling of the ostream. However, there seem to be an unfortunate implementation
|
|
|
|
* of this on some Windows versions where passing an invalid format string results in std::bad_alloc and the process
|
|
|
|
* allocating more and more memory before throwing the exception. In case someone in the future wants to try
|
|
|
|
* std::put_time() again: better build packages for Windows and test them across all supported versions.
|
|
|
|
* Hypothesis: it's implemented using a fixed output buffer and retrying with a larger buffer on error, assuming
|
|
|
|
* the error was due to the buffer being too small.
|
|
|
|
*/
|
|
|
|
|
2024-08-21 12:08:04 +02:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
/* On Windows, the strftime() function family invokes an invalid parameter handler when the format string is
|
|
|
|
* invalid (see the "Remarks" section in their documentation). std::put_time() shows the same behavior as it
|
|
|
|
* uses _wcsftime_l() internally. The default invalid parameter handler may terminate the process, which can
|
|
|
|
* be a problem given that the format string can be specified by the user from the Icinga DSL.
|
|
|
|
*
|
|
|
|
* Thus, temporarily set a thread-local no-op handler to disable the default one allowing the program to
|
|
|
|
* continue. This then simply results in the function returning an error which then results in an exception as
|
|
|
|
* we ask the stream to throw one.
|
|
|
|
*
|
|
|
|
* See also:
|
|
|
|
* https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/strftime-wcsftime-strftime-l-wcsftime-l?view=msvc-170
|
|
|
|
* https://learn.microsoft.com/en-us/cpp/c-runtime-library/parameter-validation?view=msvc-170
|
|
|
|
* https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/set-invalid-parameter-handler-set-thread-local-invalid-parameter-handler?view=msvc-170
|
|
|
|
*/
|
|
|
|
|
|
|
|
auto oldHandler = _set_thread_local_invalid_parameter_handler(
|
|
|
|
[](const wchar_t*, const wchar_t*, const wchar_t*, unsigned int, uintptr_t) {
|
|
|
|
// Intentionally do nothing to continue executing.
|
|
|
|
});
|
|
|
|
|
|
|
|
Defer resetHandler([oldHandler]() {
|
|
|
|
_set_thread_local_invalid_parameter_handler(oldHandler);
|
|
|
|
});
|
|
|
|
#endif /* _MSC_VER */
|
|
|
|
|
Utility::FormatDateTime(): handle errors from strftime()
So far, the return value of strftime() was simply ignored and the output buffer
passed to the icinga::String constructor. However, there are error conditions
where strftime() returns 0 to signal an error, like if the buffer was too small
for the output. In that case, there's no guarantee on the buffer contents and
reading it can result in undefined behavior. Unfortunately, returning 0 can
also indicate success and strftime() doesn't set errno, so there's no reliable
way to distinguish both situations. Thus, the implementation now returns the
empty string in both cases.
I attempted to use std::put_time() at first as that allows for better error
handling, however, there were problems with the implementation on Windows (see
inline comment), so I put that plan on hold at left strftime() there for the
time being.
2024-08-21 11:55:19 +02:00
|
|
|
char buf[128];
|
2024-08-21 12:31:44 +02:00
|
|
|
size_t n = strftime(buf, sizeof(buf), format, t);
|
Utility::FormatDateTime(): handle errors from strftime()
So far, the return value of strftime() was simply ignored and the output buffer
passed to the icinga::String constructor. However, there are error conditions
where strftime() returns 0 to signal an error, like if the buffer was too small
for the output. In that case, there's no guarantee on the buffer contents and
reading it can result in undefined behavior. Unfortunately, returning 0 can
also indicate success and strftime() doesn't set errno, so there's no reliable
way to distinguish both situations. Thus, the implementation now returns the
empty string in both cases.
I attempted to use std::put_time() at first as that allows for better error
handling, however, there were problems with the implementation on Windows (see
inline comment), so I put that plan on hold at left strftime() there for the
time being.
2024-08-21 11:55:19 +02:00
|
|
|
// On error, n == 0 and an empty string is returned.
|
|
|
|
return std::string(buf, n);
|
2013-02-28 10:27:33 +01:00
|
|
|
}
|
2013-03-22 10:58:47 +01:00
|
|
|
|
2014-06-05 14:08:01 +02:00
|
|
|
String Utility::FormatErrorNumber(int code) {
|
|
|
|
std::ostringstream msgbuf;
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
2014-08-17 17:57:20 +02:00
|
|
|
char *message;
|
|
|
|
String result = "Unknown error.";
|
2014-06-05 14:08:01 +02:00
|
|
|
|
2014-08-17 17:57:20 +02:00
|
|
|
DWORD rc = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
2017-12-14 15:37:20 +01:00
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, code, 0, (char *)&message,
|
|
|
|
0, nullptr);
|
2014-06-05 14:08:01 +02:00
|
|
|
|
2014-08-17 17:57:20 +02:00
|
|
|
if (rc != 0) {
|
|
|
|
result = String(message);
|
|
|
|
LocalFree(message);
|
2014-06-05 14:08:01 +02:00
|
|
|
|
2014-08-17 17:57:20 +02:00
|
|
|
/* remove trailing new-line characters */
|
|
|
|
boost::algorithm::trim_right(result);
|
|
|
|
}
|
2014-06-05 14:08:01 +02:00
|
|
|
|
2014-08-17 17:57:20 +02:00
|
|
|
msgbuf << code << ", \"" << result << "\"";
|
2014-06-05 14:08:01 +02:00
|
|
|
#else
|
2014-06-05 17:45:02 +02:00
|
|
|
msgbuf << strerror(code);
|
2014-06-05 14:08:01 +02:00
|
|
|
#endif
|
|
|
|
return msgbuf.str();
|
|
|
|
}
|
|
|
|
|
2013-03-22 10:58:47 +01:00
|
|
|
String Utility::EscapeShellCmd(const String& s)
|
|
|
|
{
|
|
|
|
String result;
|
2013-08-20 11:06:04 +02:00
|
|
|
size_t prev_quote = String::NPos;
|
2013-03-22 16:05:48 +01:00
|
|
|
int index = -1;
|
2013-03-22 10:58:47 +01:00
|
|
|
|
2016-08-25 06:19:44 +02:00
|
|
|
for (char ch : s) {
|
2013-03-22 10:58:47 +01:00
|
|
|
bool escape = false;
|
|
|
|
|
|
|
|
index++;
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (ch == '%' || ch == '"' || ch == '\'')
|
|
|
|
escape = true;
|
|
|
|
#else /* _WIN32 */
|
|
|
|
if (ch == '"' || ch == '\'') {
|
|
|
|
/* Find a matching closing quotation character. */
|
|
|
|
if (prev_quote == String::NPos && (prev_quote = s.FindFirstOf(ch, index + 1)) != String::NPos)
|
|
|
|
; /* Empty statement. */
|
|
|
|
else if (prev_quote != String::NPos && s[prev_quote] == ch)
|
|
|
|
prev_quote = String::NPos;
|
|
|
|
else
|
|
|
|
escape = true;
|
|
|
|
}
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
|
|
|
|
if (ch == '#' || ch == '&' || ch == ';' || ch == '`' || ch == '|' ||
|
2017-12-19 15:50:05 +01:00
|
|
|
ch == '*' || ch == '?' || ch == '~' || ch == '<' || ch == '>' ||
|
|
|
|
ch == '^' || ch == '(' || ch == ')' || ch == '[' || ch == ']' ||
|
|
|
|
ch == '{' || ch == '}' || ch == '$' || ch == '\\' || ch == '\x0A' ||
|
|
|
|
ch == '\xFF')
|
2013-03-22 10:58:47 +01:00
|
|
|
escape = true;
|
|
|
|
|
|
|
|
if (escape)
|
|
|
|
#ifdef _WIN32
|
2013-11-13 09:08:17 +01:00
|
|
|
result += '^';
|
2013-03-22 10:58:47 +01:00
|
|
|
#else /* _WIN32 */
|
|
|
|
result += '\\';
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
|
|
|
|
result += ch;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2013-03-25 18:36:15 +01:00
|
|
|
|
2014-04-18 12:14:21 +02:00
|
|
|
String Utility::EscapeShellArg(const String& s)
|
|
|
|
{
|
|
|
|
String result;
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
result = "\"";
|
|
|
|
#else /* _WIN32 */
|
|
|
|
result = "'";
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
|
2016-08-25 06:19:44 +02:00
|
|
|
for (char ch : s) {
|
2014-04-18 12:14:21 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
if (ch == '"' || ch == '%') {
|
|
|
|
result += ' ';
|
|
|
|
}
|
|
|
|
#else /* _WIN32 */
|
2014-04-18 12:39:50 +02:00
|
|
|
if (ch == '\'')
|
2014-04-18 12:14:21 +02:00
|
|
|
result += "'\\'";
|
|
|
|
#endif
|
|
|
|
result += ch;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
result += '"';
|
|
|
|
#else /* _WIN32 */
|
|
|
|
result += '\'';
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-09-30 10:54:34 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
String Utility::EscapeCreateProcessArg(const String& arg)
|
|
|
|
{
|
|
|
|
if (arg.FindFirstOf(" \t\n\v\"") == String::NPos)
|
|
|
|
return arg;
|
|
|
|
|
|
|
|
String result = "\"";
|
|
|
|
|
|
|
|
for (String::ConstIterator it = arg.Begin(); ; it++) {
|
|
|
|
int numBackslashes = 0;
|
|
|
|
|
|
|
|
while (it != arg.End() && *it == '\\') {
|
|
|
|
it++;
|
|
|
|
numBackslashes++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (it == arg.End()) {
|
|
|
|
result.Append(numBackslashes * 2, '\\');
|
|
|
|
break;
|
|
|
|
} else if (*it == '"') {
|
2019-04-08 10:58:50 +02:00
|
|
|
result.Append(numBackslashes * 2 + 1, '\\');
|
2015-09-30 10:54:34 +02:00
|
|
|
result.Append(1, *it);
|
|
|
|
} else {
|
|
|
|
result.Append(numBackslashes, '\\');
|
|
|
|
result.Append(1, *it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result += "\"";
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
|
2013-08-30 10:19:32 +02:00
|
|
|
#ifdef _WIN32
|
2013-09-01 06:01:27 +02:00
|
|
|
static void WindowsSetThreadName(const char *name)
|
|
|
|
{
|
2013-08-30 10:19:32 +02:00
|
|
|
THREADNAME_INFO info;
|
|
|
|
info.dwType = 0x1000;
|
2013-09-01 06:01:27 +02:00
|
|
|
info.szName = name;
|
2013-08-30 10:19:32 +02:00
|
|
|
info.dwThreadID = -1;
|
|
|
|
info.dwFlags = 0;
|
|
|
|
|
|
|
|
__try {
|
|
|
|
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info);
|
|
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
/* Nothing to do here. */
|
|
|
|
}
|
2013-09-01 06:01:27 +02:00
|
|
|
}
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
|
2013-09-03 15:44:31 +02:00
|
|
|
void Utility::SetThreadName(const String& name, bool os)
|
2013-09-01 06:01:27 +02:00
|
|
|
{
|
|
|
|
m_ThreadName.reset(new String(name));
|
|
|
|
|
2013-09-03 15:44:31 +02:00
|
|
|
if (!os)
|
|
|
|
return;
|
|
|
|
|
2013-09-01 06:01:27 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
WindowsSetThreadName(name.CStr());
|
2013-08-30 10:19:32 +02:00
|
|
|
#endif /* _WIN32 */
|
|
|
|
|
2013-09-08 13:13:37 +02:00
|
|
|
#ifdef HAVE_PTHREAD_SET_NAME_NP
|
|
|
|
pthread_set_name_np(pthread_self(), name.CStr());
|
|
|
|
#endif /* HAVE_PTHREAD_SET_NAME_NP */
|
2013-08-30 10:19:32 +02:00
|
|
|
|
2013-09-08 13:13:37 +02:00
|
|
|
#ifdef HAVE_PTHREAD_SETNAME_NP
|
|
|
|
# ifdef __APPLE__
|
|
|
|
pthread_setname_np(name.CStr());
|
|
|
|
# else /* __APPLE__ */
|
2013-08-30 10:19:32 +02:00
|
|
|
String tname = name.SubStr(0, 15);
|
|
|
|
pthread_setname_np(pthread_self(), tname.CStr());
|
2013-09-08 13:13:37 +02:00
|
|
|
# endif /* __APPLE__ */
|
|
|
|
#endif /* HAVE_PTHREAD_SETNAME_NP */
|
2013-03-25 18:36:15 +01:00
|
|
|
}
|
|
|
|
|
2018-01-04 04:25:35 +01:00
|
|
|
String Utility::GetThreadName()
|
2013-03-25 18:36:15 +01:00
|
|
|
{
|
|
|
|
String *name = m_ThreadName.get();
|
|
|
|
|
|
|
|
if (!name) {
|
|
|
|
std::ostringstream idbuf;
|
2017-11-21 12:12:58 +01:00
|
|
|
idbuf << std::this_thread::get_id();
|
2013-03-25 18:36:15 +01:00
|
|
|
return idbuf.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
return *name;
|
|
|
|
}
|
2013-09-12 10:03:48 +02:00
|
|
|
|
2013-12-13 12:54:14 +01:00
|
|
|
unsigned long Utility::SDBM(const String& str, size_t len)
|
2013-09-12 10:03:48 +02:00
|
|
|
{
|
|
|
|
unsigned long hash = 0;
|
2013-12-13 12:54:14 +01:00
|
|
|
size_t current = 0;
|
2013-09-12 10:03:48 +02:00
|
|
|
|
2016-08-25 06:19:44 +02:00
|
|
|
for (char c : str) {
|
2013-12-13 12:54:14 +01:00
|
|
|
if (current >= len)
|
|
|
|
break;
|
|
|
|
|
2013-09-12 10:03:48 +02:00
|
|
|
hash = c + (hash << 6) + (hash << 16) - hash;
|
2013-12-13 12:54:14 +01:00
|
|
|
|
|
|
|
current++;
|
2013-09-12 10:03:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return hash;
|
|
|
|
}
|
2013-09-27 15:30:17 +02:00
|
|
|
|
2019-08-14 11:22:55 +02:00
|
|
|
String Utility::ParseVersion(const String& v)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* 2.11.0-0.rc1.1
|
|
|
|
* v2.10.5
|
|
|
|
* r2.10.3
|
|
|
|
* v2.11.0-rc1-58-g7c1f716da
|
|
|
|
*/
|
|
|
|
boost::regex pattern("^[vr]?(2\\.\\d+\\.\\d+).*$");
|
|
|
|
boost::smatch result;
|
|
|
|
|
|
|
|
if (boost::regex_search(v.GetData(), result, pattern)) {
|
|
|
|
String res(result[1].first, result[1].second);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Couldn't not extract anything, return unparsed version
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2013-09-27 15:30:17 +02:00
|
|
|
int Utility::CompareVersion(const String& v1, const String& v2)
|
|
|
|
{
|
2018-01-04 18:24:45 +01:00
|
|
|
std::vector<String> tokensv1 = v1.Split(".");
|
|
|
|
std::vector<String> tokensv2 = v2.Split(".");
|
2013-09-27 15:30:17 +02:00
|
|
|
|
2014-05-11 06:00:34 +02:00
|
|
|
for (std::vector<String>::size_type i = 0; i < tokensv2.size() - tokensv1.size(); i++)
|
2018-01-04 09:14:55 +01:00
|
|
|
tokensv1.emplace_back("0");
|
2013-09-27 15:30:17 +02:00
|
|
|
|
2014-05-11 06:00:34 +02:00
|
|
|
for (std::vector<String>::size_type i = 0; i < tokensv1.size() - tokensv2.size(); i++)
|
2018-01-04 09:14:55 +01:00
|
|
|
tokensv2.emplace_back("0");
|
2013-09-27 15:30:17 +02:00
|
|
|
|
2014-05-11 06:00:34 +02:00
|
|
|
for (std::vector<String>::size_type i = 0; i < tokensv1.size(); i++) {
|
2013-09-27 15:30:17 +02:00
|
|
|
if (Convert::ToLong(tokensv2[i]) > Convert::ToLong(tokensv1[i]))
|
|
|
|
return 1;
|
|
|
|
else if (Convert::ToLong(tokensv2[i]) < Convert::ToLong(tokensv1[i]))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2013-10-03 22:10:46 +02:00
|
|
|
|
2018-01-04 04:25:35 +01:00
|
|
|
String Utility::GetHostName()
|
2014-05-27 10:20:33 +02:00
|
|
|
{
|
|
|
|
char name[255];
|
|
|
|
|
|
|
|
if (gethostname(name, sizeof(name)) < 0)
|
|
|
|
return "localhost";
|
|
|
|
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2014-05-26 17:32:18 +02:00
|
|
|
/**
|
|
|
|
* Returns the fully-qualified domain name for the host
|
|
|
|
* we're running on.
|
|
|
|
*
|
|
|
|
* @returns The FQDN.
|
|
|
|
*/
|
2018-01-04 04:25:35 +01:00
|
|
|
String Utility::GetFQDN()
|
2014-02-06 15:27:50 +01:00
|
|
|
{
|
2014-05-27 10:20:33 +02:00
|
|
|
String hostname = GetHostName();
|
2014-05-26 17:32:18 +02:00
|
|
|
|
|
|
|
addrinfo hints;
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_family = AF_UNSPEC;
|
|
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
|
|
hints.ai_flags = AI_CANONNAME;
|
|
|
|
|
|
|
|
addrinfo *result;
|
2017-12-14 15:37:20 +01:00
|
|
|
int rc = getaddrinfo(hostname.CStr(), nullptr, &hints, &result);
|
2014-05-26 17:32:18 +02:00
|
|
|
|
2014-10-06 14:21:18 +02:00
|
|
|
if (rc != 0)
|
2017-12-14 15:37:20 +01:00
|
|
|
result = nullptr;
|
2014-05-26 17:32:18 +02:00
|
|
|
|
2014-05-27 10:20:33 +02:00
|
|
|
if (result) {
|
|
|
|
if (strcmp(result->ai_canonname, "localhost") != 0)
|
2014-10-29 16:54:33 +01:00
|
|
|
hostname = result->ai_canonname;
|
2014-05-27 10:20:33 +02:00
|
|
|
|
2014-05-26 17:32:18 +02:00
|
|
|
freeaddrinfo(result);
|
|
|
|
}
|
2014-02-06 15:27:50 +01:00
|
|
|
|
2014-10-29 16:54:33 +01:00
|
|
|
return hostname;
|
2014-02-06 15:27:50 +01:00
|
|
|
}
|
|
|
|
|
2018-01-04 04:25:35 +01:00
|
|
|
int Utility::Random()
|
2013-10-03 22:10:46 +02:00
|
|
|
{
|
2013-12-06 21:46:50 +01:00
|
|
|
#ifdef _WIN32
|
2013-10-03 22:10:46 +02:00
|
|
|
return rand();
|
2013-12-06 21:46:50 +01:00
|
|
|
#else /* _WIN32 */
|
|
|
|
unsigned int *seed = m_RandSeed.get();
|
|
|
|
|
|
|
|
if (!seed) {
|
|
|
|
seed = new unsigned int(Utility::GetTime());
|
|
|
|
m_RandSeed.reset(seed);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rand_r(seed);
|
|
|
|
#endif /* _WIN32 */
|
2013-10-03 22:10:46 +02:00
|
|
|
}
|
2013-11-13 14:56:31 +01:00
|
|
|
|
|
|
|
tm Utility::LocalTime(time_t ts)
|
|
|
|
{
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
tm *result = localtime(&ts);
|
|
|
|
|
2017-12-14 15:37:20 +01:00
|
|
|
if (!result) {
|
2013-11-13 14:56:31 +01:00
|
|
|
BOOST_THROW_EXCEPTION(posix_error()
|
2017-12-19 15:50:05 +01:00
|
|
|
<< boost::errinfo_api_function("localtime")
|
|
|
|
<< boost::errinfo_errno(errno));
|
2013-11-13 14:56:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return *result;
|
|
|
|
#else /* _MSC_VER */
|
|
|
|
tm result;
|
|
|
|
|
2017-12-14 15:37:20 +01:00
|
|
|
if (!localtime_r(&ts, &result)) {
|
2013-11-13 14:56:31 +01:00
|
|
|
BOOST_THROW_EXCEPTION(posix_error()
|
2017-12-19 15:50:05 +01:00
|
|
|
<< boost::errinfo_api_function("localtime_r")
|
|
|
|
<< boost::errinfo_errno(errno));
|
2013-11-13 14:56:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
#endif /* _MSC_VER */
|
2013-12-09 13:46:53 +01:00
|
|
|
}
|
2014-05-13 14:40:12 +02:00
|
|
|
|
|
|
|
bool Utility::PathExists(const String& path)
|
|
|
|
{
|
2019-04-10 11:21:05 +02:00
|
|
|
namespace fs = boost::filesystem;
|
|
|
|
|
|
|
|
boost::system::error_code ec;
|
|
|
|
|
2019-04-10 12:31:53 +02:00
|
|
|
return fs::exists(fs::path(path.Begin(), path.End()), ec) && !ec;
|
2014-05-13 14:40:12 +02:00
|
|
|
}
|
2014-10-27 15:12:19 +01:00
|
|
|
|
2021-01-14 18:39:14 +01:00
|
|
|
time_t Utility::GetFileCreationTime(const String& path)
|
|
|
|
{
|
|
|
|
namespace fs = boost::filesystem;
|
|
|
|
|
|
|
|
return fs::last_write_time(boost::lexical_cast<fs::path>(path));
|
|
|
|
}
|
|
|
|
|
2014-10-27 15:12:19 +01:00
|
|
|
Value Utility::LoadJsonFile(const String& path)
|
|
|
|
{
|
|
|
|
std::ifstream fp;
|
|
|
|
fp.open(path.CStr());
|
|
|
|
|
|
|
|
String json((std::istreambuf_iterator<char>(fp)), std::istreambuf_iterator<char>());
|
|
|
|
|
|
|
|
fp.close();
|
|
|
|
|
|
|
|
if (fp.fail())
|
|
|
|
BOOST_THROW_EXCEPTION(std::runtime_error("Could not read JSON file '" + path + "'."));
|
|
|
|
|
|
|
|
return JsonDecode(json);
|
|
|
|
}
|
|
|
|
|
2016-02-24 11:00:44 +01:00
|
|
|
void Utility::SaveJsonFile(const String& path, int mode, const Value& value)
|
2014-10-27 15:12:19 +01:00
|
|
|
{
|
2022-08-01 17:44:05 +02:00
|
|
|
AtomicFile::Write(path, mode, JsonEncode(value));
|
2014-10-27 15:12:19 +01:00
|
|
|
}
|
2014-12-10 13:20:16 +01:00
|
|
|
|
|
|
|
static void HexEncode(char ch, std::ostream& os)
|
|
|
|
{
|
|
|
|
const char *hex_chars = "0123456789ABCDEF";
|
|
|
|
|
|
|
|
os << hex_chars[ch >> 4 & 0x0f];
|
|
|
|
os << hex_chars[ch & 0x0f];
|
|
|
|
}
|
|
|
|
|
|
|
|
static int HexDecode(char hc)
|
|
|
|
{
|
|
|
|
if (hc >= '0' && hc <= '9')
|
|
|
|
return hc - '0';
|
|
|
|
else if (hc >= 'a' && hc <= 'f')
|
|
|
|
return hc - 'a' + 10;
|
|
|
|
else if (hc >= 'A' && hc <= 'F')
|
|
|
|
return hc - 'A' + 10;
|
|
|
|
else
|
|
|
|
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid hex character."));
|
|
|
|
}
|
|
|
|
|
2015-06-26 15:37:47 +02:00
|
|
|
String Utility::EscapeString(const String& s, const String& chars, const bool illegal)
|
2014-12-10 13:20:16 +01:00
|
|
|
{
|
|
|
|
std::ostringstream result;
|
2015-06-26 15:37:47 +02:00
|
|
|
if (illegal) {
|
2016-08-25 06:19:44 +02:00
|
|
|
for (char ch : s) {
|
2015-06-26 15:37:47 +02:00
|
|
|
if (chars.FindFirstOf(ch) != String::NPos || ch == '%') {
|
|
|
|
result << '%';
|
|
|
|
HexEncode(ch, result);
|
|
|
|
} else
|
|
|
|
result << ch;
|
|
|
|
}
|
|
|
|
} else {
|
2016-08-25 06:19:44 +02:00
|
|
|
for (char ch : s) {
|
2015-06-26 15:37:47 +02:00
|
|
|
if (chars.FindFirstOf(ch) == String::NPos || ch == '%') {
|
|
|
|
result << '%';
|
|
|
|
HexEncode(ch, result);
|
|
|
|
} else
|
|
|
|
result << ch;
|
|
|
|
}
|
2014-12-10 13:20:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return result.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
String Utility::UnescapeString(const String& s)
|
|
|
|
{
|
|
|
|
std::ostringstream result;
|
|
|
|
|
|
|
|
for (String::SizeType i = 0; i < s.GetLength(); i++) {
|
|
|
|
if (s[i] == '%') {
|
|
|
|
if (i + 2 > s.GetLength() - 1)
|
|
|
|
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid escape sequence."));
|
|
|
|
|
|
|
|
char ch = HexDecode(s[i + 1]) * 16 + HexDecode(s[i + 2]);
|
|
|
|
result << ch;
|
|
|
|
|
|
|
|
i += 2;
|
|
|
|
} else
|
|
|
|
result << s[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return result.str();
|
2015-03-03 07:18:47 +01:00
|
|
|
}
|
2015-11-22 12:36:50 +01:00
|
|
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
static String UnameHelper(char type)
|
|
|
|
{
|
2018-12-20 15:48:10 +01:00
|
|
|
struct utsname name;
|
|
|
|
uname(&name);
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case 'm':
|
|
|
|
return (char*)name.machine;
|
|
|
|
case 'n':
|
|
|
|
return (char*)name.nodename;
|
|
|
|
case 'r':
|
|
|
|
return (char*)name.release;
|
|
|
|
case 's':
|
|
|
|
return (char*)name.sysname;
|
|
|
|
case 'v':
|
|
|
|
return (char*)name.version;
|
|
|
|
default:
|
|
|
|
VERIFY(!"Invalid uname query.");
|
|
|
|
}
|
2015-11-22 12:36:50 +01:00
|
|
|
}
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
static bool ReleaseHelper(String *platformName, String *platformVersion)
|
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (platformName)
|
|
|
|
*platformName = "Windows";
|
|
|
|
|
|
|
|
if (platformVersion) {
|
2015-11-26 11:17:17 +01:00
|
|
|
*platformVersion = "Vista";
|
2015-11-22 12:36:50 +01:00
|
|
|
if (IsWindowsVistaSP1OrGreater())
|
|
|
|
*platformVersion = "Vista SP1";
|
|
|
|
if (IsWindowsVistaSP2OrGreater())
|
|
|
|
*platformVersion = "Vista SP2";
|
|
|
|
if (IsWindows7OrGreater())
|
|
|
|
*platformVersion = "7";
|
|
|
|
if (IsWindows7SP1OrGreater())
|
|
|
|
*platformVersion = "7 SP1";
|
|
|
|
if (IsWindows8OrGreater())
|
|
|
|
*platformVersion = "8";
|
|
|
|
if (IsWindows8Point1OrGreater())
|
|
|
|
*platformVersion = "8.1 or greater";
|
|
|
|
if (IsWindowsServer())
|
|
|
|
*platformVersion += " (Server)";
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
#else /* _WIN32 */
|
|
|
|
if (platformName)
|
|
|
|
*platformName = "Unknown";
|
|
|
|
|
|
|
|
if (platformVersion)
|
|
|
|
*platformVersion = "Unknown";
|
|
|
|
|
2016-01-26 15:16:59 +01:00
|
|
|
/* You have systemd or Ubuntu etc. */
|
|
|
|
std::ifstream release("/etc/os-release");
|
|
|
|
if (release.is_open()) {
|
|
|
|
std::string release_line;
|
|
|
|
while (getline(release, release_line)) {
|
|
|
|
std::string::size_type pos = release_line.find("=");
|
|
|
|
|
|
|
|
if (pos == std::string::npos)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
std::string key = release_line.substr(0, pos);
|
|
|
|
std::string value = release_line.substr(pos + 1);
|
|
|
|
|
|
|
|
std::string::size_type firstQuote = value.find("\"");
|
|
|
|
|
|
|
|
if (firstQuote != std::string::npos)
|
|
|
|
value.erase(0, firstQuote + 1);
|
|
|
|
|
|
|
|
std::string::size_type lastQuote = value.rfind("\"");
|
|
|
|
|
|
|
|
if (lastQuote != std::string::npos)
|
|
|
|
value.erase(lastQuote);
|
|
|
|
|
|
|
|
if (platformName && key == "NAME")
|
|
|
|
*platformName = value;
|
|
|
|
|
|
|
|
if (platformVersion && key == "VERSION")
|
|
|
|
*platformVersion = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-22 12:36:50 +01:00
|
|
|
/* You are using a distribution which supports LSB. */
|
2017-02-08 17:45:38 +01:00
|
|
|
FILE *fp = popen("type lsb_release >/dev/null 2>&1 && lsb_release -s -i 2>&1", "r");
|
2015-11-22 12:36:50 +01:00
|
|
|
|
2017-12-14 15:37:20 +01:00
|
|
|
if (fp) {
|
2015-11-22 12:36:50 +01:00
|
|
|
std::ostringstream msgbuf;
|
|
|
|
char line[1024];
|
2017-12-14 15:37:20 +01:00
|
|
|
while (fgets(line, sizeof(line), fp))
|
2015-11-22 12:36:50 +01:00
|
|
|
msgbuf << line;
|
|
|
|
int status = pclose(fp);
|
|
|
|
if (WEXITSTATUS(status) == 0) {
|
|
|
|
if (platformName)
|
|
|
|
*platformName = msgbuf.str();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-08 17:45:38 +01:00
|
|
|
fp = popen("type lsb_release >/dev/null 2>&1 && lsb_release -s -r 2>&1", "r");
|
2015-11-22 12:36:50 +01:00
|
|
|
|
2017-12-14 15:37:20 +01:00
|
|
|
if (fp) {
|
2015-11-22 12:36:50 +01:00
|
|
|
std::ostringstream msgbuf;
|
|
|
|
char line[1024];
|
2017-12-14 15:37:20 +01:00
|
|
|
while (fgets(line, sizeof(line), fp))
|
2015-11-22 12:36:50 +01:00
|
|
|
msgbuf << line;
|
|
|
|
int status = pclose(fp);
|
|
|
|
if (WEXITSTATUS(status) == 0) {
|
|
|
|
if (platformVersion)
|
|
|
|
*platformVersion = msgbuf.str();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* OS X */
|
2017-02-08 17:45:38 +01:00
|
|
|
fp = popen("type sw_vers >/dev/null 2>&1 && sw_vers -productName 2>&1", "r");
|
2015-11-22 12:36:50 +01:00
|
|
|
|
2017-12-14 15:37:20 +01:00
|
|
|
if (fp) {
|
2015-11-22 12:36:50 +01:00
|
|
|
std::ostringstream msgbuf;
|
|
|
|
char line[1024];
|
2017-12-14 15:37:20 +01:00
|
|
|
while (fgets(line, sizeof(line), fp))
|
2015-11-22 12:36:50 +01:00
|
|
|
msgbuf << line;
|
|
|
|
int status = pclose(fp);
|
|
|
|
if (WEXITSTATUS(status) == 0) {
|
|
|
|
String info = msgbuf.str();
|
|
|
|
info = info.Trim();
|
|
|
|
|
|
|
|
if (platformName)
|
|
|
|
*platformName = info;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-08 17:45:38 +01:00
|
|
|
fp = popen("type sw_vers >/dev/null 2>&1 && sw_vers -productVersion 2>&1", "r");
|
2015-11-22 12:36:50 +01:00
|
|
|
|
2017-12-14 15:37:20 +01:00
|
|
|
if (fp) {
|
2015-11-22 12:36:50 +01:00
|
|
|
std::ostringstream msgbuf;
|
|
|
|
char line[1024];
|
2017-12-14 15:37:20 +01:00
|
|
|
while (fgets(line, sizeof(line), fp))
|
2015-11-22 12:36:50 +01:00
|
|
|
msgbuf << line;
|
|
|
|
int status = pclose(fp);
|
|
|
|
if (WEXITSTATUS(status) == 0) {
|
|
|
|
String info = msgbuf.str();
|
|
|
|
info = info.Trim();
|
|
|
|
|
|
|
|
if (platformVersion)
|
|
|
|
*platformVersion = info;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-26 15:16:59 +01:00
|
|
|
/* Centos/RHEL < 7 */
|
2015-11-22 12:36:50 +01:00
|
|
|
release.close();
|
|
|
|
release.open("/etc/redhat-release");
|
|
|
|
if (release.is_open()) {
|
|
|
|
std::string release_line;
|
|
|
|
getline(release, release_line);
|
|
|
|
|
|
|
|
String info = release_line;
|
|
|
|
|
2016-01-26 15:16:59 +01:00
|
|
|
/* example: Red Hat Enterprise Linux Server release 6.7 (Santiago) */
|
2015-11-22 12:36:50 +01:00
|
|
|
if (platformName)
|
2016-01-26 15:16:59 +01:00
|
|
|
*platformName = info.SubStr(0, info.Find("release") - 1);
|
2015-11-22 12:36:50 +01:00
|
|
|
|
|
|
|
if (platformVersion)
|
2016-01-26 15:16:59 +01:00
|
|
|
*platformVersion = info.SubStr(info.Find("release") + 8);
|
2015-11-22 12:36:50 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sles 11 sp3, opensuse w/e */
|
|
|
|
release.close();
|
|
|
|
release.open("/etc/SuSE-release");
|
|
|
|
if (release.is_open()) {
|
|
|
|
std::string release_line;
|
|
|
|
getline(release, release_line);
|
|
|
|
|
|
|
|
String info = release_line;
|
|
|
|
|
|
|
|
if (platformName)
|
|
|
|
*platformName = info.SubStr(0, info.FindFirstOf(" "));
|
|
|
|
|
|
|
|
if (platformVersion)
|
|
|
|
*platformVersion = info.SubStr(info.FindFirstOf(" ") + 1);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Just give up */
|
|
|
|
return false;
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
}
|
|
|
|
|
2018-01-04 04:25:35 +01:00
|
|
|
String Utility::GetPlatformKernel()
|
2015-11-22 12:36:50 +01:00
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
return "Windows";
|
|
|
|
#else /* _WIN32 */
|
|
|
|
return UnameHelper('s');
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
}
|
|
|
|
|
2018-01-04 04:25:35 +01:00
|
|
|
String Utility::GetPlatformKernelVersion()
|
2015-11-22 12:36:50 +01:00
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
OSVERSIONINFO info;
|
|
|
|
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
|
|
GetVersionEx(&info);
|
|
|
|
|
|
|
|
std::ostringstream msgbuf;
|
|
|
|
msgbuf << info.dwMajorVersion << "." << info.dwMinorVersion;
|
|
|
|
|
|
|
|
return msgbuf.str();
|
|
|
|
#else /* _WIN32 */
|
|
|
|
return UnameHelper('r');
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
}
|
|
|
|
|
2018-01-04 04:25:35 +01:00
|
|
|
String Utility::GetPlatformName()
|
2015-11-22 12:36:50 +01:00
|
|
|
{
|
|
|
|
String platformName;
|
2017-12-14 15:37:20 +01:00
|
|
|
if (!ReleaseHelper(&platformName, nullptr))
|
2015-11-22 12:36:50 +01:00
|
|
|
return "Unknown";
|
|
|
|
return platformName;
|
|
|
|
}
|
|
|
|
|
2018-01-04 04:25:35 +01:00
|
|
|
String Utility::GetPlatformVersion()
|
2015-11-22 12:36:50 +01:00
|
|
|
{
|
|
|
|
String platformVersion;
|
2017-12-14 15:37:20 +01:00
|
|
|
if (!ReleaseHelper(nullptr, &platformVersion))
|
2015-11-22 12:36:50 +01:00
|
|
|
return "Unknown";
|
|
|
|
return platformVersion;
|
|
|
|
}
|
|
|
|
|
2018-01-04 04:25:35 +01:00
|
|
|
String Utility::GetPlatformArchitecture()
|
2015-11-22 12:36:50 +01:00
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
SYSTEM_INFO info;
|
|
|
|
GetNativeSystemInfo(&info);
|
|
|
|
switch (info.wProcessorArchitecture) {
|
|
|
|
case PROCESSOR_ARCHITECTURE_AMD64:
|
|
|
|
return "x86_64";
|
|
|
|
case PROCESSOR_ARCHITECTURE_ARM:
|
|
|
|
return "arm";
|
|
|
|
case PROCESSOR_ARCHITECTURE_INTEL:
|
|
|
|
return "x86";
|
|
|
|
default:
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
#else /* _WIN32 */
|
|
|
|
return UnameHelper('m');
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
}
|
2015-12-10 12:25:46 +01:00
|
|
|
|
2019-03-14 09:34:51 +01:00
|
|
|
const char l_Utf8Replacement[] = "\xEF\xBF\xBD";
|
|
|
|
|
2015-12-10 12:25:46 +01:00
|
|
|
String Utility::ValidateUTF8(const String& input)
|
|
|
|
{
|
2022-09-07 15:44:12 +02:00
|
|
|
std::string output;
|
|
|
|
output.reserve(input.GetLength());
|
2015-12-10 12:25:46 +01:00
|
|
|
|
2019-03-14 09:34:51 +01:00
|
|
|
try {
|
|
|
|
utf8::replace_invalid(input.Begin(), input.End(), std::back_inserter(output));
|
|
|
|
} catch (const utf8::not_enough_room&) {
|
|
|
|
output.insert(output.end(), (const char*)l_Utf8Replacement, (const char*)l_Utf8Replacement + 3);
|
2015-12-10 12:25:46 +01:00
|
|
|
}
|
|
|
|
|
2022-09-07 15:44:12 +02:00
|
|
|
return String(std::move(output));
|
2015-12-10 12:25:46 +01:00
|
|
|
}
|
2016-02-22 16:47:41 +01:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
/* mkstemp extracted from libc/sysdeps/posix/tempname.c. Copyright
|
2017-12-19 15:50:05 +01:00
|
|
|
* (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc.
|
|
|
|
*
|
|
|
|
* The GNU C Library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*/
|
2016-02-22 16:47:41 +01:00
|
|
|
|
|
|
|
#define _O_EXCL 0x0400
|
|
|
|
#define _O_CREAT 0x0100
|
|
|
|
#define _O_RDWR 0x0002
|
|
|
|
#define O_EXCL _O_EXCL
|
|
|
|
#define O_CREAT _O_CREAT
|
|
|
|
#define O_RDWR _O_RDWR
|
|
|
|
|
|
|
|
static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
|
|
|
|
|
|
/* Generate a temporary file name based on TMPL. TMPL must match the
|
2017-12-19 15:50:05 +01:00
|
|
|
* rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed
|
|
|
|
* does not exist at the time of the call to mkstemp. TMPL is
|
|
|
|
* overwritten with the result.
|
|
|
|
*/
|
2016-02-22 16:47:41 +01:00
|
|
|
int Utility::MksTemp(char *tmpl)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
char *XXXXXX;
|
|
|
|
static unsigned long long value;
|
|
|
|
unsigned long long random_time_bits;
|
|
|
|
unsigned int count;
|
|
|
|
int fd = -1;
|
|
|
|
int save_errno = errno;
|
|
|
|
|
|
|
|
/* A lower bound on the number of temporary files to attempt to
|
2017-12-19 15:50:05 +01:00
|
|
|
* generate. The maximum total number of temporary file names that
|
|
|
|
* can exist for a given template is 62**6. It should never be
|
|
|
|
* necessary to try all these combinations. Instead if a reasonable
|
|
|
|
* number of names is tried (we define reasonable as 62**3) fail to
|
|
|
|
* give the system administrator the chance to remove the problems.
|
|
|
|
*/
|
2016-02-22 16:47:41 +01:00
|
|
|
#define ATTEMPTS_MIN (62 * 62 * 62)
|
|
|
|
|
2017-12-19 15:50:05 +01:00
|
|
|
/* The number of times to attempt to generate a temporary file
|
|
|
|
* To conform to POSIX, this must be no smaller than TMP_MAX.
|
|
|
|
*/
|
2016-02-22 16:47:41 +01:00
|
|
|
#if ATTEMPTS_MIN < TMP_MAX
|
|
|
|
unsigned int attempts = TMP_MAX;
|
|
|
|
#else
|
|
|
|
unsigned int attempts = ATTEMPTS_MIN;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
len = strlen (tmpl);
|
|
|
|
if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is where the Xs start. */
|
|
|
|
XXXXXX = &tmpl[len - 6];
|
|
|
|
|
|
|
|
/* Get some more or less random data. */
|
|
|
|
{
|
|
|
|
SYSTEMTIME stNow;
|
|
|
|
FILETIME ftNow;
|
|
|
|
|
|
|
|
// get system time
|
|
|
|
GetSystemTime(&stNow);
|
|
|
|
stNow.wMilliseconds = 500;
|
|
|
|
if (!SystemTimeToFileTime(&stNow, &ftNow)) {
|
2017-12-19 15:50:05 +01:00
|
|
|
errno = -1;
|
|
|
|
return -1;
|
2016-02-22 16:47:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
random_time_bits = (((unsigned long long)ftNow.dwHighDateTime << 32) | (unsigned long long)ftNow.dwLowDateTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
value += random_time_bits ^ (unsigned long long)GetCurrentThreadId();
|
|
|
|
|
|
|
|
for (count = 0; count < attempts; value += 7777, ++count) {
|
|
|
|
unsigned long long v = value;
|
|
|
|
|
|
|
|
/* Fill in the random bits. */
|
|
|
|
XXXXXX[0] = letters[v % 62];
|
|
|
|
v /= 62;
|
|
|
|
XXXXXX[1] = letters[v % 62];
|
|
|
|
v /= 62;
|
|
|
|
XXXXXX[2] = letters[v % 62];
|
|
|
|
v /= 62;
|
|
|
|
XXXXXX[3] = letters[v % 62];
|
|
|
|
v /= 62;
|
|
|
|
XXXXXX[4] = letters[v % 62];
|
|
|
|
v /= 62;
|
|
|
|
XXXXXX[5] = letters[v % 62];
|
|
|
|
|
|
|
|
fd = open(tmpl, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE);
|
|
|
|
if (fd >= 0) {
|
|
|
|
errno = save_errno;
|
|
|
|
return fd;
|
|
|
|
} else if (errno != EEXIST)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We got out of the loop because we ran out of combinations to try. */
|
|
|
|
errno = EEXIST;
|
|
|
|
return -1;
|
|
|
|
}
|
2016-03-30 18:59:23 +02:00
|
|
|
|
2018-01-04 04:25:35 +01:00
|
|
|
String Utility::GetIcingaInstallPath()
|
2016-03-30 18:59:23 +02:00
|
|
|
{
|
|
|
|
char szProduct[39];
|
|
|
|
|
|
|
|
for (int i = 0; MsiEnumProducts(i, szProduct) == ERROR_SUCCESS; i++) {
|
|
|
|
char szName[128];
|
|
|
|
DWORD cbName = sizeof(szName);
|
|
|
|
if (MsiGetProductInfo(szProduct, INSTALLPROPERTY_INSTALLEDPRODUCTNAME, szName, &cbName) != ERROR_SUCCESS)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (strcmp(szName, "Icinga 2") != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
char szLocation[1024];
|
|
|
|
DWORD cbLocation = sizeof(szLocation);
|
|
|
|
if (MsiGetProductInfo(szProduct, INSTALLPROPERTY_INSTALLLOCATION, szLocation, &cbLocation) == ERROR_SUCCESS)
|
|
|
|
return szLocation;
|
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2018-01-04 04:25:35 +01:00
|
|
|
String Utility::GetIcingaDataPath()
|
2016-04-01 08:25:36 +02:00
|
|
|
{
|
|
|
|
char path[MAX_PATH];
|
2017-12-14 15:37:20 +01:00
|
|
|
if (!SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_COMMON_APPDATA, nullptr, 0, path)))
|
2016-04-01 08:25:36 +02:00
|
|
|
return "";
|
|
|
|
return String(path) + "\\icinga2";
|
|
|
|
}
|
|
|
|
|
2016-05-10 09:44:41 +02:00
|
|
|
#endif /* _WIN32 */
|
2018-01-26 16:27:16 +01:00
|
|
|
|
2018-11-19 14:59:20 +01:00
|
|
|
/**
|
|
|
|
* Retrieve the environment variable value by given key.
|
|
|
|
*
|
|
|
|
* @param env Environment variable name.
|
|
|
|
*/
|
|
|
|
|
2018-05-23 13:37:29 +02:00
|
|
|
String Utility::GetFromEnvironment(const String& env)
|
2018-01-26 16:27:16 +01:00
|
|
|
{
|
2018-05-23 13:37:29 +02:00
|
|
|
const char *envValue = getenv(env.CStr());
|
2018-11-19 14:59:20 +01:00
|
|
|
|
2018-05-23 13:37:29 +02:00
|
|
|
if (envValue == NULL)
|
|
|
|
return String();
|
2018-01-26 16:27:16 +01:00
|
|
|
else
|
2018-05-23 13:37:29 +02:00
|
|
|
return String(envValue);
|
2018-01-26 16:27:16 +01:00
|
|
|
}
|
2019-02-22 11:37:07 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Compare the password entered by a client with the actual password.
|
|
|
|
* The comparision is safe against timing attacks.
|
|
|
|
*/
|
|
|
|
bool Utility::ComparePasswords(const String& enteredPassword, const String& actualPassword)
|
|
|
|
{
|
|
|
|
volatile const char * volatile enteredPasswordCStr = enteredPassword.CStr();
|
|
|
|
volatile size_t enteredPasswordLen = enteredPassword.GetLength();
|
|
|
|
|
|
|
|
volatile const char * volatile actualPasswordCStr = actualPassword.CStr();
|
|
|
|
volatile size_t actualPasswordLen = actualPassword.GetLength();
|
|
|
|
|
|
|
|
volatile uint_fast8_t result = enteredPasswordLen == actualPasswordLen;
|
|
|
|
|
|
|
|
if (result) {
|
|
|
|
auto cStr (actualPasswordCStr);
|
|
|
|
auto len (actualPasswordLen);
|
|
|
|
|
|
|
|
actualPasswordCStr = cStr;
|
|
|
|
actualPasswordLen = len;
|
|
|
|
} else {
|
|
|
|
auto cStr (enteredPasswordCStr);
|
|
|
|
auto len (enteredPasswordLen);
|
|
|
|
|
|
|
|
actualPasswordCStr = cStr;
|
|
|
|
actualPasswordLen = len;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (volatile size_t i = 0; i < enteredPasswordLen; ++i) {
|
|
|
|
result &= uint_fast8_t(enteredPasswordCStr[i] == actualPasswordCStr[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|