2015-04-13 04:09:02 -05:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
2015-03-17 18:56:15 -05:00
|
|
|
/*
|
|
|
|
* This file is part of the LibreOffice project.
|
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef INCLUDED_UTIL_HPP
|
|
|
|
#define INCLUDED_UTIL_HPP
|
|
|
|
|
2016-09-26 21:29:53 -05:00
|
|
|
#include <atomic>
|
loolwsd: include cleanup and organization
A source file (.cpp) must include its own header first.
This insures that the header is self-contained and
doesn't depend on arbitrary (and accidental) includes
before it to compile.
Furthermore, system headers should go next, followed by
C then C++ headers, then libraries (Poco, etc) and, finally,
project headers come last.
This makes sure that headers and included in the same dependency
order to avoid side-effects. For example, Poco should never rely on
anything from our project in the same way that a C header should
never rely on anything in C++, Poco, or project headers.
Also, includes ought to be sorted where possible, to improve
readability and avoid accidental duplicates (of which there
were a few).
Change-Id: I62cc1343e4a091d69195e37ed659dba20cfcb1ef
Reviewed-on: https://gerrit.libreoffice.org/25262
Reviewed-by: Ashod Nakashian <ashnakash@gmail.com>
Tested-by: Ashod Nakashian <ashnakash@gmail.com>
2016-05-21 09:23:07 -05:00
|
|
|
#include <cassert>
|
2015-12-28 15:34:21 -06:00
|
|
|
#include <functional>
|
2016-01-21 08:28:05 -06:00
|
|
|
#include <memory>
|
2016-10-05 21:04:25 -05:00
|
|
|
#include <mutex>
|
2016-04-18 17:52:10 -05:00
|
|
|
#include <set>
|
loolwsd: include cleanup and organization
A source file (.cpp) must include its own header first.
This insures that the header is self-contained and
doesn't depend on arbitrary (and accidental) includes
before it to compile.
Furthermore, system headers should go next, followed by
C then C++ headers, then libraries (Poco, etc) and, finally,
project headers come last.
This makes sure that headers and included in the same dependency
order to avoid side-effects. For example, Poco should never rely on
anything from our project in the same way that a C header should
never rely on anything in C++, Poco, or project headers.
Also, includes ought to be sorted where possible, to improve
readability and avoid accidental duplicates (of which there
were a few).
Change-Id: I62cc1343e4a091d69195e37ed659dba20cfcb1ef
Reviewed-on: https://gerrit.libreoffice.org/25262
Reviewed-by: Ashod Nakashian <ashnakash@gmail.com>
Tested-by: Ashod Nakashian <ashnakash@gmail.com>
2016-05-21 09:23:07 -05:00
|
|
|
#include <sstream>
|
|
|
|
#include <string>
|
2015-04-24 04:49:19 -05:00
|
|
|
|
2015-12-25 19:11:47 -06:00
|
|
|
#include <Poco/File.h>
|
|
|
|
#include <Poco/Path.h>
|
2016-02-16 15:25:51 -06:00
|
|
|
#include <Poco/Process.h>
|
2016-09-27 10:07:52 -05:00
|
|
|
#include <Poco/RegularExpression.h>
|
2015-04-22 03:14:11 -05:00
|
|
|
|
2015-11-24 02:19:17 -06:00
|
|
|
#define LOK_USE_UNSTABLE_API
|
|
|
|
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
|
|
|
|
|
2015-03-26 10:07:52 -05:00
|
|
|
namespace Util
|
2015-03-17 18:56:15 -05:00
|
|
|
{
|
2015-12-19 08:16:44 -06:00
|
|
|
namespace rng
|
|
|
|
{
|
2016-03-12 09:15:45 -06:00
|
|
|
void reseed();
|
|
|
|
unsigned getNext();
|
2016-11-12 15:38:13 -06:00
|
|
|
|
2017-02-17 00:15:42 -06:00
|
|
|
/// Generate an array of random characters.
|
|
|
|
std::vector<char> getBytes(const size_t length);
|
|
|
|
|
2016-11-12 15:38:13 -06:00
|
|
|
/// Generates a random string suitable for
|
|
|
|
/// file/directory names.
|
|
|
|
std::string getFilename(const size_t length);
|
2015-12-19 08:16:44 -06:00
|
|
|
}
|
|
|
|
|
2017-05-24 14:37:20 -05:00
|
|
|
/// Hex to unsigned char
|
|
|
|
bool dataFromHexString(const std::string& hexString, std::vector<unsigned char>& data);
|
2015-12-27 21:47:39 -06:00
|
|
|
/// Encode an integral ID into a string, with padding support.
|
|
|
|
std::string encodeId(const unsigned number, const int padding = 5);
|
2016-01-09 14:46:08 -06:00
|
|
|
/// Decode an integral ID from a string.
|
|
|
|
unsigned decodeId(const std::string& str);
|
2015-12-27 21:47:39 -06:00
|
|
|
|
2015-03-28 06:53:44 -05:00
|
|
|
bool windowingAvailable();
|
2015-04-24 04:49:47 -05:00
|
|
|
|
2017-02-06 08:59:14 -06:00
|
|
|
#if !defined(BUILDING_TESTS) && !defined(KIT_IN_PROCESS)
|
2016-11-17 08:00:05 -06:00
|
|
|
|
|
|
|
/// Send a message to all clients.
|
|
|
|
void alertAllUsers(const std::string& msg);
|
|
|
|
|
|
|
|
/// Send a 'error:' message with the specified cmd and kind parameters to all connected
|
|
|
|
/// clients. This function can be called either in loolwsd or loolkit processes, even if only
|
|
|
|
/// loolwsd obviously has contact with the actual clients; in loolkit it will be forwarded to
|
|
|
|
/// loolwsd for redistribution. (This function must be implemented separately in each program
|
|
|
|
/// that uses it, it is not in Util.cpp.)
|
2016-09-28 14:07:07 -05:00
|
|
|
void alertAllUsers(const std::string& cmd, const std::string& kind);
|
|
|
|
#else
|
2016-11-17 08:00:05 -06:00
|
|
|
|
|
|
|
/// No-op implementation in the test programs
|
|
|
|
inline void alertAllUsers(const std::string&)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/// No-op implementation in the test programs
|
2016-09-28 14:07:07 -05:00
|
|
|
inline void alertAllUsers(const std::string&, const std::string&)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|
2016-09-27 07:48:32 -05:00
|
|
|
|
2016-05-02 18:17:46 -05:00
|
|
|
/// Assert that a lock is already taken.
|
|
|
|
template <typename T>
|
2016-10-05 21:04:25 -05:00
|
|
|
void assertIsLocked(const T& lock)
|
2016-05-02 18:17:46 -05:00
|
|
|
{
|
2017-04-05 04:57:28 -05:00
|
|
|
#ifdef NDEBUG
|
|
|
|
(void) lock;
|
|
|
|
#else
|
2016-10-05 21:04:25 -05:00
|
|
|
assert(lock.owns_lock());
|
2017-04-05 04:57:28 -05:00
|
|
|
#endif
|
2016-10-05 21:04:25 -05:00
|
|
|
}
|
|
|
|
|
2016-10-29 20:15:00 -05:00
|
|
|
inline void assertIsLocked(std::mutex& mtx)
|
2016-10-05 21:04:25 -05:00
|
|
|
{
|
2017-04-05 04:57:28 -05:00
|
|
|
#ifdef NDEBUG
|
|
|
|
(void) mtx;
|
|
|
|
#else
|
2016-10-05 21:04:25 -05:00
|
|
|
assert(!mtx.try_lock());
|
2017-04-05 04:57:28 -05:00
|
|
|
#endif
|
2016-05-02 18:17:46 -05:00
|
|
|
}
|
|
|
|
|
2017-07-07 06:42:19 -05:00
|
|
|
/// Returns the total physical memory (in kB) available in the system
|
|
|
|
size_t getTotalSystemMemory();
|
|
|
|
|
2017-02-03 00:29:53 -06:00
|
|
|
/// Returns the process PSS in KB (works only when we have perms for /proc/pid/smaps).
|
|
|
|
size_t getMemoryUsagePSS(const Poco::Process::PID pid);
|
|
|
|
|
|
|
|
/// Returns the process RSS in KB.
|
|
|
|
size_t getMemoryUsageRSS(const Poco::Process::PID pid);
|
|
|
|
|
|
|
|
/// Returns the RSS and PSS of the current process in KB.
|
|
|
|
/// Example: "procmemstats: pid=123 rss=12400 pss=566"
|
|
|
|
std::string getMemoryStats(FILE* file);
|
2016-03-31 03:01:21 -05:00
|
|
|
|
2017-06-05 20:17:42 -05:00
|
|
|
size_t getCpuUsage(const Poco::Process::PID pid);
|
|
|
|
|
|
|
|
size_t getStatFromPid(const Poco::Process::PID pid, int ind);
|
|
|
|
|
2017-05-14 20:44:16 -05:00
|
|
|
std::string replace(std::string s, const std::string& a, const std::string& b);
|
2016-03-31 03:01:21 -05:00
|
|
|
|
|
|
|
std::string formatLinesForLog(const std::string& s);
|
2016-04-07 02:55:57 -05:00
|
|
|
|
|
|
|
void setThreadName(const std::string& s);
|
2016-04-12 04:00:33 -05:00
|
|
|
|
2017-03-30 12:14:40 -05:00
|
|
|
const char *getThreadName();
|
|
|
|
|
|
|
|
pid_t getThreadId();
|
|
|
|
|
2016-06-20 04:51:35 -05:00
|
|
|
/// Get version information
|
|
|
|
void getVersionInfo(std::string& version, std::string& hash);
|
2016-04-18 17:52:10 -05:00
|
|
|
|
2016-04-20 08:43:00 -05:00
|
|
|
/// Return a string that is unique across processes and calls.
|
|
|
|
std::string UniqueId();
|
|
|
|
|
2017-05-28 11:20:49 -05:00
|
|
|
// Extract all json entries into a map.
|
|
|
|
std::map<std::string, std::string> JsonToMap(const std::string& jsonString);
|
|
|
|
|
2016-10-09 10:27:01 -05:00
|
|
|
/// Trim spaces from the left. Just spaces.
|
2016-10-29 20:15:00 -05:00
|
|
|
inline std::string& ltrim(std::string& s)
|
2016-10-09 10:27:01 -05:00
|
|
|
{
|
2016-10-13 01:19:53 -05:00
|
|
|
const auto pos = s.find_first_not_of(' ');
|
2016-10-09 10:27:01 -05:00
|
|
|
if (pos != std::string::npos)
|
|
|
|
{
|
|
|
|
s = s.substr(pos);
|
|
|
|
}
|
2016-10-21 12:01:13 -05:00
|
|
|
|
|
|
|
return s;
|
2016-10-09 10:27:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Trim spaces from the left and copy. Just spaces.
|
2016-10-29 20:15:00 -05:00
|
|
|
inline std::string ltrimmed(const std::string& s)
|
2016-10-09 10:27:01 -05:00
|
|
|
{
|
2016-10-13 01:19:53 -05:00
|
|
|
const auto pos = s.find_first_not_of(' ');
|
2016-10-09 10:27:01 -05:00
|
|
|
if (pos != std::string::npos)
|
|
|
|
{
|
|
|
|
return s.substr(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2016-12-17 08:55:28 -06:00
|
|
|
/// Trim spaces from both left and right. Just spaces.
|
|
|
|
inline std::string& trim(std::string& s)
|
|
|
|
{
|
|
|
|
const auto first = s.find_first_not_of(' ');
|
|
|
|
const auto last = s.find_last_not_of(' ');
|
|
|
|
if (first != std::string::npos)
|
|
|
|
{
|
|
|
|
if (last != std::string::npos)
|
|
|
|
{
|
|
|
|
s = s.substr(first, last + 1 - first);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
s = s.substr(first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (last != std::string::npos)
|
|
|
|
{
|
|
|
|
s = s.substr(0, last + 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
s.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Trim spaces from both left and right and copy. Just spaces.
|
|
|
|
inline std::string trimmed(const std::string& s)
|
|
|
|
{
|
|
|
|
const auto first = s.find_first_not_of(' ');
|
|
|
|
const auto last = s.find_last_not_of(' ');
|
|
|
|
if (first != std::string::npos)
|
|
|
|
{
|
|
|
|
if (last != std::string::npos)
|
|
|
|
{
|
|
|
|
return s.substr(first, last + 1 - first);
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.substr(first);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last != std::string::npos)
|
|
|
|
{
|
|
|
|
return s.substr(0, last + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::string();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Trim spaces from left and right. Just spaces.
|
|
|
|
inline std::string trimmed(const char* s)
|
|
|
|
{
|
|
|
|
return trimmed(std::string(s));
|
|
|
|
}
|
|
|
|
|
2016-04-18 17:52:10 -05:00
|
|
|
/// Given one or more patterns to allow, and one or more to deny,
|
|
|
|
/// the match member will return true if, and only if, the subject
|
|
|
|
/// matches the allowed list, but not the deny.
|
|
|
|
/// By default, everything is denied.
|
|
|
|
class RegexListMatcher
|
|
|
|
{
|
|
|
|
public:
|
2016-08-04 17:44:24 -05:00
|
|
|
RegexListMatcher() :
|
|
|
|
_allowByDefault(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
RegexListMatcher(const bool allowByDefault) :
|
|
|
|
_allowByDefault(allowByDefault)
|
2016-04-18 20:11:33 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:35:36 -05:00
|
|
|
RegexListMatcher(std::initializer_list<std::string> allowed) :
|
2016-08-04 17:44:24 -05:00
|
|
|
_allowByDefault(false),
|
2016-04-19 02:35:36 -05:00
|
|
|
_allowed(allowed)
|
2016-04-18 20:11:33 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-04-19 02:35:36 -05:00
|
|
|
RegexListMatcher(std::initializer_list<std::string> allowed,
|
|
|
|
std::initializer_list<std::string> denied) :
|
2016-08-04 17:44:24 -05:00
|
|
|
_allowByDefault(false),
|
2016-04-19 02:35:36 -05:00
|
|
|
_allowed(allowed),
|
|
|
|
_denied(denied)
|
2016-04-18 20:11:33 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-08-04 17:44:24 -05:00
|
|
|
RegexListMatcher(const bool allowByDefault,
|
|
|
|
std::initializer_list<std::string> denied) :
|
|
|
|
_allowByDefault(allowByDefault),
|
|
|
|
_denied(denied)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-04-18 17:52:10 -05:00
|
|
|
void allow(const std::string& pattern) { _allowed.insert(pattern); }
|
|
|
|
void deny(const std::string& pattern)
|
|
|
|
{
|
|
|
|
_allowed.erase(pattern);
|
|
|
|
_denied.insert(pattern);
|
|
|
|
}
|
|
|
|
|
2016-04-18 20:11:33 -05:00
|
|
|
void clear()
|
|
|
|
{
|
|
|
|
_allowed.clear();
|
|
|
|
_denied.clear();
|
|
|
|
}
|
|
|
|
|
2016-04-18 17:52:10 -05:00
|
|
|
bool match(const std::string& subject) const
|
|
|
|
{
|
2016-10-29 20:15:00 -05:00
|
|
|
return (_allowByDefault || match(_allowed, subject)) && !match(_denied, subject);
|
2016-04-18 17:52:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2016-12-23 00:42:35 -06:00
|
|
|
static bool match(const std::set<std::string>& set, const std::string& subject)
|
2016-04-18 17:52:10 -05:00
|
|
|
{
|
|
|
|
if (set.find(subject) != set.end())
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not a perfect match, try regex.
|
|
|
|
for (const auto& value : set)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// Not performance critical to warrant caching.
|
2016-09-27 09:59:29 -05:00
|
|
|
Poco::RegularExpression re(value, Poco::RegularExpression::RE_CASELESS);
|
2016-09-27 10:07:52 -05:00
|
|
|
Poco::RegularExpression::Match reMatch;
|
2016-04-18 17:52:10 -05:00
|
|
|
|
|
|
|
// Must be a full match.
|
2016-09-27 09:59:29 -05:00
|
|
|
if (re.match(subject, reMatch) && reMatch.offset == 0 && reMatch.length == subject.size())
|
2016-04-18 17:52:10 -05:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const std::exception& exc)
|
|
|
|
{
|
|
|
|
// Nothing to do; skip.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2016-08-04 17:44:24 -05:00
|
|
|
const bool _allowByDefault;
|
2016-04-18 17:52:10 -05:00
|
|
|
std::set<std::string> _allowed;
|
|
|
|
std::set<std::string> _denied;
|
|
|
|
};
|
|
|
|
|
2016-08-13 23:01:45 -05:00
|
|
|
/// A logical constant that is allowed to initialize
|
|
|
|
/// exactly once and checks usage before initialization.
|
2016-10-29 20:15:00 -05:00
|
|
|
template <typename T>
|
2016-07-19 04:07:07 -05:00
|
|
|
class RuntimeConstant
|
2016-07-18 06:45:36 -05:00
|
|
|
{
|
2016-07-28 01:39:03 -05:00
|
|
|
T _value;
|
2016-08-13 23:01:45 -05:00
|
|
|
std::atomic<bool> _initialized;
|
2016-07-18 06:45:36 -05:00
|
|
|
|
|
|
|
public:
|
2016-07-19 04:07:07 -05:00
|
|
|
RuntimeConstant()
|
2016-07-28 01:39:03 -05:00
|
|
|
: _value()
|
|
|
|
, _initialized(false)
|
2016-08-13 23:01:45 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Use a compile-time const instead.
|
|
|
|
RuntimeConstant(const T& value) = delete;
|
2016-07-18 06:45:36 -05:00
|
|
|
|
|
|
|
const T& get()
|
|
|
|
{
|
2016-08-13 23:01:45 -05:00
|
|
|
if (_initialized)
|
2016-07-18 06:45:36 -05:00
|
|
|
{
|
2016-07-28 01:39:03 -05:00
|
|
|
return _value;
|
2016-07-18 06:45:36 -05:00
|
|
|
}
|
2016-08-13 23:01:45 -05:00
|
|
|
|
|
|
|
throw std::runtime_error("RuntimeConstant instance read before being initialized.");
|
2016-07-18 06:45:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void set(const T& value)
|
|
|
|
{
|
2016-07-28 01:39:03 -05:00
|
|
|
assert(!_initialized);
|
2016-07-19 04:07:07 -05:00
|
|
|
|
2016-07-28 01:39:03 -05:00
|
|
|
_initialized = true;
|
|
|
|
_value = value;
|
2016-07-18 06:45:36 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end namespace Util
|
2015-03-17 18:56:15 -05:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|