Add class 'BufferUtility'.
This commit is contained in:
parent
e1558d53a2
commit
75929a843e
@ -1,3 +1,5 @@
|
||||
find_package(Boost COMPONENTS url REQUIRED)
|
||||
|
||||
add_library(HttpProxy
|
||||
NetworkUtility.h NetworkUtility.cpp
|
||||
ProxyHttpSession.h ProxyHttpSession.cpp
|
||||
@ -11,8 +13,11 @@ add_library(HttpProxy
|
||||
|
||||
target_include_directories(HttpProxy
|
||||
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PRIVATE ${OPENSSL_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(HttpProxy
|
||||
PUBLIC ${Boost_LIBRARIES}
|
||||
PUBLIC Universal
|
||||
PRIVATE ${OpenSSL_LIBRARY}
|
||||
)
|
98
Universal/BufferUtility.cpp
Normal file
98
Universal/BufferUtility.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
#include "BufferUtility.h"
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
namespace Amass {
|
||||
const char CRLF[] = "\r\n";
|
||||
const char CRLFCRLF[] = "\r\n\r\n";
|
||||
|
||||
Buffer::Buffer(size_t initialSize) : m_buffer(initialSize) {
|
||||
}
|
||||
|
||||
void Buffer::swap(Buffer &buffer) {
|
||||
m_buffer.swap(buffer.m_buffer);
|
||||
std::swap(m_readerIndex, buffer.m_readerIndex);
|
||||
std::swap(m_writerIndex, buffer.m_writerIndex);
|
||||
}
|
||||
|
||||
int Buffer::lastError() {
|
||||
return m_lastError;
|
||||
}
|
||||
|
||||
void Buffer::append(const char *data, size_t len) {
|
||||
if (writableBytes() < len + 1)
|
||||
makeSpace(len + 1); // 剩下的可写空间如果小于需要的空间len+1,则增加len+1长度个空间,[len+1]用来加上\0进行截断
|
||||
std::memcpy(writableAddress(), data, len);
|
||||
std::copy(data, data + len, writableAddress()); // 其实相当于把已有数据往前挪动
|
||||
m_writerIndex += len;
|
||||
// 当Buffer以readableAddress()开始直接作为字符串直接使用时,在此加上\0截断,否则随机会打印出writableAddress()后的内容
|
||||
m_buffer[m_writerIndex] = '\0';
|
||||
}
|
||||
|
||||
void Buffer::appendFileContent(const std::string_view &path) {
|
||||
if (path.empty() || !std::filesystem::exists(path)) return;
|
||||
std::ifstream ifs(path.data(), std::ifstream::binary);
|
||||
char buffer[512];
|
||||
while (ifs.read(buffer, sizeof(buffer))) {
|
||||
append(buffer, ifs.gcount());
|
||||
}
|
||||
auto size = ifs.gcount();
|
||||
if (size > 0) append(buffer, size);
|
||||
}
|
||||
|
||||
void Buffer::makeSpace(size_t needSize) {
|
||||
if ((writableBytes() + m_readerIndex) < needSize) { // 即使数据前移,空间还是不足
|
||||
auto remainSize = readableBytes();
|
||||
auto newSize = remainSize + needSize + 1024;
|
||||
std::vector<char> newBuffer(newSize);
|
||||
memcpy(newBuffer.data(), readableAddress(), remainSize);
|
||||
m_buffer.swap(newBuffer);
|
||||
m_readerIndex = 0;
|
||||
m_writerIndex = remainSize;
|
||||
} else { // 将数据前移,空间满足要求
|
||||
auto remainSize = readableBytes();
|
||||
auto dst = m_buffer.data();
|
||||
auto src = readableAddress();
|
||||
for (size_t i = 0; i < remainSize; i++) {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
m_readerIndex = 0;
|
||||
m_writerIndex = remainSize;
|
||||
}
|
||||
}
|
||||
const char *Buffer::findCRLF() const {
|
||||
const char *crlf = std::search(readableAddress(), writableAddress(), CRLF, CRLF + 2);
|
||||
return crlf == writableAddress() ? nullptr : crlf;
|
||||
}
|
||||
|
||||
const char *Buffer::findCRLFCRLF() const {
|
||||
auto crlf = std::search(readableAddress(), writableAddress(), CRLFCRLF, CRLFCRLF + 4);
|
||||
return crlf == writableAddress() ? nullptr : crlf;
|
||||
}
|
||||
|
||||
const char *Buffer::findEOL(const char *start) const {
|
||||
if (start == nullptr) start = readableAddress();
|
||||
if ((readableAddress() > start) || (start >= writableAddress())) return nullptr;
|
||||
|
||||
const void *eol = memchr(start, '\n', static_cast<size_t>(writableAddress() - start));
|
||||
return static_cast<const char *>(eol);
|
||||
}
|
||||
|
||||
bool Buffer::retrieve(size_t len) {
|
||||
if (len > readableBytes()) return false;
|
||||
if (len < readableBytes()) {
|
||||
m_readerIndex += len;
|
||||
} else {
|
||||
retrieveAll();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Buffer::retrieveUntil(const char *end) {
|
||||
if (((uint64_t)readableAddress() > (uint64_t)end) || ((uint64_t)end > (uint64_t)writableAddress())) return false;
|
||||
return retrieve(static_cast<size_t>(end - readableAddress()));
|
||||
}
|
||||
|
||||
} // namespace Amass
|
213
Universal/BufferUtility.h
Normal file
213
Universal/BufferUtility.h
Normal file
@ -0,0 +1,213 @@
|
||||
#ifndef __BUFFERUTILITY_H__
|
||||
#define __BUFFERUTILITY_H__
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace Amass {
|
||||
|
||||
/**
|
||||
* @brief The Buffer class A buffer class modeled after org.jboss.netty.buffer.ChannelBuffer
|
||||
* +------------------+------------------+
|
||||
* | readable bytes | writable bytes |
|
||||
* | (CONTENT) | |
|
||||
* +------------------+------------------+
|
||||
* | | |
|
||||
* 0 <=readerIndex <= writerIndex <= size
|
||||
*/
|
||||
class Buffer {
|
||||
public:
|
||||
Buffer(size_t initialSize = 1024);
|
||||
void swap(Buffer &buffer);
|
||||
|
||||
int lastError();
|
||||
|
||||
inline const char *readableAddress() const {
|
||||
return m_buffer.data() + m_readerIndex;
|
||||
}
|
||||
inline size_t readableBytes() const {
|
||||
return m_writerIndex - m_readerIndex;
|
||||
}
|
||||
|
||||
void append(const char *data, size_t len);
|
||||
inline void append(std::string_view data) {
|
||||
append(data.data(), data.size());
|
||||
}
|
||||
|
||||
void appendFileContent(const std::string_view &path);
|
||||
|
||||
/**
|
||||
* @brief peek must POD struct
|
||||
* @param fd
|
||||
* @return [errno,size]
|
||||
*/
|
||||
template <typename T>
|
||||
T peek() {
|
||||
T t;
|
||||
memcpy(&t, readableAddress(), sizeof(T));
|
||||
return t;
|
||||
}
|
||||
|
||||
inline std::string peek(size_t size, bool retrieved = false) {
|
||||
std::string ret(readableAddress(), size);
|
||||
if (retrieved) retrieve(size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief findCRLF
|
||||
* @param start if(start == nullptr),find CRLF from readableAddress.
|
||||
* @return
|
||||
*/
|
||||
const char *findCRLF() const;
|
||||
|
||||
const char *findCRLFCRLF() const;
|
||||
|
||||
template <typename T>
|
||||
const char *find(const T &data, size_t startPos = 0) {
|
||||
if (startPos >= readableBytes()) return nullptr;
|
||||
const char *pos = nullptr;
|
||||
if constexpr (!std::is_array_v<T>) {
|
||||
if (startPos + sizeof(T) > readableBytes()) return nullptr;
|
||||
pos = std::search(readableAddress() + startPos, (const char *)writableAddress(), (const char *)(&data),
|
||||
(const char *)(&data));
|
||||
} else {
|
||||
if (startPos + sizeof(T) * std::extent_v < T >> readableBytes()) return nullptr;
|
||||
pos = std::search(readableAddress() + startPos, (const char *)writableAddress(), (const char *)(&data[0]),
|
||||
(const char *)(&data[std::extent_v<T>]));
|
||||
}
|
||||
|
||||
return pos == writableAddress() ? nullptr : pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief findEOL
|
||||
* @param start if(start == nullptr),find EOL from readableAddress.
|
||||
* @return
|
||||
*/
|
||||
const char *findEOL(const char *start = nullptr) const;
|
||||
|
||||
/**
|
||||
* @brief retrieve retrieve returns void, to prevent string str(retrieve(readableBytes()),
|
||||
* readableBytes()); the evaluation of two functions are unspecified
|
||||
* @param len
|
||||
* @return
|
||||
*/
|
||||
bool retrieve(size_t len);
|
||||
|
||||
/**
|
||||
* @brief retrieveUntil retrive [begin,end)
|
||||
* @param end
|
||||
* @return
|
||||
*/
|
||||
bool retrieveUntil(const char *end);
|
||||
|
||||
/**
|
||||
* @brief retrieveAll
|
||||
* @param Return[template parameter] void,std::string,std::vector<char>
|
||||
* @return
|
||||
*/
|
||||
template <typename ReturnType = void>
|
||||
typename std::conditional_t<std::is_same_v<ReturnType, void>, void, ReturnType> retrieveAll() {
|
||||
if constexpr (std::is_same_v<ReturnType, std::string>) {
|
||||
std::string data(m_buffer.cbegin() + static_cast<int64_t>(m_readerIndex),
|
||||
m_buffer.cbegin() + static_cast<int64_t>(m_writerIndex));
|
||||
m_readerIndex = 0;
|
||||
m_writerIndex = 0;
|
||||
m_buffer[m_readerIndex] = '\0';
|
||||
return data;
|
||||
} else if constexpr (std::is_same_v<ReturnType, std::vector<char>>) {
|
||||
std::vector<char> data(m_buffer.cbegin() + static_cast<int64_t>(m_readerIndex),
|
||||
m_buffer.cbegin() + static_cast<int64_t>(m_writerIndex));
|
||||
m_readerIndex = 0;
|
||||
m_writerIndex = 0;
|
||||
m_buffer[m_readerIndex] = '\0';
|
||||
return data;
|
||||
} else if constexpr (std::is_same_v<ReturnType, void>) {
|
||||
m_readerIndex = 0;
|
||||
m_writerIndex = 0;
|
||||
m_buffer[m_readerIndex] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief makeSpace 增加m_buffer的writeable区域容量,或者将可读数据前移
|
||||
* @param len
|
||||
*/
|
||||
void makeSpace(size_t len);
|
||||
|
||||
inline size_t writableBytes() const {
|
||||
return m_buffer.size() - m_writerIndex;
|
||||
}
|
||||
inline const char *writableAddress() const {
|
||||
return m_buffer.data() + m_writerIndex;
|
||||
}
|
||||
inline char *writableAddress() {
|
||||
return m_buffer.data() + m_writerIndex;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<char> m_buffer;
|
||||
size_t m_readerIndex = 0;
|
||||
size_t m_writerIndex = 0;
|
||||
int m_lastError;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class VectorView {
|
||||
public:
|
||||
using size_type = std::size_t;
|
||||
using const_reference = const T &;
|
||||
VectorView(const T *ptr, size_type length) : m_data(ptr), m_length(length) {
|
||||
}
|
||||
constexpr size_type size() const noexcept {
|
||||
return m_length;
|
||||
}
|
||||
constexpr size_type length() const noexcept {
|
||||
return m_length;
|
||||
}
|
||||
constexpr const_reference front() const {
|
||||
return m_data[0];
|
||||
}
|
||||
constexpr const_reference back() const {
|
||||
return m_data[size() - 1];
|
||||
}
|
||||
constexpr const_reference at(size_type pos) const {
|
||||
return m_data[pos];
|
||||
}
|
||||
|
||||
private:
|
||||
const T *m_data;
|
||||
size_type m_length;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class BufferView {
|
||||
public:
|
||||
BufferView() {
|
||||
}
|
||||
BufferView(const T *data, size_t size) : m_data(data), m_length(size), m_current(data) {
|
||||
}
|
||||
bool empty() const {
|
||||
return m_data == nullptr || (m_current > (m_data + m_length));
|
||||
}
|
||||
size_t size() const {
|
||||
return m_data + m_length - m_current;
|
||||
}
|
||||
void read(T *buffer, size_t size) {
|
||||
std::copy(m_current, m_current + size, buffer);
|
||||
m_current += size;
|
||||
}
|
||||
|
||||
private:
|
||||
const T *m_data = nullptr;
|
||||
size_t m_length = 0;
|
||||
const T *m_current = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Amass
|
||||
|
||||
#endif // __BUFFERUTILITY_H__
|
@ -1,12 +1,16 @@
|
||||
find_package(Boost REQUIRED COMPONENTS log log_setup)
|
||||
find_package(Boost REQUIRED COMPONENTS log log_setup program_options)
|
||||
|
||||
add_library(Universal
|
||||
BoostLog.h BoostLog.inl BoostLog.cpp
|
||||
BufferUtility.h BufferUtility.cpp
|
||||
CommandLineInterpreter.h CommandLineInterpreter.cpp
|
||||
DateTime.h DateTime.cpp
|
||||
FunctionTraits.h
|
||||
IoContext.h IoContext.cpp
|
||||
MessageManager.h MessageManager.inl MessageManager.cpp
|
||||
ProcessUtility.h ProcessUtility.inl ProcessUtility.cpp
|
||||
Singleton.h
|
||||
SingletonProcess.h SingletonProcess.cpp
|
||||
StreamFormat.h StreamFormat.inl StreamFormat.cpp
|
||||
StringUtility.h StringUtility.cpp
|
||||
)
|
||||
|
101
Universal/CommandLineInterpreter.cpp
Normal file
101
Universal/CommandLineInterpreter.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
#include "CommandLineInterpreter.h"
|
||||
#include "BoostLog.h"
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
#include <iostream>
|
||||
|
||||
CommandLineInterpreter::CommandLineInterpreter(const DescriptionPointer &description, const std::string &prompt)
|
||||
: m_description(description), m_prompt(prompt) {}
|
||||
|
||||
void CommandLineInterpreter::interpret(std::istream &inputStream) {
|
||||
std::string command;
|
||||
std::cout << m_prompt << std::flush;
|
||||
|
||||
while (std::getline(inputStream, command, '\n')) {
|
||||
handleReadLine(command);
|
||||
std::cout << m_prompt << std::flush;
|
||||
}
|
||||
}
|
||||
|
||||
void CommandLineInterpreter::handleReadLine(std::string line) {
|
||||
using namespace boost::program_options;
|
||||
if (m_description.expired()) {
|
||||
LOG(error) << "description has expired.";
|
||||
return;
|
||||
}
|
||||
auto description = m_description.lock();
|
||||
std::vector<std::string> args;
|
||||
|
||||
// huu, ugly...
|
||||
args = splitCommandLine(std::string("--") + line);
|
||||
|
||||
try {
|
||||
variables_map vm;
|
||||
store(command_line_parser(args).options(*description).run(), vm);
|
||||
notify(vm);
|
||||
} catch (boost::program_options::unknown_option &e) {
|
||||
std::cerr << "error: " << e.what() << std::endl;
|
||||
} catch (boost::program_options::invalid_command_line_syntax &e) {
|
||||
std::cerr << "error: " << e.what() << std::endl;
|
||||
} catch (boost::program_options::validation_error &e) {
|
||||
std::cerr << "error: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> CommandLineInterpreter::splitCommandLine(const std::string &input) {
|
||||
std::vector<std::string> result;
|
||||
|
||||
std::string::const_iterator i = input.begin(), e = input.end();
|
||||
for (; i != e; ++i)
|
||||
if (!isspace((unsigned char)*i)) break;
|
||||
|
||||
if (i != e) {
|
||||
std::string current;
|
||||
bool inside_quoted = false;
|
||||
int backslash_count = 0;
|
||||
|
||||
for (; i != e; ++i) {
|
||||
if (*i == '"') {
|
||||
// '"' preceded by even number (n) of backslashes generates
|
||||
// n/2 backslashes and is a quoted block delimiter
|
||||
if (backslash_count % 2 == 0) {
|
||||
current.append(backslash_count / 2, '\\');
|
||||
inside_quoted = !inside_quoted;
|
||||
// '"' preceded by odd number (n) of backslashes generates
|
||||
// (n-1)/2 backslashes and is literal quote.
|
||||
} else {
|
||||
current.append(backslash_count / 2, '\\');
|
||||
current += '"';
|
||||
}
|
||||
backslash_count = 0;
|
||||
} else if (*i == '\\') {
|
||||
++backslash_count;
|
||||
} else {
|
||||
// Not quote or backslash. All accumulated backslashes should be
|
||||
// added
|
||||
if (backslash_count) {
|
||||
current.append(backslash_count, '\\');
|
||||
backslash_count = 0;
|
||||
}
|
||||
if (isspace((unsigned char)*i) && !inside_quoted) {
|
||||
// Space outside quoted section terminate the current argument
|
||||
result.push_back(current);
|
||||
current.resize(0);
|
||||
for (; i != e && isspace((unsigned char)*i); ++i)
|
||||
;
|
||||
--i;
|
||||
} else {
|
||||
current += *i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have trailing backslashes, add them
|
||||
if (backslash_count) current.append(backslash_count, '\\');
|
||||
|
||||
// If we have non-empty 'current' or we're still in quoted
|
||||
// section (even if 'current' is empty), add the last token.
|
||||
if (!current.empty() || inside_quoted) result.push_back(current);
|
||||
}
|
||||
return result;
|
||||
}
|
23
Universal/CommandLineInterpreter.h
Normal file
23
Universal/CommandLineInterpreter.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef COMMANDLINEINTERPRETER_H
|
||||
#define COMMANDLINEINTERPRETER_H
|
||||
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <memory>
|
||||
|
||||
class CommandLineInterpreter {
|
||||
public:
|
||||
using DescriptionPointer = std::shared_ptr<boost::program_options::options_description>;
|
||||
using DescriptionWeakPointer = std::weak_ptr<boost::program_options::options_description>;
|
||||
CommandLineInterpreter(const DescriptionPointer &description, const std::string &prompt);
|
||||
void interpret(std::istream &inputStream);
|
||||
|
||||
protected:
|
||||
void handleReadLine(std::string line);
|
||||
static std::vector<std::string> splitCommandLine(const std::string &input);
|
||||
|
||||
private:
|
||||
const DescriptionWeakPointer m_description;
|
||||
std::string m_prompt;
|
||||
};
|
||||
|
||||
#endif // COMMANDLINEINTERPRETER_H
|
200
Universal/ProcessUtility.cpp
Normal file
200
Universal/ProcessUtility.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
#include "ProcessUtility.h"
|
||||
#include "BoostLog.h"
|
||||
#include "boost/algorithm/string.hpp"
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <boost/winapi/dll.hpp>
|
||||
#endif
|
||||
|
||||
bool DockerUtility::supervisorStatus(std::string_view container, const std::string &module) {
|
||||
using namespace boost::process;
|
||||
boost::process::ipstream ips;
|
||||
auto docker = search_path("docker");
|
||||
auto ctl = boost::process::search_path("supervisorctl");
|
||||
auto env = boost::this_process::environment();
|
||||
if (env.count("USER")) {
|
||||
auto user = env["USER"].to_string();
|
||||
system(docker, args = {"exec", "-u", user, container.data(), "supervisorctl", "status", module}, std_out > ips);
|
||||
std::string line;
|
||||
if (!ips) return false;
|
||||
std::getline(ips, line);
|
||||
if (line.empty()) return false;
|
||||
boost::algorithm::trim(line);
|
||||
std::vector<std::string> s;
|
||||
boost::algorithm::split(s, line, boost::algorithm::is_any_of(" "), boost::algorithm::token_compress_on);
|
||||
if (s.size() < 3) return false;
|
||||
return s.at(2) == "RUNNING";
|
||||
} else {
|
||||
LOG(error) << "this_process did not contain USER env value.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DockerUtility::running(std::string_view container, std::error_code &error) {
|
||||
#ifdef __linux__
|
||||
using namespace boost::process;
|
||||
boost::process::ipstream stream;
|
||||
std::ostringstream oss;
|
||||
oss << "docker ps -q --filter \"name=" << container << "\" --filter \"status=running\"";
|
||||
system(oss.str(), boost::process::std_out > stream, error);
|
||||
if (error) return false;
|
||||
bool ret = false;
|
||||
std::string line;
|
||||
while (std::getline(stream, line) && !line.empty()) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string NativeUtility::currentExecutable() {
|
||||
// win32 GetModuleFileNameA
|
||||
#ifdef __linux__
|
||||
char buffer[512] = {0};
|
||||
auto status = readlink("/proc/self/exe", buffer, sizeof(buffer));
|
||||
return status == -1 ? std::string() : std::string(buffer);
|
||||
#else
|
||||
char buffer[MAX_PATH];
|
||||
boost::winapi::get_module_file_name(nullptr, buffer, MAX_PATH);
|
||||
return buffer;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string NativeUtility::executableDirectory() {
|
||||
auto path = currentExecutable();
|
||||
#ifdef __linux__
|
||||
auto slashPos = path.find_last_of("/");
|
||||
#else
|
||||
auto slashPos = path.find_last_of("\\");
|
||||
#endif
|
||||
return path.substr(0, slashPos);
|
||||
}
|
||||
|
||||
std::string NativeUtility::applicationDataDirectory(bool create) {
|
||||
std::string directory;
|
||||
|
||||
#ifdef WIN32
|
||||
DWORD bufferSize = GetEnvironmentVariable("APPDATA", 0, 0);
|
||||
char *buffer = new char[bufferSize + 2];
|
||||
GetEnvironmentVariable("APPDATA", buffer, bufferSize);
|
||||
directory.assign(buffer);
|
||||
delete[] buffer;
|
||||
#else
|
||||
const char *result = std::getenv("XDG_CONFIG_HOME");
|
||||
if (result && strlen(result)) {
|
||||
directory = std::string(result);
|
||||
} else {
|
||||
directory = std::string(std::getenv("HOME")) + std::string("/.config/");
|
||||
}
|
||||
#endif
|
||||
auto exePath = currentExecutable();
|
||||
auto slashPos = exePath.find_last_of("\\");
|
||||
auto dotPos = exePath.find_last_of(".");
|
||||
auto appName = exePath.substr(slashPos + 1, dotPos - slashPos - 1);
|
||||
std::ostringstream oss;
|
||||
oss << directory << "/" << appName << "/";
|
||||
directory = oss.str();
|
||||
if (create) {
|
||||
try {
|
||||
std::filesystem::path path(std::filesystem::u8path(directory));
|
||||
if (!std::filesystem::exists(path)) {
|
||||
std::filesystem::create_directories(path);
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
return directory;
|
||||
}
|
||||
|
||||
std::string NativeUtility::homeDirectory() {
|
||||
std::string directory;
|
||||
#ifdef WIN32
|
||||
DWORD bufferSize = GetEnvironmentVariable("USERPROFILE", 0, 0);
|
||||
char *buffer = new char[bufferSize + 2];
|
||||
GetEnvironmentVariable("USERPROFILE", buffer, bufferSize);
|
||||
directory.assign(buffer);
|
||||
delete[] buffer;
|
||||
#else
|
||||
directory = std::string(std::getenv("HOME"));
|
||||
#endif
|
||||
return directory;
|
||||
}
|
||||
|
||||
bool NativeUtility::processRunning(const std::string &nameFilter) {
|
||||
boost::process::ipstream ips;
|
||||
// for unknow reason,on gdb debug mode,the columns is very short.
|
||||
boost::process::system("ps -eF --columns 999", boost::process::std_out > ips);
|
||||
std::string line;
|
||||
std::regex regex(nameFilter);
|
||||
while (std::getline(ips, line)) {
|
||||
if (line.empty()) continue;
|
||||
// LOG(debug) << "processRunning: " << line;
|
||||
if (std::regex_search(line, regex)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DockerUtility::executeBashCommand(const std::string &command) {
|
||||
std::ostringstream oss;
|
||||
oss << "bash -c \"" << command << "\"";
|
||||
boost::process::system("bash -c \" \"");
|
||||
|
||||
// ps -aux | grep rosbridge | awk '{print $2}' | xargs kill -s 9 2>/dev/null
|
||||
}
|
||||
|
||||
void DockerUtility::execute(const std::string_view &container, const std::string_view &command) {
|
||||
std::ostringstream oss;
|
||||
auto env = boost::this_process::environment();
|
||||
auto user = env["USER"].to_string();
|
||||
oss << "docker exec -u " << user << " -d " << container << " " << command;
|
||||
using namespace boost::process;
|
||||
system(oss.str());
|
||||
}
|
||||
|
||||
void DockerUtility::kill(const std::string &nameFilter) {
|
||||
std::ostringstream oss;
|
||||
oss << "ps -aux | grep " << nameFilter << " | awk '{print $2}' | xargs kill -s 9 2>/dev/null";
|
||||
executeBashCommand(oss.str());
|
||||
}
|
||||
|
||||
bool DockerUtility::containerExisted(const std::string_view &name) {
|
||||
boost::process::ipstream ips;
|
||||
std::ostringstream oss;
|
||||
oss << "docker ps -aq --filter name=" << name;
|
||||
boost::process::system(oss.str(), boost::process::std_out > ips);
|
||||
std::string line;
|
||||
while (std::getline(ips, line)) {
|
||||
if (line.empty()) continue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DockerUtility::supervisorctl(const std::string_view &container, const std::string &module, bool isStart) {
|
||||
using namespace boost::process;
|
||||
auto docker = search_path("docker");
|
||||
auto env = boost::this_process::environment();
|
||||
auto user = env["USER"].to_string();
|
||||
system(docker, args = {"exec", "-u", user, container.data(), "supervisorctl", isStart ? "start" : "stop", module});
|
||||
}
|
||||
|
||||
void DockerUtility::stop(const std::string &nameFilter) {
|
||||
using namespace boost::process;
|
||||
ipstream ips;
|
||||
std::ostringstream oss;
|
||||
oss << "docker ps -q --filter \"name=" << nameFilter << "\" --filter \"status=running\" ";
|
||||
system(oss.str(), std_out > ips);
|
||||
std::string id;
|
||||
auto docker = search_path("docker");
|
||||
while (std::getline(ips, id) && !id.empty()) {
|
||||
system(docker, "stop", args += {id});
|
||||
}
|
||||
}
|
54
Universal/ProcessUtility.h
Normal file
54
Universal/ProcessUtility.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef DOCKERUTILITY_H
|
||||
#define DOCKERUTILITY_H
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
|
||||
/**
|
||||
* @brief used when app run on the native machine
|
||||
*/
|
||||
class DockerUtility {
|
||||
public:
|
||||
void stop(const std::string &nameFilter);
|
||||
static bool supervisorStatus(std::string_view container, const std::string &module);
|
||||
|
||||
template <typename... Args>
|
||||
static void runScript(std::string_view container, const std::string &script, Args &&...scriptArgs);
|
||||
|
||||
static bool running(std::string_view container, std::error_code &error);
|
||||
static void executeBashCommand(const std::string &command);
|
||||
static void execute(const std::string_view &container, const std::string_view &command);
|
||||
static void kill(const std::string &nameFilter);
|
||||
static bool containerExisted(const std::string_view &name);
|
||||
|
||||
/**
|
||||
* @brief supervisorctl
|
||||
* @param module
|
||||
* @param isStart true:start,false:stop
|
||||
*/
|
||||
static void supervisorctl(const std::string_view &container, const std::string &module, bool isStart);
|
||||
|
||||
private:
|
||||
DockerUtility() = default;
|
||||
};
|
||||
|
||||
class NativeUtility {
|
||||
public:
|
||||
using ShellCommand = std::true_type;
|
||||
template <typename... Args>
|
||||
static void runScript(const std::string &script, Args &&...scriptArgs);
|
||||
static bool processRunning(const std::string &nameFilter);
|
||||
static std::string currentExecutable();
|
||||
static std::string executableDirectory();
|
||||
static std::string applicationDataDirectory(bool create = true);
|
||||
static std::string homeDirectory();
|
||||
|
||||
private:
|
||||
NativeUtility() = default;
|
||||
};
|
||||
|
||||
#include "ProcessUtility.inl"
|
||||
|
||||
#endif // DOCKERUTILITY_H
|
44
Universal/ProcessUtility.inl
Normal file
44
Universal/ProcessUtility.inl
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef __PROCESSUTILITY_INL__
|
||||
#define __PROCESSUTILITY_INL__
|
||||
|
||||
#include "BoostLog.h"
|
||||
#include "ProcessUtility.h"
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/process.hpp>
|
||||
#if (defined __arm__) || (defined __aarch64__)
|
||||
#include <experimental/filesystem>
|
||||
#else
|
||||
#include <filesystem>
|
||||
#endif
|
||||
|
||||
template <typename... Args>
|
||||
void DockerUtility::runScript(std::string_view container, const std::string &script, Args &&...scriptArgs) {
|
||||
using namespace boost::process;
|
||||
auto env = boost::this_process::environment();
|
||||
auto user = env["USER"].to_string();
|
||||
auto docker = search_path("docker");
|
||||
system(docker,
|
||||
args = {"exec", "-d", "-u", user, container.data(), "bash", script, std::forward<Args>(scriptArgs)...});
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void NativeUtility::runScript(const std::string &script, Args &&...scriptArgs) {
|
||||
#if (defined __arm__) || (defined __aarch64__)
|
||||
using namespace std::experimental;
|
||||
#else
|
||||
using namespace std;
|
||||
#endif
|
||||
|
||||
#if defined(WIN32) || defined(ANDROID)
|
||||
LOG(info) << "DockerUtility::runScript() not supported on windows or android.";
|
||||
#else
|
||||
auto askpass = filesystem::current_path().parent_path() / "Askpass/Askpassd.AppImage";
|
||||
auto env = boost::this_process::environment();
|
||||
env["SUDO_ASKPASS"] = askpass;
|
||||
|
||||
auto bash = boost::process::search_path("bash");
|
||||
boost::process::system(bash, boost::process::args = {script, std::forward<Args>(scriptArgs)...}, env);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // __PROCESSUTILITY_INL__
|
66
Universal/SingletonProcess.cpp
Normal file
66
Universal/SingletonProcess.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
#include "SingletonProcess.h"
|
||||
#include "BoostLog.h"
|
||||
#ifdef WIN32
|
||||
#include "Windows.h"
|
||||
#else
|
||||
#include <sys/file.h>
|
||||
#endif
|
||||
|
||||
SingletonProcess::SingletonProcess(const std::string_view &name) {
|
||||
#ifdef WIN32
|
||||
m_handle = CreateMutex(NULL, FALSE, name.data());
|
||||
if (m_handle) {
|
||||
if (ERROR_ALREADY_EXISTS == GetLastError()) {
|
||||
m_alreadyRun = true;
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::ostringstream oss;
|
||||
oss << "/tmp/" << name;
|
||||
int pidFile = open(oss.str().data(), O_CREAT | O_RDWR, 0666);
|
||||
m_handle = flock(pidFile, LOCK_EX | LOCK_NB);
|
||||
if (m_handle) {
|
||||
if (EWOULDBLOCK == errno) m_alreadyRun = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_alreadyRun) {
|
||||
boost::interprocess::message_queue::remove(name.data());
|
||||
m_messageQueue = std::make_unique<boost::interprocess::message_queue>(boost::interprocess::create_only,
|
||||
name.data(), 64, 1024);
|
||||
m_thread = std::make_unique<std::thread>(&SingletonProcess::run, this);
|
||||
} else {
|
||||
m_messageQueue =
|
||||
std::make_unique<boost::interprocess::message_queue>(boost::interprocess::open_only, name.data());
|
||||
}
|
||||
}
|
||||
|
||||
bool SingletonProcess::alreadyRun() const {
|
||||
return m_alreadyRun;
|
||||
}
|
||||
|
||||
SingletonProcess::~SingletonProcess() {
|
||||
m_exit = true;
|
||||
m_conditionVariable.notify_all();
|
||||
if (m_thread && m_thread->joinable()) m_thread->join();
|
||||
}
|
||||
|
||||
bool SingletonProcess::sendMessage(const char *data, size_t size) {
|
||||
if (!m_alreadyRun) return false;
|
||||
return m_messageQueue->try_send(data, size, 0);
|
||||
}
|
||||
|
||||
void SingletonProcess::run() {
|
||||
char buffer[1024];
|
||||
while (!m_exit) {
|
||||
unsigned int priority;
|
||||
boost::interprocess::message_queue::size_type receivedSize;
|
||||
bool status = m_messageQueue->try_receive(buffer, sizeof(buffer), receivedSize, priority);
|
||||
if (status) {
|
||||
messageHandler(buffer, receivedSize);
|
||||
} else {
|
||||
std::unique_lock locker(m_mutex);
|
||||
m_conditionVariable.wait_for(locker, std::chrono::milliseconds(500));
|
||||
}
|
||||
}
|
||||
}
|
39
Universal/SingletonProcess.h
Normal file
39
Universal/SingletonProcess.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef SINGLETONPROCESS_H
|
||||
#define SINGLETONPROCESS_H
|
||||
|
||||
#include <boost/interprocess/ipc/message_queue.hpp>
|
||||
#include <boost/signals2/signal.hpp>
|
||||
#include <condition_variable>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
|
||||
class SingletonProcess
|
||||
{
|
||||
public:
|
||||
using MessageHandler = boost::signals2::signal<void(const char *, size_t)>;
|
||||
using MessageHandlerSlot = MessageHandler::slot_type;
|
||||
SingletonProcess(const std::string_view &name);
|
||||
bool alreadyRun() const;
|
||||
MessageHandler messageHandler;
|
||||
~SingletonProcess();
|
||||
bool sendMessage(const char *data, size_t size);
|
||||
|
||||
protected:
|
||||
void run();
|
||||
|
||||
private:
|
||||
#if WIN32
|
||||
void *m_handle{nullptr}; // windows HANDLE
|
||||
#else
|
||||
int m_handle{-1};
|
||||
#endif
|
||||
bool m_alreadyRun{false};
|
||||
std::unique_ptr<boost::interprocess::message_queue> m_messageQueue;
|
||||
|
||||
std::unique_ptr<std::thread> m_thread;
|
||||
std::condition_variable m_conditionVariable;
|
||||
std::mutex m_mutex;
|
||||
bool m_exit{false};
|
||||
};
|
||||
|
||||
#endif // SINGLETONPROCESS_H
|
Loading…
Reference in New Issue
Block a user