icinga2/lib/base/utility.cpp

1105 lines
25 KiB
C++
Raw Normal View History

/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
2012-05-11 13:33:57 +02:00
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
2014-05-25 16:23:35 +02:00
#include "base/utility.hpp"
#include "base/convert.hpp"
#include "base/application.hpp"
#include "base/logger_fwd.hpp"
#include "base/exception.hpp"
#include "base/socket.hpp"
#include <mmatch.h>
2013-03-15 18:21:29 +01:00
#include <boost/lexical_cast.hpp>
2013-03-16 21:18:53 +01:00
#include <boost/foreach.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/trim.hpp>
2013-03-16 21:18:53 +01:00
#ifdef __FreeBSD__
# include <pthread_np.h>
#endif /* __FreeBSD__ */
2013-11-03 13:45:26 +01:00
#ifndef _MSC_VER
2013-03-16 21:18:53 +01:00
# include <cxxabi.h>
2013-11-03 13:45:26 +01:00
#endif /* _MSC_VER */
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;
boost::thread_specific_ptr<unsigned int> Utility::m_RandSeed;
boost::thread_specific_ptr<bool> Utility::m_LoadingLibrary;
boost::thread_specific_ptr<std::vector<boost::function<void(void)> > > Utility::m_DeferredInitializers;
2013-03-25 18:36:15 +01:00
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
2013-11-03 13:45:26 +01:00
#ifndef _MSC_VER
2012-07-11 20:55:46 +02:00
int status;
2012-09-27 09:38:28 +02:00
char *realname = abi::__cxa_demangle(sym.CStr(), 0, 0, &status);
2012-07-11 20:55:46 +02:00
if (realname != NULL) {
2012-09-27 09:38:28 +02:00
result = String(realname);
2012-07-11 20:55:46 +02:00
free(realname);
}
#else /* _MSC_VER */
CHAR output[256];
if (UnDecorateSymbolName(sym.CStr(), output, sizeof(output), UNDNAME_COMPLETE) > 0)
result = output;
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
/**
* Looks up source file name and line number information for the specified
* ELF executable and RVA.
*
* @param exe The ELF file.
* @param rva The RVA.
* @returns Source file and line number.
*/
String Utility::Addr2Line(const String& exe, uintptr_t rva)
{
#ifndef _WIN32
std::ostringstream msgbuf;
msgbuf << "addr2line -s -e " << Application::GetExePath(exe) << " " << std::hex << rva << " 2>/dev/null";
String args = msgbuf.str();
FILE *fp = popen(args.CStr(), "r");
if (!fp)
return "RVA: " + Convert::ToString(rva);
char buffer[512] = {};
fgets(buffer, sizeof(buffer), fp);
fclose(fp);
String line = buffer;
boost::algorithm::trim_right(line);
if (line.GetLength() == 0)
return "RVA: " + Convert::ToString(rva);
return line;
#else /* _WIN32 */
return String();
#endif /* _WIN32 */
}
String Utility::GetSymbolName(const void *addr)
{
#ifdef HAVE_DLADDR
Dl_info dli;
if (dladdr(addr, &dli) > 0)
return dli.dli_sname;
#endif /* HAVE_DLADDR */
#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)";
}
String Utility::GetSymbolSource(const void *addr)
{
#ifdef HAVE_DLADDR
Dl_info dli;
if (dladdr(addr, &dli) > 0) {
uintptr_t rva = reinterpret_cast<uintptr_t>(addr) - reinterpret_cast<uintptr_t>(dli.dli_fbase);
return Addr2Line(dli.dli_fname, rva);
}
#endif /* HAVE_DLADDR */
#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;
DWORD dwDisplacement;
IMAGEHLP_LINE64 line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (SymGetLineFromAddr64(GetCurrentProcess(), dwAddress, &dwDisplacement, &line))
return String(line.FileName) + ":" + Convert::ToString(line.LineNumber);
#endif /* _WIN32 */
return "(unknown file/line)";
}
/**
* Performs wildcard pattern matching.
*
* @param pattern The wildcard pattern.
* @param text The String that should be checked.
* @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)
{
return (match(pattern.CStr(), text.CStr()) == 0);
}
/**
* Returns the directory component of a path. See dirname(3) for details.
*
* @param path The full path.
* @returns The directory.
*/
String Utility::DirName(const String& path)
{
char *dir;
#ifdef _WIN32
String dupPath = path;
/* PathRemoveFileSpec doesn't properly handle forward slashes. */
BOOST_FOREACH(char& ch, dupPath) {
if (ch == '/')
ch = '\\';
}
dir = strdup(dupPath.CStr());
#else /* _WIN32 */
dir = strdup(path.CStr());
#endif /* _WIN32 */
if (dir == NULL)
2013-03-16 21:18:53 +01:00
BOOST_THROW_EXCEPTION(std::bad_alloc());
String result;
#ifndef _WIN32
result = dirname(dir);
#else /* _WIN32 */
if (!PathRemoveFileSpec(dir)) {
free(dir);
2013-03-11 14:03:01 +01:00
BOOST_THROW_EXCEPTION(win32_error()
2013-03-18 17:04:22 +01:00
<< boost::errinfo_api_function("PathRemoveFileSpec")
<< errinfo_win32_error(GetLastError()));
}
result = dir;
if (result.IsEmpty())
result = ".";
#endif /* _WIN32 */
free(dir);
return result;
}
/**
* Returns the file component of a path. See basename(3) for details.
*
* @param path The full path.
* @returns The filename.
*/
String Utility::BaseName(const String& path)
{
char *dir = strdup(path.CStr());
String result;
if (dir == NULL)
2013-03-16 21:18:53 +01:00
BOOST_THROW_EXCEPTION(std::bad_alloc());
#ifndef _WIN32
result = basename(dir);
#else /* _WIN32 */
result = PathFindFileName(dir);
#endif /* _WIN32 */
free(dir);
return result;
}
/**
* 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-08-07 21:02:12 +02:00
void Utility::NullDeleter(void *)
{
/* Nothing to do here. */
}
/**
* Returns the current UNIX timestamp including fractions of seconds.
*
* @returns The current time.
*/
double Utility::GetTime(void)
{
#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 */
struct timeval tv;
2013-03-11 13:45:08 +01:00
if (gettimeofday(&tv, NULL) < 0) {
BOOST_THROW_EXCEPTION(posix_error()
2013-03-18 17:04:22 +01:00
<< boost::errinfo_api_function("gettimeofday")
<< boost::errinfo_errno(errno));
2013-03-11 13:45:08 +01:00
}
return tv.tv_sec + tv.tv_usec / 1000000.0;
#endif /* _WIN32 */
}
/**
* Returns the ID of the current process.
*
* @returns The PID.
*/
pid_t Utility::GetPid(void)
{
#ifndef _WIN32
return getpid();
#else /* _WIN32 */
return GetCurrentProcessId();
#endif /* _WIN32 */
}
/**
* Sleeps for the specified amount of time.
*
* @param timeout The timeout in seconds.
*/
void Utility::Sleep(double timeout)
{
#ifndef _WIN32
usleep(timeout * 1000 * 1000);
#else /* _WIN32 */
2012-09-27 10:05:54 +02:00
::Sleep(timeout * 1000);
#endif /* _WIN32 */
}
/**
* Loads the specified library.
*
* @param library The name of the library.
*/
#ifdef _WIN32
HMODULE
#else /* _WIN32 */
2013-10-30 11:58:45 +01:00
void *
#endif /* _WIN32 */
Utility::LoadExtensionLibrary(const String& library)
{
String path;
#if defined(_WIN32)
2013-11-03 13:45:26 +01:00
path = library + ".dll";
#elif defined(__APPLE__)
path = "lib" + library + ".dylib";
#else /* __APPLE__ */
2013-11-03 13:45:26 +01:00
path = "lib" + library + ".so";
#endif /* _WIN32 */
Log(LogInformation, "Utility", "Loading library '" + path + "'");
#ifdef _WIN32
HMODULE hModule = LoadLibrary(path.CStr());
2013-03-11 14:03:01 +01:00
if (hModule == NULL) {
BOOST_THROW_EXCEPTION(win32_error()
2013-03-18 17:04:22 +01:00
<< boost::errinfo_api_function("LoadLibrary")
<< errinfo_win32_error(GetLastError())
<< boost::errinfo_file_name(path));
2013-03-11 14:03:01 +01:00
}
#else /* _WIN32 */
void *hModule = dlopen(path.CStr(), RTLD_NOW);
if (hModule == NULL) {
2013-10-30 11:58:45 +01:00
BOOST_THROW_EXCEPTION(std::runtime_error("Could not load library '" + path + "': " + dlerror()));
}
#endif /* _WIN32 */
ExecuteDeferredInitializers();
return hModule;
}
void Utility::ExecuteDeferredInitializers(void)
{
if (!m_DeferredInitializers.get())
return;
BOOST_FOREACH(const boost::function<void(void)>& callback, *m_DeferredInitializers.get())
callback();
m_DeferredInitializers.reset();
}
void Utility::AddDeferredInitializer(const boost::function<void(void)>& callback)
{
if (!m_DeferredInitializers.get())
m_DeferredInitializers.reset(new std::vector<boost::function<void(void)> >());
m_DeferredInitializers.get()->push_back(callback);
}
/**
2013-05-03 11:26:18 +02:00
* Generates a new unique ID.
*
2013-05-03 11:26:18 +02:00
* @returns The new unique ID.
*/
2013-05-03 11:26:18 +02:00
String Utility::NewUniqueID(void)
{
2013-05-03 11:26:18 +02:00
static boost::mutex mutex;
static int next_id = 0;
/* I'd much rather use UUIDs but RHEL is way too cool to have
* a semi-recent version of boost. Yay. */
String id;
char buf[128];
if (gethostname(buf, sizeof(buf)) == 0)
id = String(buf) + "-";
id += Convert::ToString((long)Utility::GetTime()) + "-";
{
boost::mutex::scoped_lock lock(mutex);
id += Convert::ToString(next_id);
next_id++;
}
return id;
}
/**
* 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)
*/
bool Utility::Glob(const String& pathSpec, const boost::function<void (const String&)>& callback, int type)
{
std::vector<String> files, dirs;
#ifdef _WIN32
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()
2013-03-18 17:04:22 +01:00
<< boost::errinfo_api_function("FindFirstFile")
2013-03-18 22:40:40 +01:00
<< errinfo_win32_error(errorCode)
2013-03-18 17:04:22 +01:00
<< boost::errinfo_file_name(pathSpec));
}
do {
if (strcmp(wfd.cFileName, ".") == 0 || strcmp(wfd.cFileName, "..") == 0)
continue;
String path = DirName(pathSpec) + "/" + wfd.cFileName;
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))
files.push_back(path);
} while (FindNextFile(handle, &wfd));
2013-03-11 14:03:01 +01:00
if (!FindClose(handle)) {
BOOST_THROW_EXCEPTION(win32_error()
2013-03-18 17:04:22 +01:00
<< boost::errinfo_api_function("FindClose")
2013-03-11 14:03:01 +01:00
<< errinfo_win32_error(GetLastError()));
}
#else /* _WIN32 */
glob_t gr;
int rc = glob(pathSpec.CStr(), GLOB_ERR | GLOB_NOSORT, NULL, &gr);
if (rc < 0) {
if (rc == GLOB_NOMATCH)
return false;
2013-03-11 13:45:08 +01:00
BOOST_THROW_EXCEPTION(posix_error()
2013-03-18 17:04:22 +01:00
<< boost::errinfo_api_function("glob")
<< boost::errinfo_errno(errno)
<< boost::errinfo_file_name(pathSpec));
}
if (gr.gl_pathc == 0) {
globfree(&gr);
return false;
}
size_t left;
char **gp;
for (gp = gr.gl_pathv, left = gr.gl_pathc; left > 0; gp++, left--) {
struct stat statbuf;
if (stat(*gp, &statbuf) < 0)
continue;
if (!S_ISDIR(statbuf.st_mode) && !S_ISREG(statbuf.st_mode))
continue;
if (S_ISDIR(statbuf.st_mode) && (type & GlobDirectory))
dirs.push_back(*gp);
else if (!S_ISDIR(statbuf.st_mode) && (type & GlobFile))
files.push_back(*gp);
}
globfree(&gr);
#endif /* _WIN32 */
std::sort(files.begin(), files.end());
BOOST_FOREACH(const String& cpath, files) {
callback(cpath);
}
std::sort(dirs.begin(), dirs.end());
BOOST_FOREACH(const String& cpath, dirs) {
callback(cpath);
}
return true;
}
/**
* 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)
*/
bool Utility::GlobRecursive(const String& path, const String& pattern, const boost::function<void (const String&)>& callback, int type)
{
std::vector<String> files, dirs, alldirs;
#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()
<< boost::errinfo_api_function("FindFirstFile")
<< errinfo_win32_error(errorCode)
<< boost::errinfo_file_name(pathSpec));
}
do {
if (strcmp(wfd.cFileName, ".") == 0 || strcmp(wfd.cFileName, "..") == 0)
continue;
String cpath = path + "/" + wfd.cFileName;
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
alldirs.push_back(cpath);
if (!Utility::Match(pattern, wfd.cFileName))
continue;
if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (type & GlobFile))
files.push_back(cpath);
if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (type & GlobDirectory))
dirs.push_back(cpath);
} while (FindNextFile(handle, &wfd));
if (!FindClose(handle)) {
BOOST_THROW_EXCEPTION(win32_error()
<< boost::errinfo_api_function("FindClose")
<< errinfo_win32_error(GetLastError()));
}
#else /* _WIN32 */
DIR *dirp;
dirp = opendir(path.CStr());
if (dirp == NULL)
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("opendir")
<< boost::errinfo_errno(errno)
<< boost::errinfo_file_name(path));
while (dirp) {
dirent ent, *pent;
if (readdir_r(dirp, &ent, &pent) < 0) {
closedir(dirp);
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("readdir_r")
<< boost::errinfo_errno(errno)
<< boost::errinfo_file_name(path));
}
if (!pent)
break;
if (strcmp(ent.d_name, ".") == 0 || strcmp(ent.d_name, "..") == 0)
continue;
String cpath = path + "/" + ent.d_name;
struct stat statbuf;
if (lstat(cpath.CStr(), &statbuf) < 0)
continue;
if (S_ISDIR(statbuf.st_mode))
alldirs.push_back(cpath);
if (!Utility::Match(pattern, ent.d_name))
continue;
if (S_ISDIR(statbuf.st_mode) && (type & GlobDirectory))
dirs.push_back(cpath);
if (!S_ISDIR(statbuf.st_mode) && (type & GlobFile))
files.push_back(cpath);
}
closedir(dirp);
#endif /* _WIN32 */
std::sort(files.begin(), files.end());
BOOST_FOREACH(const String& cpath, files) {
callback(cpath);
}
std::sort(dirs.begin(), dirs.end());
BOOST_FOREACH(const String& cpath, dirs) {
callback(cpath);
}
std::sort(alldirs.begin(), alldirs.end());
BOOST_FOREACH(const String& cpath, alldirs) {
GlobRecursive(cpath, pattern, callback, type);
}
return true;
}
2013-02-14 10:59:01 +01:00
#ifndef _WIN32
void Utility::SetNonBlocking(int fd)
{
2013-03-11 13:45:08 +01:00
int flags = fcntl(fd, F_GETFL, 0);
2013-03-11 13:45:08 +01:00
if (flags < 0) {
BOOST_THROW_EXCEPTION(posix_error()
2013-03-18 17:04:22 +01:00
<< boost::errinfo_api_function("fcntl")
<< boost::errinfo_errno(errno));
2013-03-11 13:45:08 +01:00
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
BOOST_THROW_EXCEPTION(posix_error()
2013-03-18 17:04:22 +01:00
<< boost::errinfo_api_function("fcntl")
<< boost::errinfo_errno(errno));
2013-03-11 13:45:08 +01:00
}
}
void Utility::SetCloExec(int fd)
{
2013-03-11 13:45:08 +01:00
int flags = fcntl(fd, F_GETFD, 0);
2013-03-11 13:45:08 +01:00
if (flags < 0) {
BOOST_THROW_EXCEPTION(posix_error()
2013-03-18 17:04:22 +01:00
<< boost::errinfo_api_function("fcntl")
<< boost::errinfo_errno(errno));
2013-03-11 13:45:08 +01:00
}
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
BOOST_THROW_EXCEPTION(posix_error()
2013-03-18 17:04:22 +01:00
<< boost::errinfo_api_function("fcntl")
<< boost::errinfo_errno(errno));
2013-03-11 13:45:08 +01:00
}
}
2013-02-14 10:59:01 +01:00
#endif /* _WIN32 */
void Utility::SetNonBlockingSocket(SOCKET s)
{
#ifndef _WIN32
SetNonBlocking(s);
#else /* _WIN32 */
unsigned long lTrue = 1;
ioctlsocket(s, FIONBIO, &lTrue);
#endif /* _WIN32 */
}
2013-02-18 14:40:24 +01:00
void Utility::QueueAsyncCallback(const boost::function<void (void)>& callback)
{
2013-03-25 18:36:15 +01:00
Application::GetTP().Post(callback);
2013-02-18 14:40:24 +01: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++) {
result += tokens[i];
if (tokens.size() > i + 1) {
if (i < tokens.size() - 2)
result += ", ";
else if (i == tokens.size() - 2)
result += " and ";
}
}
return result;
}
String Utility::FormatDuration(int duration)
{
std::vector<String> tokens;
String result;
if (duration >= 86400) {
int days = duration / 86400;
tokens.push_back(Convert::ToString(days) + (days != 1 ? " days" : " day"));
duration %= 86400;
}
if (duration >= 3600) {
int hours = duration / 3600;
tokens.push_back(Convert::ToString(hours) + (hours != 1 ? " hours" : " hour"));
duration %= 3600;
}
if (duration >= 60) {
int minutes = duration / 60;
tokens.push_back(Convert::ToString(minutes) + (minutes != 1 ? " minutes" : " minute"));
duration %= 60;
}
if (duration >= 1) {
int seconds = duration;
tokens.push_back(Convert::ToString(seconds) + (seconds != 1 ? " seconds" : " second"));
}
if (tokens.size() == 0) {
int milliseconds = floor(duration * 1000);
if (milliseconds >= 1)
tokens.push_back(Convert::ToString(milliseconds) + (milliseconds != 1 ? " milliseconds" : " millisecond"));
else
tokens.push_back("less than 1 millisecond");
}
return NaturalJoin(tokens);
}
String Utility::FormatDateTime(const char *format, double ts)
{
char timestamp[128];
2013-09-02 10:53:01 +02:00
time_t tempts = (time_t)ts; /* We don't handle sub-second timestamps here just yet. */
tm tmthen;
#ifdef _MSC_VER
tm *temp = localtime(&tempts);
2013-03-11 13:45:08 +01:00
if (temp == NULL) {
BOOST_THROW_EXCEPTION(posix_error()
2013-03-18 17:04:22 +01:00
<< boost::errinfo_api_function("localtime")
<< boost::errinfo_errno(errno));
2013-03-11 13:45:08 +01:00
}
tmthen = *temp;
#else /* _MSC_VER */
2013-03-11 13:45:08 +01:00
if (localtime_r(&tempts, &tmthen) == NULL) {
BOOST_THROW_EXCEPTION(posix_error()
2013-03-18 17:04:22 +01:00
<< boost::errinfo_api_function("localtime_r")
<< boost::errinfo_errno(errno));
2013-03-11 13:45:08 +01:00
}
#endif /* _MSC_VER */
strftime(timestamp, sizeof(timestamp), format, &tmthen);
return timestamp;
}
2013-03-22 10:58:47 +01:00
String Utility::FormatErrorNumber(int code) {
std::ostringstream msgbuf;
#ifdef _WIN32
char *message;
String result = "Unknown error.";
DWORD rc = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, (char *)&message,
0, NULL);
if (rc != 0) {
result = String(message);
LocalFree(message);
/* remove trailing new-line characters */
boost::algorithm::trim_right(result);
}
msgbuf << code << ", \"" << result << "\"";
return tmp.str();
#else
msgbuf << strerror(code);
#endif
return msgbuf.str();
}
2013-03-22 10:58:47 +01:00
String Utility::EscapeShellCmd(const String& s)
{
String result;
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
BOOST_FOREACH(char ch, s) {
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 == '|' ||
ch == '*' || ch == '?' || ch == '~' || ch == '<' || ch == '>' ||
ch == '^' || ch == '(' || ch == ')' || ch == '[' || ch == ']' ||
ch == '{' || ch == '}' || ch == '$' || ch == '\\' || ch == '\x0A' ||
ch == '\xFF')
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
String Utility::EscapeShellArg(const String& s)
{
String result;
#ifdef _WIN32
result = "\"";
#else /* _WIN32 */
result = "'";
#endif /* _WIN32 */
BOOST_FOREACH(char ch, s) {
#ifdef _WIN32
if (ch == '"' || ch == '%') {
result += ' ';
}
#else /* _WIN32 */
if (ch == '\'')
result += "'\\'";
#endif
result += ch;
}
#ifdef _WIN32
result += '"';
#else /* _WIN32 */
result += '\'';
#endif /* _WIN32 */
return result;
}
#ifdef _WIN32
2013-09-01 06:01:27 +02:00
static void WindowsSetThreadName(const char *name)
{
THREADNAME_INFO info;
info.dwType = 0x1000;
2013-09-01 06:01:27 +02:00
info.szName = name;
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 */
void Utility::SetThreadName(const String& name, bool os)
2013-09-01 06:01:27 +02:00
{
m_ThreadName.reset(new String(name));
if (!os)
return;
2013-09-01 06:01:27 +02:00
#ifdef _WIN32
WindowsSetThreadName(name.CStr());
#endif /* _WIN32 */
#ifdef HAVE_PTHREAD_SET_NAME_NP
pthread_set_name_np(pthread_self(), name.CStr());
#endif /* HAVE_PTHREAD_SET_NAME_NP */
#ifdef HAVE_PTHREAD_SETNAME_NP
# ifdef __APPLE__
pthread_setname_np(name.CStr());
# else /* __APPLE__ */
String tname = name.SubStr(0, 15);
pthread_setname_np(pthread_self(), tname.CStr());
# endif /* __APPLE__ */
#endif /* HAVE_PTHREAD_SETNAME_NP */
2013-03-25 18:36:15 +01:00
}
String Utility::GetThreadName(void)
{
String *name = m_ThreadName.get();
if (!name) {
std::ostringstream idbuf;
idbuf << boost::this_thread::get_id();
return idbuf.str();
}
return *name;
}
2013-09-12 10:03:48 +02:00
unsigned long Utility::SDBM(const String& str, size_t len)
2013-09-12 10:03:48 +02:00
{
unsigned long hash = 0;
size_t current = 0;
2013-09-12 10:03:48 +02:00
BOOST_FOREACH(char c, str) {
if (current >= len)
break;
2013-09-12 10:03:48 +02:00
hash = c + (hash << 6) + (hash << 16) - hash;
current++;
2013-09-12 10:03:48 +02:00
}
return hash;
}
int Utility::CompareVersion(const String& v1, const String& v2)
{
std::vector<String> tokensv1, tokensv2;
boost::algorithm::split(tokensv1, v1, boost::is_any_of("."));
boost::algorithm::split(tokensv2, v2, boost::is_any_of("."));
2014-05-11 06:00:34 +02:00
for (std::vector<String>::size_type i = 0; i < tokensv2.size() - tokensv1.size(); i++)
tokensv1.push_back("0");
2014-05-11 06:00:34 +02:00
for (std::vector<String>::size_type i = 0; i < tokensv1.size() - tokensv2.size(); i++)
tokensv2.push_back("0");
2014-05-11 06:00:34 +02:00
for (std::vector<String>::size_type i = 0; i < tokensv1.size(); i++) {
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
String Utility::GetHostName(void)
{
char name[255];
if (gethostname(name, sizeof(name)) < 0)
return "localhost";
return name;
}
/**
* Returns the fully-qualified domain name for the host
* we're running on.
*
* @returns The FQDN.
*/
String Utility::GetFQDN(void)
{
String hostname = GetHostName();
addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_CANONNAME;
addrinfo *result;
int rc = getaddrinfo(hostname.CStr(), NULL, &hints, &result);
if (rc < 0)
result = NULL;
String canonicalName;
if (result) {
if (strcmp(result->ai_canonname, "localhost") != 0)
canonicalName = result->ai_canonname;
freeaddrinfo(result);
} else {
canonicalName = hostname;
}
return canonicalName;
}
2013-10-03 22:10:46 +02:00
int Utility::Random(void)
{
#ifdef _WIN32
2013-10-03 22:10:46 +02:00
return rand();
#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
}
tm Utility::LocalTime(time_t ts)
{
#ifdef _MSC_VER
tm *result = localtime(&ts);
if (result == NULL) {
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("localtime")
<< boost::errinfo_errno(errno));
}
return *result;
#else /* _MSC_VER */
tm result;
if (localtime_r(&ts, &result) == NULL) {
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("localtime_r")
<< boost::errinfo_errno(errno));
}
return result;
#endif /* _MSC_VER */
}
bool Utility::PathExists(const String& path)
{
#ifndef _WIN32
struct stat statbuf;
return (lstat(path.CStr(), &statbuf) >= 0);
#else /* _WIN32 */
struct _stat statbuf;
2014-05-17 08:38:28 +02:00
return (_stat(path.CStr(), &statbuf) >= 0);
#endif /* _WIN32 */
}