2258 lines
47 KiB
C++
2258 lines
47 KiB
C++
#include "utils.h"
|
|
|
|
#include "ini_file.h"
|
|
|
|
|
|
#include <mutex>
|
|
#include <algorithm>
|
|
|
|
#if OS_WIN
|
|
#include <direct.h>
|
|
#include <Windows.h>
|
|
#include <time.h>
|
|
#include <Psapi.h>
|
|
#include <Tlhelp32.h>
|
|
#pragma comment(lib, "Psapi.lib")
|
|
|
|
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 // microseconds from '1601-01-01 00:00:00' to '1970-01-01 00:00:00'
|
|
|
|
int gettimeofday(TIMEV* tv, struct timezone* tz)
|
|
{
|
|
FILETIME ft = { 0 };
|
|
uint64_t ui64 = 0;
|
|
static bool set_tz = true;
|
|
|
|
GetSystemTimeAsFileTime(&ft); // 100 ns - from 1601-01-01 00:00:00
|
|
ui64 = ft.dwHighDateTime;
|
|
ui64 <<= 32;
|
|
ui64 |= ft.dwLowDateTime;
|
|
|
|
// convert to microseconds ...
|
|
ui64 += 5;
|
|
ui64 /= 10;
|
|
|
|
// move to 1970-01-01 00:00:00
|
|
ui64 -= DELTA_EPOCH_IN_MICROSECS;
|
|
if (tv)
|
|
{
|
|
tv->tv_sec = ui64 / 1000000;
|
|
tv->tv_usec = ui64 % 1000000;
|
|
}
|
|
|
|
if (tz)
|
|
{
|
|
if (set_tz)
|
|
{
|
|
set_tz = false;
|
|
_tzset();
|
|
}
|
|
tz->tz_minuteswest = _timezone / 60;
|
|
tz->tz_dsttime = _daylight;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
#include <map>
|
|
#include <sys/sysinfo.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <thread>
|
|
#include <iconv.h>
|
|
#include <sys/vfs.h>
|
|
#include <sys/types.h>
|
|
#include <sys/ipc.h>
|
|
#include <sys/shm.h>
|
|
#include <sys/mman.h>
|
|
|
|
#define PRODUCT_VENDOR "HuaGo"
|
|
|
|
static std::mutex ini_lock_;
|
|
static std::map<std::string, simple_ini*> ini_files_;
|
|
static std::string debug_cfg_file_ = "";
|
|
|
|
static void init_default_config_file_path(void)
|
|
{
|
|
if(debug_cfg_file_.empty())
|
|
{
|
|
debug_cfg_file_ = utils::get_local_data_path() + PATH_SEPARATOR + "config" + PATH_SEPARATOR + "debug.cfg";
|
|
}
|
|
}
|
|
static simple_ini* get_ini_object(const char* file)
|
|
{
|
|
if(!file || file[0] == 0)
|
|
{
|
|
init_default_config_file_path();
|
|
file = debug_cfg_file_.c_str();
|
|
}
|
|
|
|
{
|
|
std::lock_guard<std::mutex> lock(ini_lock_);
|
|
|
|
if (ini_files_.count(file) == 0)
|
|
{
|
|
simple_ini* ini = new simple_ini();
|
|
ini->load(file);
|
|
|
|
ini_files_[file] = ini;
|
|
}
|
|
}
|
|
|
|
return ini_files_[file];
|
|
}
|
|
|
|
DWORD GetPrivateProfileStringA(const char* lpAppName, const char* lpKeyName, const char* lpDefault, char* lpReturnedString, DWORD nSize, const char* lpFileName)
|
|
{
|
|
simple_ini* ini = get_ini_object(lpFileName);
|
|
std::string str(ini->get(lpAppName, lpKeyName, lpDefault));
|
|
|
|
if (nSize)
|
|
{
|
|
if (nSize < str.length())
|
|
{
|
|
memcpy(lpReturnedString, str.c_str(), nSize);
|
|
}
|
|
else
|
|
{
|
|
strcpy(lpReturnedString, str.c_str());
|
|
nSize = str.length();
|
|
}
|
|
}
|
|
|
|
return nSize;
|
|
}
|
|
|
|
BOOL WritePrivateProfileStringA(const char* lpAppName, const char* lpKeyName, const char* lpString, const char* lpFileName)
|
|
{
|
|
simple_ini* ini = get_ini_object(lpFileName);
|
|
|
|
ini->set(lpAppName, lpKeyName, lpString);
|
|
ini->save(lpFileName);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD GetLastError(void)
|
|
{
|
|
return errno;
|
|
}
|
|
DWORD GetPrivateProfileIntA(const char* app, const char* key, DWORD def, const char* file)
|
|
{
|
|
std::string val(get_ini_object(file)->get(app, key));
|
|
|
|
return val.empty() ? def : atoi(val.c_str());
|
|
}
|
|
DWORD GetPrivateProfileStringA(const char* app, const char* key, const char* init, char* buf, size_t len, const char* file)
|
|
{
|
|
std::string val(get_ini_object(file)->get(app, key));
|
|
|
|
if(val.empty())
|
|
{
|
|
if(init)
|
|
{
|
|
strcpy(buf, init);
|
|
len = strlen(init);
|
|
}
|
|
else
|
|
{
|
|
len = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(len < val.length())
|
|
memcpy(buf, val.c_str(), len);
|
|
else
|
|
{
|
|
strcpy(buf, val.c_str());
|
|
len = val.length();
|
|
}
|
|
}
|
|
|
|
return len;
|
|
}
|
|
void Sleep(DWORD milliseconds)
|
|
{
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
|
}
|
|
int GetModuleFileNameA(HMODULE module, char* buf, size_t len)
|
|
{
|
|
std::string name(""),
|
|
val(utils::get_module_full_path((char*)module));
|
|
|
|
val += PATH_SEPARATOR + name;
|
|
if(len < val.length())
|
|
memcpy(buf, val.c_str(), len);
|
|
else
|
|
{
|
|
strcpy(buf, val.c_str());
|
|
len = val.length();
|
|
}
|
|
|
|
return len;
|
|
}
|
|
uint64_t GetCurrentThreadId(void)
|
|
{
|
|
return (uint64_t)pthread_self();
|
|
}
|
|
uint64_t GetCurrentProcessId(void)
|
|
{
|
|
return (uint64_t)getpid();
|
|
}
|
|
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// log class
|
|
#define MAX_LOG_FILE_SIZE SIZE_MB(10)
|
|
|
|
class log_cls
|
|
{
|
|
void(*log_)(const char*, void*, void*);
|
|
std::string path_file_;
|
|
FILE* file_;
|
|
int level_;
|
|
int type_;
|
|
std::mutex lock_;
|
|
|
|
static log_cls* inst_;
|
|
|
|
static FILE* create_log_file(const char* path_file, bool truncate)
|
|
{
|
|
FILE* file_ = fopen(path_file, "a+b");
|
|
|
|
if (file_)
|
|
{
|
|
fseek(file_, 0, SEEK_END);
|
|
if (ftell(file_) == 0)
|
|
{
|
|
unsigned char bom[] = { 0x0ef, 0x0bb, 0x0bf };
|
|
fwrite(bom, sizeof(bom), 1, file_);
|
|
}
|
|
else
|
|
{
|
|
std::string sep("\n\n========================================================================================================================\n");
|
|
fwrite(sep.c_str(), sizeof(sep[0]), sep.length(), file_);
|
|
}
|
|
|
|
std::string now("[" + utils::format_current_time() + "]: =====================================");
|
|
now += truncate ? "--truncated--=====================================\n" : "--started--" + std::to_string(GetCurrentProcessId()) + "=====================================\n";
|
|
fwrite(now.c_str(), sizeof(now[0]), now.length(), file_);
|
|
}
|
|
|
|
return file_;
|
|
}
|
|
static void log_none(const char* info, void* param, void* param2)
|
|
{}
|
|
static void log_consonle(const char* info, void* param, void* param2)
|
|
{
|
|
printf(info);
|
|
}
|
|
static void log_file(const char* info, void* param, void* param2)
|
|
{
|
|
FILE** file = (FILE**)param;
|
|
|
|
if (*file == nullptr)
|
|
*file = create_log_file(((std::string*)param2)->c_str(), false);
|
|
|
|
if (*file)
|
|
{
|
|
fwrite(info, 1, strlen(info), *file);
|
|
fflush(*file);
|
|
if (ftell(*file) >= MAX_LOG_FILE_SIZE)
|
|
{
|
|
fclose(*file);
|
|
remove(((std::string*)param2)->c_str());
|
|
*file = create_log_file(((std::string*)param2)->c_str(), true);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
protected:
|
|
log_cls() : path_file_(""), file_(0), log_(&log_cls::log_consonle), level_(LOG_LEVEL_ALL)
|
|
{}
|
|
~log_cls()
|
|
{
|
|
if (file_)
|
|
{
|
|
fclose(file_);
|
|
file_ = 0;
|
|
}
|
|
}
|
|
|
|
public:
|
|
static log_cls* instance(void)
|
|
{
|
|
if (!log_cls::inst_)
|
|
log_cls::inst_ = new log_cls();
|
|
|
|
return log_cls::inst_;
|
|
}
|
|
static void clear(void)
|
|
{
|
|
if(log_cls::inst_)
|
|
{
|
|
delete log_cls::inst_;
|
|
log_cls::inst_ = nullptr;
|
|
}
|
|
}
|
|
|
|
int set_log_type(int type, void* param)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (file_)
|
|
{
|
|
fclose(file_);
|
|
file_ = 0;
|
|
}
|
|
|
|
type_ = type;
|
|
if (type == LOG_TYPE_NONE)
|
|
log_ = &log_cls::log_none;
|
|
else if (type == LOG_TYPE_CONSOLE)
|
|
log_ = &log_cls::log_consonle;
|
|
else if (type == LOG_TYPE_FILE)
|
|
{
|
|
log_ = &log_cls::log_file;
|
|
|
|
ret = -1;
|
|
if (param)
|
|
{
|
|
path_file_ = (char*)param;
|
|
file_ = create_log_file(path_file_.c_str(), false);
|
|
if (file_)
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
if (ret != 0)
|
|
{
|
|
log_ = &log_cls::log_none;
|
|
type_ = LOG_TYPE_NONE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
void set_log_level(int level)
|
|
{
|
|
level_ = level;
|
|
}
|
|
int level(void)
|
|
{
|
|
return level_;
|
|
}
|
|
int type(void)
|
|
{
|
|
return type_;
|
|
}
|
|
|
|
void log(const char* info)
|
|
{
|
|
std::lock_guard<std::mutex> lock(lock_);
|
|
|
|
log_(info, &file_, &path_file_);
|
|
}
|
|
std::string get_log_file_path(const char* dst = nullptr)
|
|
{
|
|
std::string file("");
|
|
|
|
if (log_ == &log_cls::log_file && file_)
|
|
{
|
|
file = path_file_;
|
|
|
|
if (dst)
|
|
{
|
|
file = dst;
|
|
|
|
FILE* dst = fopen(file.c_str(), "wb");
|
|
|
|
if (!dst)
|
|
file = "";
|
|
else
|
|
{
|
|
std::lock_guard<std::mutex> lock(lock_);
|
|
char buf[1024] = { 0 };
|
|
size_t l = 0;
|
|
|
|
fseek(file_, 0, SEEK_SET);
|
|
while ((l = fread(buf, 1, sizeof(buf), file_)))
|
|
fwrite(buf, 1, l, dst);
|
|
fclose(dst);
|
|
}
|
|
}
|
|
}
|
|
|
|
return file;
|
|
}
|
|
void clear_log(void)
|
|
{
|
|
if (log_ == &log_cls::log_file && file_)
|
|
{
|
|
std::lock_guard<std::mutex> lock(lock_);
|
|
|
|
fclose(file_);
|
|
remove(path_file_.c_str());
|
|
file_ = create_log_file(path_file_.c_str(), true);
|
|
}
|
|
}
|
|
};
|
|
log_cls* log_cls::inst_ = NULL;
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// util
|
|
|
|
namespace utils
|
|
{
|
|
typedef struct _match_part
|
|
{
|
|
std::string pattern;
|
|
std::string found;
|
|
bool(*match)(const char*, const char*);
|
|
}MATCHPART;
|
|
|
|
static bool find_sub_str(const char* text, const char* pattern)
|
|
{
|
|
return strstr(text, pattern) != nullptr;
|
|
}
|
|
static bool STDCALL match_part_filename(const char* path_name, bool dir, void* param)
|
|
{
|
|
MATCHPART* partn = (MATCHPART*)param;
|
|
|
|
if (!dir)
|
|
{
|
|
if (partn->pattern.empty())
|
|
{
|
|
partn->found = path_name;
|
|
}
|
|
else
|
|
{
|
|
const char* name = strrchr(path_name, PATH_SEPARATOR[0]);
|
|
if (name++ == nullptr)
|
|
name = path_name;
|
|
|
|
std::string n(name);
|
|
to_lower(n);
|
|
// if (strstr(n.c_str(), partn[0].c_str()))
|
|
if(partn->match(n.c_str(), partn->pattern.c_str()))
|
|
partn->found = path_name;
|
|
else
|
|
dir = true;
|
|
}
|
|
}
|
|
|
|
return dir;
|
|
}
|
|
|
|
#if OS_WIN
|
|
static std::string u2m(const wchar_t* u, int page)
|
|
{
|
|
char* ansi = NULL;
|
|
int len = 0;
|
|
std::string mb("");
|
|
|
|
len = WideCharToMultiByte(page, 0, u, lstrlenW(u), NULL, 0, NULL, NULL);
|
|
ansi = new char[len + 2];
|
|
len = WideCharToMultiByte(page, 0, u, lstrlenW(u), ansi, len, NULL, NULL);
|
|
ansi[len--] = 0;
|
|
mb = ansi;
|
|
delete[] ansi;
|
|
|
|
return mb;
|
|
}
|
|
static std::wstring m2u(const char* m, int page)
|
|
{
|
|
wchar_t* unic = NULL;
|
|
int len = 0;
|
|
std::wstring u(L"");
|
|
|
|
len = MultiByteToWideChar(page, 0, m, lstrlenA(m), NULL, 0);
|
|
unic = new wchar_t[len + 2];
|
|
len = MultiByteToWideChar(page, 0, m, lstrlenA(m), unic, len);
|
|
unic[len--] = 0;
|
|
u = unic;
|
|
delete[] unic;
|
|
|
|
return u;
|
|
}
|
|
|
|
std::string utf82ansi(const char* utf8)
|
|
{
|
|
return u2m(m2u(utf8, CP_UTF8).c_str(), CP_ACP);
|
|
}
|
|
std::string ansi2utf8(const char* ansi)
|
|
{
|
|
return u2m(m2u(ansi, CP_ACP).c_str(), CP_UTF8);
|
|
}
|
|
#else
|
|
// This function will return 'in' string if failed !
|
|
static std::string transform_between_gbk_and_utf8(const char* in, bool to_utf8, int *err, const char* ansi = "GBK")
|
|
{
|
|
size_t len = strlen(in) + 8, ol = len * 2;
|
|
char *buf = (char*)malloc(len), *oper = buf, *out = nullptr, *oper1 = nullptr;
|
|
iconv_t conv;
|
|
|
|
memset(buf, 0, len);
|
|
strcpy(buf, in);
|
|
if(to_utf8)
|
|
conv = iconv_open("UTF-8", ansi);
|
|
else
|
|
conv = iconv_open(ansi, "UTF-8");
|
|
if(conv == (iconv_t)-1)
|
|
{
|
|
if(err)
|
|
*err = errno;
|
|
|
|
free(buf);
|
|
|
|
return in;
|
|
}
|
|
|
|
oper1 = out = (char*)malloc(ol);
|
|
memset(out, 0, ol);
|
|
len -= 8;
|
|
if(iconv(conv, &oper, &len, &oper1, &ol))
|
|
{
|
|
if(err)
|
|
*err = errno;
|
|
}
|
|
else if(err)
|
|
*err = 0;
|
|
|
|
std::string ret(out);
|
|
|
|
free(buf);
|
|
free(out);
|
|
iconv_close(conv);
|
|
|
|
return ret.empty() ? in : std::move(ret);
|
|
}
|
|
|
|
std::string utf82ansi(const char* utf8)
|
|
{
|
|
// fix me ...
|
|
return transform_between_gbk_and_utf8(utf8, false, nullptr);
|
|
}
|
|
std::string ansi2utf8(const char* ansi)
|
|
{
|
|
// fix me ...
|
|
return transform_between_gbk_and_utf8(ansi, true, nullptr);
|
|
}
|
|
#endif
|
|
|
|
std::string get_command_result(const char* cmd, int len, int *err)
|
|
{
|
|
std::string result("");
|
|
|
|
#if OS_WIN
|
|
#else
|
|
FILE* src = popen(cmd, "r");
|
|
|
|
if(err)
|
|
*err = 0;
|
|
if (src)
|
|
{
|
|
char buf[128] = { 0 };
|
|
int rv = fread(buf, 1, sizeof(buf) - 1, src);
|
|
|
|
while (rv > 0)
|
|
{
|
|
buf[rv] = 0;
|
|
result += buf;
|
|
if (len != -1 && result.length() >= len)
|
|
{
|
|
result.erase(len);
|
|
break;
|
|
}
|
|
rv = fread(buf, 1, sizeof(buf) - 1, src);
|
|
}
|
|
if(rv == -1 && err)
|
|
{
|
|
*err = errno;
|
|
utils::to_log(LOG_LEVEL_DEBUG, "Failed to excute shell command '%s' in read pipe: %d - %s\n", cmd, errno, strerror(errno));
|
|
}
|
|
pclose(src);
|
|
}
|
|
else if(err)
|
|
{
|
|
*err = errno;
|
|
utils::to_log(LOG_LEVEL_DEBUG, "Failed to excute shell command '%s' in open pipe: %d - %s\n", cmd, errno, strerror(errno));
|
|
}
|
|
#endif
|
|
|
|
return std::move(result);
|
|
}
|
|
std::string get_local_data_path(void)
|
|
{
|
|
static std::string ldp("");
|
|
|
|
if (ldp.empty())
|
|
{
|
|
#if OS_WIN
|
|
const char* path(getenv("LOCALAPPDATA"));
|
|
|
|
if (path)
|
|
{
|
|
ldp = path;
|
|
ldp += PATH_SEPARATOR;
|
|
}
|
|
#else
|
|
#ifdef BUILD_AS_DEVICE
|
|
const char* path = "/var/log";
|
|
#else
|
|
const char* path(getenv("HOME"));
|
|
#endif
|
|
|
|
if (path)
|
|
{
|
|
if (strstr(path, "/root"))
|
|
{
|
|
std::string usr(get_command_result("logname"));
|
|
ldp = std::string("/home/") + trim(usr);
|
|
if (!opendir(ldp.c_str()))
|
|
{
|
|
printf("opendir(%s) failed: %s\n", ldp.c_str(), strerror(errno));
|
|
ldp = path;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ldp = path;
|
|
}
|
|
ldp += std::string(PATH_SEPARATOR) + ".";
|
|
}
|
|
#endif
|
|
|
|
ldp += PRODUCT_VENDOR;
|
|
ldp += "Scan";
|
|
create_folder(ldp.c_str());
|
|
|
|
std::string first("[" + format_current_time() + "]: Process "),
|
|
ff(temporary_path() + PATH_SEPARATOR + PRODUCT_VENDOR + "scanner-first.log");
|
|
first += std::to_string(GetCurrentProcessId()) + " root of local data path is " + ldp + "\n";
|
|
save_2_file(&first[0], first.length(), ff.c_str(), true, SIZE_KB(1));
|
|
}
|
|
|
|
return ldp;
|
|
}
|
|
std::string temporary_path(void)
|
|
{
|
|
return std::move(simple_ini::temporary_path());
|
|
}
|
|
std::string format_current_time(void)
|
|
{
|
|
return std::move(chronograph::now());
|
|
}
|
|
std::string get_module_full_path(const char* part_name/*nullptr to get main-pe/first module's full path*/)
|
|
{
|
|
MATCHPART file = {part_name ? part_name : "", "", find_sub_str};
|
|
|
|
to_lower(file.pattern);
|
|
if(file.pattern.find("*") != std::string::npos)
|
|
file.match = is_match_pattern;
|
|
|
|
#if OS_WIN
|
|
if (part_name && *part_name)
|
|
{
|
|
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
|
|
MODULEENTRY32W pei = { 0 };
|
|
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
{
|
|
pei.dwSize = sizeof(pei);
|
|
if (Module32FirstW(h, &pei))
|
|
{
|
|
do
|
|
{
|
|
char path[256] = { 0 };
|
|
|
|
GetModuleFileNameA(pei.hModule, path, _countof(path) - 1);
|
|
if (!match_part_filename(path, false, (void*)&file))
|
|
break;
|
|
pei.dwSize = sizeof(pei);
|
|
} while (Module32NextW(h, &pei));
|
|
}
|
|
CloseHandle(h);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char path[256] = { 0 };
|
|
|
|
GetModuleFileNameA(NULL, path, _countof(path) - 1);
|
|
file.found = path;
|
|
}
|
|
#else
|
|
char path[128] = { 0 };
|
|
sprintf(path, "/proc/%u/map_files/", getpid());
|
|
enum_file(path, false, match_part_filename, (void*)&file);
|
|
#endif
|
|
|
|
return std::move(file.found);
|
|
}
|
|
std::string find_file(const char* root_dir, const char* part_name, bool recursive)
|
|
{
|
|
MATCHPART file = {part_name ? part_name : "", "", find_sub_str};
|
|
|
|
to_lower(file.pattern);
|
|
if(file.pattern.find("*") != std::string::npos)
|
|
file.match = is_match_pattern;
|
|
enum_file(root_dir, recursive, match_part_filename, (void*)&file);
|
|
|
|
return std::move(file.found);
|
|
}
|
|
std::string target_file_from_link(const char* lnk_file)
|
|
{
|
|
#if OS_WIN
|
|
std::string ret("");
|
|
|
|
return ret;
|
|
#else
|
|
char path[256] = { 0 };
|
|
int len = readlink(lnk_file, path, sizeof(path) - 1);
|
|
|
|
return len > 0 ? path : lnk_file;
|
|
#endif
|
|
}
|
|
std::string from_console(const char* tips, console_color clr)
|
|
{
|
|
std::string cmd("");
|
|
char in[2] = {0};
|
|
|
|
if(tips && tips[0])
|
|
printf_with_color(tips, clr);
|
|
while((in[0] = getchar()) != 0x0a)
|
|
cmd += in;
|
|
|
|
return std::move(cmd);
|
|
}
|
|
std::string get_ini_value(const char* seg, const char* key, const char* cfg_file)
|
|
{
|
|
char buf[512] = { 0 };
|
|
|
|
GetPrivateProfileStringA(seg, key, "", buf, sizeof(buf) - 1, cfg_file);
|
|
|
|
return buf;
|
|
}
|
|
std::string load_mini_file(const char* file, int* err)
|
|
{
|
|
std::string cont("");
|
|
FILE* src = fopen(file, "rb");
|
|
int en = 0;
|
|
|
|
if (src)
|
|
{
|
|
long len = 0;
|
|
|
|
fseek(src, 0, SEEK_END);
|
|
len = ftell(src);
|
|
fseek(src, 0, SEEK_SET);
|
|
if (len > SIZE_MB(1))
|
|
{
|
|
en = E2BIG;
|
|
}
|
|
else
|
|
{
|
|
char* buf = new char[len];
|
|
if (buf)
|
|
{
|
|
len = fread(buf, 1, len, src);
|
|
cont = std::string(buf, len);
|
|
en = 0;
|
|
delete[] buf;
|
|
}
|
|
else
|
|
en = ENOMEM;
|
|
}
|
|
fclose(src);
|
|
}
|
|
else
|
|
en = errno;
|
|
|
|
if(err)
|
|
*err = en;
|
|
|
|
return std::move(cont);
|
|
}
|
|
int save_2_file(void* data, size_t len, const char* file, bool append, size_t max_size)
|
|
{
|
|
FILE* dst = fopen(file, append ? "a+b" : "wb");
|
|
int err = 0;
|
|
|
|
if (!dst)
|
|
return errno;
|
|
|
|
if(append && max_size != -1 && ftell(dst) >= max_size)
|
|
fseek(dst, 0, SEEK_SET);
|
|
|
|
if (fwrite(data, 1, len, dst) < len)
|
|
err = ENOSPC;
|
|
fclose(dst);
|
|
|
|
return err;
|
|
}
|
|
void printf_with_color(const char* msg, console_color text_clr, console_color bkg_clr, console_display_mode mode)
|
|
{
|
|
char buf[80] = {0};
|
|
|
|
// sprintf(buf, "\033[%xm %%s \033[0m", text_clr);
|
|
if(mode != console_display_mode::MODE_DEFAULT)
|
|
{
|
|
if(bkg_clr != console_color::COLOR_BKG_NONE)
|
|
sprintf(buf, "\033[%d;%x;%xm%%s\033[0m", mode, text_clr, bkg_clr);
|
|
else
|
|
sprintf(buf, "\033[%d;%xm%%s\033[0m", mode, text_clr);
|
|
}
|
|
else if(bkg_clr != console_color::COLOR_BKG_NONE)
|
|
{
|
|
sprintf(buf, "\033[%x;%xm%%s\033[0m", text_clr, bkg_clr);
|
|
}
|
|
else
|
|
{
|
|
sprintf(buf, "\033[%xm%%s\033[0m", text_clr);
|
|
}
|
|
|
|
printf(buf, msg);
|
|
}
|
|
|
|
bool is_match_pattern(const char* text, const char* pattern)
|
|
{
|
|
int str_ind = 0, pattern_ind = 0, star = -1, m = 0,
|
|
str_len = strlen(text),
|
|
patt_len = strlen(pattern);
|
|
bool ok = true;
|
|
|
|
while (str_ind < str_len)
|
|
{
|
|
if (pattern_ind < patt_len && (text[str_ind] == pattern[pattern_ind] || pattern[pattern_ind] == '?'))
|
|
{
|
|
str_ind++;
|
|
pattern_ind++;
|
|
}
|
|
else if (pattern_ind < patt_len && pattern[pattern_ind] == '*')
|
|
{
|
|
star = pattern_ind++;
|
|
m = str_ind;
|
|
}
|
|
else if (star != -1)
|
|
{
|
|
pattern_ind = star + 1;
|
|
str_ind = ++m;
|
|
}
|
|
else
|
|
{
|
|
ok = false;
|
|
break;
|
|
}
|
|
}
|
|
if(ok)
|
|
{
|
|
while (pattern_ind < patt_len && pattern[pattern_ind] == '*')
|
|
pattern_ind++;
|
|
ok = pattern_ind == patt_len;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
const char* to_lower(std::string& str)
|
|
{
|
|
std::transform(str.begin(), str.end(), str.begin(), tolower);
|
|
|
|
return str.c_str();
|
|
}
|
|
const char* trim(std::string& str, const char* sp)
|
|
{
|
|
int pos = 0;
|
|
char ch[2] = { 0 };
|
|
|
|
for (; pos < str.length(); ++pos)
|
|
{
|
|
ch[0] = str[pos];
|
|
if (!strstr(sp, ch))
|
|
break;
|
|
}
|
|
if (pos)
|
|
str.erase(0, pos);
|
|
|
|
pos = str.length() - 1;
|
|
for (; pos >= 0; --pos)
|
|
{
|
|
ch[0] = str[pos];
|
|
if (!strstr(sp, ch))
|
|
break;
|
|
}
|
|
if (++pos < str.length())
|
|
str.erase(pos);
|
|
|
|
return str.c_str();
|
|
}
|
|
|
|
static bool hex_str_2_num(char hex, uint8_t* v)
|
|
{
|
|
*v = 0;
|
|
if(hex >= '0' && hex <= '9')
|
|
*v = hex - '0';
|
|
else if(hex >= 'a' && hex <= 'f')
|
|
*v = hex - 'a' + 10;
|
|
else if(hex >= 'A' && hex <= 'F')
|
|
*v = hex - 'A' + 10;
|
|
else
|
|
return hex == 0;
|
|
|
|
return true;
|
|
}
|
|
bool from_hex_string(const char* hex, uint8_t* val)
|
|
{
|
|
uint8_t t = 0;
|
|
bool ret = true;
|
|
|
|
*val = 0;
|
|
for(int i = 0; i < 2 && *hex && (ret = hex_str_2_num(*hex++, &t)); ++i)
|
|
{
|
|
*val <<= 4;
|
|
*val += t;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
bool from_hex_string(const char* hex, uint16_t* val)
|
|
{
|
|
uint8_t t = 0;
|
|
bool ret = true;
|
|
|
|
*val = 0;
|
|
for(int i = 0; i < 4 && *hex && (ret = hex_str_2_num(*hex++, &t)); ++i)
|
|
{
|
|
*val <<= 4;
|
|
*val += t;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
bool from_hex_string(const char* hex, uint32_t* val)
|
|
{
|
|
uint8_t t = 0;
|
|
bool ret = true;
|
|
|
|
*val = 0;
|
|
for(int i = 0; i < 8 && *hex && (ret = hex_str_2_num(*hex++, &t)); ++i)
|
|
{
|
|
*val <<= 4;
|
|
*val += t;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
bool from_hex_string(const char* hex, uint64_t* val)
|
|
{
|
|
uint8_t t = 0;
|
|
bool ret = true;
|
|
|
|
*val = 0;
|
|
for(int i = 0; i < 16 && *hex && (ret = hex_str_2_num(*hex++, &t)); ++i)
|
|
{
|
|
*val <<= 4;
|
|
*val += t;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
HMODULE load_dll(const char* path_file, int flag)
|
|
{
|
|
#if OS_WIN
|
|
HMODULE h = NULL; // LoadLibraryA(path_file);
|
|
int ret = 0; // GetLastError();
|
|
|
|
// utils::to_log(1, "[TWAIN]Load: LoadLibraryA(%s) = %d\r\n", path_file, ret);
|
|
// if (!h && (ret == ERROR_MOD_NOT_FOUND || ret == ERROR_BAD_EXE_FORMAT))
|
|
{
|
|
std::string dir(path_file);
|
|
size_t pos = dir.rfind('\\');
|
|
char path[MAX_PATH] = { 0 };
|
|
|
|
GetDllDirectoryA(_countof(path) - 1, path);
|
|
if (pos != std::wstring::npos)
|
|
dir.erase(pos);
|
|
utils::to_log(LOG_LEVEL_FATAL, "[TWAIN]Load: change directory to '%s' and retry LoadLibraryA(%s) ...\r\n", dir.c_str(), path_file);
|
|
SetDllDirectoryA(dir.c_str());
|
|
h = LoadLibraryA(path_file);
|
|
// h = LoadLibraryExW(path_dll, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
|
|
ret = GetLastError();
|
|
utils::to_log(1, "[TWAIN]Load: trying LoadLibraryA(%s) = %d, restore directory to '%s'\r\n", path_file, ret, path);
|
|
SetDllDirectoryA(path);
|
|
}
|
|
|
|
errno = GetLastError();
|
|
|
|
return h;
|
|
#else
|
|
return dlopen(path_file, flag);
|
|
#endif
|
|
}
|
|
bool create_folder(const char* folder)
|
|
{
|
|
int ret = MKDIR(folder, S_IREAD | S_IWRITE | S_IEXEC);
|
|
|
|
return ret == 0 || errno == EEXIST;
|
|
}
|
|
void set_ini_value(const char* seg, const char* key, const char* val, const char* cfg_file)
|
|
{
|
|
WritePrivateProfileStringA(seg, key, val, cfg_file);
|
|
}
|
|
int enum_file(const char* folder, bool recursive, bool(STDCALL* found)(const char* path_name, bool dir, void* param), void* param)
|
|
{
|
|
int ret = EACCES;
|
|
|
|
#if OS_WIN
|
|
WIN32_FIND_DATAA fd = { 0 };
|
|
std::string root(folder);
|
|
HANDLE hf = FindFirstFileA((root + "\\*").c_str(), &fd);
|
|
|
|
root += "\\";
|
|
if (hf == INVALID_HANDLE_VALUE)
|
|
ret = GetLastError();
|
|
else
|
|
{
|
|
do
|
|
{
|
|
bool is_dir = (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
|
|
if (!found((root + fd.cFileName).c_str(), is_dir, param))
|
|
{
|
|
ret = ERROR_CANCELLED;
|
|
break;
|
|
}
|
|
if (is_dir && recursive)
|
|
{
|
|
char* name = strrchr(fd.cFileName, PATH_SEPARATOR[0]);
|
|
if (name++ == NULL)
|
|
name = fd.cFileName;
|
|
if (strcmp(name, ".") && strcmp(name, ".."))
|
|
{
|
|
if (enum_file(fd.cFileName, recursive, found, param) == ERROR_CANCELLED)
|
|
{
|
|
ret = ERROR_CANCELLED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} while (FindNextFileA(hf, &fd));
|
|
FindClose(hf);
|
|
}
|
|
#else
|
|
DIR* pdir = nullptr;
|
|
struct dirent* ent = nullptr;
|
|
|
|
pdir = opendir(folder);
|
|
if (!pdir)
|
|
ret = errno;
|
|
else
|
|
{
|
|
while ((ent = readdir(pdir)))
|
|
{
|
|
std::string file(folder);
|
|
|
|
file += PATH_SEPARATOR;
|
|
file += ent->d_name;
|
|
if (!found(target_file_from_link(file.c_str()).c_str(), ent->d_type & DT_DIR, param))
|
|
{
|
|
ret = ERROR_CANCELLED;
|
|
break;
|
|
}
|
|
|
|
if (ent->d_type & DT_DIR)
|
|
{
|
|
if (recursive)
|
|
{
|
|
if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, ".."))
|
|
{
|
|
std::string sub(folder);
|
|
sub += PATH_SEPARATOR;
|
|
sub += ent->d_name;
|
|
if (enum_file(sub.c_str(), recursive, found, param) == ERROR_CANCELLED)
|
|
{
|
|
ret = ERROR_CANCELLED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
int move_file(const char* from, const char* to)
|
|
{
|
|
return rename(from, to);
|
|
}
|
|
int make_file_size(const char* file, uint64_t size)
|
|
{
|
|
int err = 0;
|
|
|
|
get_command_result(("fallocate -l " + std::to_string(size) + " " + file).c_str(), -1, &err);
|
|
if(err == 0)
|
|
{
|
|
get_command_result(("truncate -s " + std::to_string(size) + " " + file).c_str(), -1, &err);
|
|
if(err == 0)
|
|
{
|
|
FILE* dst = fopen(file, "rb");
|
|
if(dst)
|
|
{
|
|
uint64_t rs = 0;
|
|
|
|
FSEEK(dst, 0, SEEK_END);
|
|
rs = FTELL(dst);
|
|
fclose(dst);
|
|
if(rs != size)
|
|
err = ENOSPC;
|
|
}
|
|
else
|
|
err = errno;
|
|
}
|
|
}
|
|
if(err)
|
|
remove(file);
|
|
|
|
return err;
|
|
}
|
|
|
|
int get_memory_usage(uint64_t* peak, uint64_t* now, uint64_t* phymem, uint32_t pid)
|
|
{
|
|
if(pid == -1)
|
|
pid = GetCurrentProcessId();
|
|
|
|
#if OW_WIN
|
|
#else
|
|
char cmd[40] = {0};
|
|
std::string result(""), tag("");
|
|
size_t pos = 0;
|
|
|
|
sprintf(cmd, "cat /proc/%u/status | grep Vm", pid);
|
|
result = get_command_result(cmd);
|
|
if(result.empty())
|
|
return -1;
|
|
|
|
if(peak)
|
|
{
|
|
tag = "VmPeak";
|
|
pos = result.find(tag);
|
|
if(pos != std::string::npos)
|
|
sscanf(result.c_str() + pos, (tag + ": %llu kB").c_str(), peak);
|
|
else
|
|
*peak = 0;
|
|
*peak *= 1024;
|
|
}
|
|
|
|
if(now)
|
|
{
|
|
tag = "VmSize";
|
|
pos = result.find(tag);
|
|
if(pos != std::string::npos)
|
|
sscanf(result.c_str() + pos, (tag + ": %llu kB").c_str(), now);
|
|
else
|
|
*now = 0;
|
|
*now *= 1024;
|
|
}
|
|
|
|
if(phymem)
|
|
{
|
|
tag = "VmRSS";
|
|
pos = result.find(tag);
|
|
if(pos != std::string::npos)
|
|
sscanf(result.c_str() + pos, (tag + ": %llu kB").c_str(), phymem);
|
|
else
|
|
*phymem = 0;
|
|
*phymem *= 1024;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
void print_memory_usage(const char* tips, bool to_log_file)
|
|
{
|
|
uint64_t peak = 0, now = 0, phy = 0;
|
|
|
|
get_memory_usage(&peak, &now, &phy);
|
|
if(to_log_file)
|
|
to_log(LOG_LEVEL_DEBUG, "%s: Peak = %llu, Now = %llu, Phy = %llu\n", tips, peak, now, phy);
|
|
else
|
|
printf("%s: Peak = %llu, Now = %llu, Phy = %llu\n", tips, peak, now, phy);
|
|
}
|
|
int get_disk_space(const char* path, unsigned long long* total, unsigned long long* avail, unsigned long long* block)
|
|
{
|
|
int ret = 0;
|
|
|
|
#if OS_WIN
|
|
ULARGE_INTEGER av = { 0 },
|
|
all = { 0 };
|
|
if (GetDiskFreeSpaceExA(path, &av, &all, NULL))
|
|
{
|
|
if (total)
|
|
*total = all.QuadPart;
|
|
if (avail)
|
|
*avail = av.QuadPart;
|
|
if (block)
|
|
{
|
|
DWORD sec = 0,
|
|
clu = 0;
|
|
std::string root(path);
|
|
size_t pos = root.find(":\\");
|
|
if (pos != std::string::npos)
|
|
root.erase(pos + 2);
|
|
if (GetDiskFreeSpaceA(root.c_str(), &clu, &sec, NULL, NULL))
|
|
{
|
|
*block = clu * sec;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ret = GetLastError();
|
|
#else
|
|
struct statfs fs = { 0 };
|
|
|
|
ret = statfs(path, &fs);
|
|
if (ret == 0)
|
|
{
|
|
if (total)
|
|
*total = fs.f_blocks * fs.f_bsize;
|
|
if (avail)
|
|
*avail = fs.f_bavail * fs.f_bsize;
|
|
if (block)
|
|
*block = fs.f_bsize;
|
|
}
|
|
#endif
|
|
return ret;
|
|
}
|
|
unsigned int get_page_size(unsigned int* map_unit)
|
|
{
|
|
unsigned int ps = 1024;
|
|
#if OS_WIN
|
|
SYSTEM_INFO si = { 0 };
|
|
|
|
GetSystemInfo(&si);
|
|
ps = si.dwPageSize;
|
|
if (map_unit)
|
|
*map_unit = si.dwAllocationGranularity;
|
|
#else
|
|
ps = sysconf(_SC_PAGESIZE);
|
|
if(ps < 1024 || (ps & 0x0fe0000ff)) // nKB && < 16MB
|
|
ps = getpagesize();
|
|
if (map_unit)
|
|
*map_unit = ps;
|
|
#endif
|
|
if (ps < 1024 || (ps & 0x0fe0000ff)) // nKB && < 16MB
|
|
ps = 1024;
|
|
|
|
return ps;
|
|
}
|
|
|
|
std::string init_log(log_type type, log_level level, const char* fn_appendix)
|
|
{
|
|
std::string file("");
|
|
|
|
if (type == LOG_TYPE_FILE)
|
|
{
|
|
file = get_local_data_path() + PATH_SEPARATOR + "Log";
|
|
create_folder(file.c_str());
|
|
|
|
std::string pe(get_module_full_path());
|
|
size_t pos = pe.rfind(PATH_SEPARATOR[0]);
|
|
|
|
if (pos++ == std::string::npos)
|
|
pos = 0;
|
|
file += PATH_SEPARATOR + pe.substr(pos);
|
|
if (fn_appendix)
|
|
file += fn_appendix;
|
|
file += ".log";
|
|
printf("log file : %s\n", file.c_str());
|
|
}
|
|
|
|
log_cls::instance()->set_log_type(type, &file[0]);
|
|
log_cls::instance()->set_log_level(level);
|
|
|
|
return std::move(file);
|
|
}
|
|
void uninit(void)
|
|
{
|
|
log_info(("=====================================--Exited--" + std::to_string(GetCurrentProcessId()) + "=====================================\n\n\n\n").c_str(), LOG_LEVEL_FATAL);
|
|
|
|
log_cls::clear();
|
|
}
|
|
void log_info(const char* info, int level)
|
|
{
|
|
if (get_log_type() != LOG_TYPE_NONE && get_log_level() <= level)
|
|
{
|
|
log_cls::instance()->log(("[" + format_current_time() + "]: " + info).c_str());
|
|
}
|
|
}
|
|
void log_mem_info(const char* desc, const void* data, size_t bytes, int level)
|
|
{
|
|
if (get_log_type() == LOG_TYPE_NONE || get_log_level() > level)
|
|
return;
|
|
|
|
std::string line(desc);
|
|
char buf[40] = {0};
|
|
|
|
utils::log_info((line + "\n").c_str(), level);
|
|
line = "";
|
|
for(size_t i = 0; i < bytes; ++i)
|
|
{
|
|
if((i % 16) == 0)
|
|
{
|
|
if(line.length())
|
|
utils::log_info((line + "\n").c_str(), level);
|
|
sprintf(buf, "%p ", (const char*)data + i);
|
|
line = buf;
|
|
}
|
|
else if((i % 8) == 0)
|
|
line += " ";
|
|
sprintf(buf, "%02x ", ((const unsigned char*)data)[i]);
|
|
line += buf;
|
|
}
|
|
if(line.length())
|
|
utils::log_info((line + "\n").c_str(), level);
|
|
}
|
|
int get_log_type(void)
|
|
{
|
|
return log_cls::instance()->type();
|
|
}
|
|
int get_log_level(void)
|
|
{
|
|
return log_cls::instance()->level();
|
|
}
|
|
|
|
int copy_log_file_to(const char* dst)
|
|
{
|
|
log_cls::instance()->get_log_file_path(dst);
|
|
|
|
return 0;
|
|
}
|
|
int clear_log_file(void)
|
|
{
|
|
log_cls::instance()->clear_log();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mm_2_pixel(float mm, int dpi)
|
|
{
|
|
return (int)(mm / MM_PER_INCH * dpi + .5f);
|
|
}
|
|
float pixel_2_mm(int px, int dpi)
|
|
{
|
|
float mm = px;
|
|
|
|
mm /= dpi;
|
|
mm *= MM_PER_INCH;
|
|
|
|
return mm;
|
|
}
|
|
|
|
std::string bitmap_info_header(int pixel_w, int pixel_h, int bpp, int dpix, int dpiy)
|
|
{
|
|
BITMAPINFOHEADER bih = {0};
|
|
std::string stream("");
|
|
|
|
if(dpiy == 0)
|
|
dpiy = dpix;
|
|
bih.biSize = sizeof(bih);
|
|
bih.biWidth = pixel_w;
|
|
bih.biHeight = pixel_h;
|
|
bih.biBitCount = bpp;
|
|
bih.biSizeImage = BMP_LINE_BYTES(pixel_w * bpp) * pixel_h;
|
|
bih.biPlanes = 1;
|
|
bih.biCompression = BI_RGB;
|
|
bih.biXPelsPerMeter = (LONG)(dpix * 39.37f + .5f);
|
|
bih.biYPelsPerMeter = (LONG)(dpiy * 39.37f + .5f);
|
|
stream = std::string((char*)&bih, sizeof(bih));
|
|
if(bpp == 1)
|
|
{
|
|
int pal[] = { 0, 0x0ffffff };
|
|
stream += std::string((char*)pal, sizeof(pal));
|
|
((BITMAPINFOHEADER*)&stream[0])->biClrUsed = _countof(pal);
|
|
}
|
|
else if(bpp == 8)
|
|
{
|
|
stream += std::string((char*)global_info::gray_pallete, sizeof(global_info::gray_pallete));
|
|
((BITMAPINFOHEADER*)&stream[0])->biClrUsed = _countof(global_info::gray_pallete);
|
|
}
|
|
|
|
return std::move(stream);
|
|
}
|
|
std::string bitmap_file_header(BITMAPINFOHEADER *lpbi) // return BITMAPFILEHEADER
|
|
{
|
|
BITMAPFILEHEADER bfh = {0};
|
|
int pal_size = 0;
|
|
|
|
bfh.bfType = MAKEWORD('B', 'M');
|
|
if(lpbi->biBitCount == 1)
|
|
pal_size = 2 * sizeof(int);
|
|
else if(lpbi->biBitCount == 8)
|
|
pal_size = 256 * sizeof(int);
|
|
bfh.bfOffBits = sizeof(bfh) + sizeof(BITMAPINFOHEADER) + pal_size;
|
|
bfh.bfSize = bfh.bfOffBits + lpbi->biSizeImage;
|
|
|
|
return std::move(std::string((char*)&bfh, sizeof(bfh)));
|
|
}
|
|
|
|
#if OS_WIN
|
|
bool run_get_message(HWND hwnd, UINT filter_min, UINT filter_max, std::function<bool(MSG*, bool*)> msghandler)
|
|
{
|
|
MSG msg = { 0 };
|
|
BOOL ret = GetMessageW(&msg, hwnd, filter_min, filter_max);
|
|
bool handled = false;
|
|
|
|
if ((DWORD)ret == -1 || !ret)
|
|
return false;
|
|
|
|
if (msghandler)
|
|
ret = msghandler(&msg, &handled) == true;
|
|
if (!handled)
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessageW(&msg);
|
|
}
|
|
|
|
return ret == TRUE;
|
|
}
|
|
#endif
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// base64 util ..
|
|
static char base64_default_table[] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" };
|
|
|
|
base64::base64() : padding_char_('=')
|
|
{
|
|
base64_ind_[0] = base64_char_[0] = 0;
|
|
|
|
initialize_base64_table(base64_default_table);
|
|
}
|
|
base64::~base64()
|
|
{}
|
|
|
|
bool base64::is_valid_base64_table(const char* table)
|
|
{
|
|
bool valid = false;
|
|
|
|
if (table && strlen(table) >= 64)
|
|
{
|
|
char repeat[4] = { 0 };
|
|
|
|
valid = true;
|
|
for (int i = 0; i < 63; ++i)
|
|
{
|
|
repeat[0] = table[i];
|
|
if (strstr(table + i + 1, repeat))
|
|
{
|
|
valid = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
bool base64::initialize_base64_table(const char* table)
|
|
{
|
|
if (!table || strlen(table) < 64)
|
|
{
|
|
if (memcmp(base64_default_table, base64_char_, 64) == 0)
|
|
{
|
|
return !table;
|
|
}
|
|
|
|
memcpy(base64_char_, base64_default_table, 64);
|
|
}
|
|
else
|
|
{
|
|
if (memcmp(base64_char_, table, 64) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
else if (!is_valid_base64_table(table))
|
|
return false;
|
|
|
|
memcpy(base64_char_, table, 64);
|
|
}
|
|
base64_char_[64] = base64_char_[65] = 0;
|
|
|
|
// initialize base64_index
|
|
memset(base64_ind_, 0, sizeof(base64_ind_));
|
|
for (int i = 0; i < 64; ++i)
|
|
{
|
|
base64_ind_[base64_char_[i]] = i;
|
|
}
|
|
|
|
// padding char
|
|
padding_char_ = '=';
|
|
if (base64_ind_[padding_char_])
|
|
{
|
|
for (padding_char_ = 0x21; padding_char_ < 0x7e && base64_ind_[padding_char_] && padding_char_ != base64_char_[0]; ++padding_char_);
|
|
}
|
|
|
|
return padding_char_ < 0x7e;
|
|
}
|
|
|
|
bool base64::set_base64_table(const char* table)
|
|
{
|
|
return initialize_base64_table(table ? table : base64_default_table);
|
|
}
|
|
std::string base64::encode(const char* data, size_t bytes, unsigned int line_bytes, bool need_padding)
|
|
{
|
|
char* str = (char*)malloc(bytes * 2 + 3);
|
|
unsigned char c1 = 0, c2 = 0, c3 = 0;
|
|
unsigned long line_len = 0;
|
|
unsigned long words = bytes / 3;
|
|
int rest = bytes % 3,
|
|
pos = 0;
|
|
std::string ret("");
|
|
|
|
for (unsigned long i = 0; i < words; ++i)
|
|
{
|
|
// fetch 3 letters
|
|
c1 = *data++;
|
|
c2 = *data++;
|
|
c3 = *data++;
|
|
|
|
// encoding into 4-bytes
|
|
str[pos++] = base64_char_[c1 >> 2];
|
|
str[pos++] = base64_char_[((c1 << 4) | (c2 >> 4)) & 0x3f];
|
|
str[pos++] = base64_char_[((c2 << 2) | (c3 >> 6)) & 0x3f];
|
|
str[pos++] = base64_char_[c3 & 0x3f];
|
|
line_len += 4;
|
|
|
|
// new line ...
|
|
if ((unsigned int)line_len > line_bytes - 4)
|
|
{
|
|
str[pos++] = '\r';
|
|
str[pos++] = '\n';
|
|
line_len = 0;
|
|
}
|
|
}
|
|
|
|
// rest ...
|
|
if (rest == 1)
|
|
{
|
|
c1 = *data++;
|
|
str[pos++] = base64_char_[(c1 & 0xfc) >> 2];
|
|
str[pos++] = base64_char_[((c1 & 0x03) << 4)];
|
|
if (need_padding)
|
|
{
|
|
str[pos++] = padding_char_;
|
|
str[pos++] = padding_char_;
|
|
}
|
|
}
|
|
else if (rest == 2)
|
|
{
|
|
c1 = *data++;
|
|
c2 = *data++;
|
|
str[pos++] = base64_char_[(c1 & 0xfc) >> 2];
|
|
str[pos++] = base64_char_[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)];
|
|
str[pos++] = base64_char_[((c2 & 0x0f) << 2)];
|
|
if (need_padding)
|
|
{
|
|
str[pos++] = padding_char_;
|
|
}
|
|
}
|
|
if (pos > 0)
|
|
{
|
|
str[pos++] = 0;
|
|
ret = std::string(str, pos - 1);
|
|
}
|
|
free(str);
|
|
|
|
return ret;
|
|
}
|
|
std::string base64::decode(const char* data, size_t bytes)
|
|
{
|
|
char* str = (char*)malloc(bytes + 1);
|
|
int pos = 0,
|
|
shifts = 18,
|
|
value = 0;
|
|
std::string ret("");
|
|
|
|
for (int i = 0; i < (int)bytes; ++i)
|
|
{
|
|
if (*data != '\r' && *data != '\n')
|
|
{
|
|
if (*data == padding_char_)
|
|
{
|
|
break;
|
|
}
|
|
|
|
value += base64_ind_[*data] << shifts;
|
|
if (shifts == 0)
|
|
{
|
|
shifts = 18;
|
|
str[pos++] = (value >> 16) & 0x0ff;
|
|
str[pos++] = (value >> 8) & 0x0ff;
|
|
str[pos++] = (value >> 0) & 0x0ff;
|
|
value = 0;
|
|
}
|
|
else
|
|
{
|
|
shifts -= 6;
|
|
}
|
|
}
|
|
data++;
|
|
}
|
|
|
|
if (shifts == 12 || shifts == 6)
|
|
{
|
|
str[pos++] = (value >> 16) & 0x0ff;
|
|
}
|
|
else if (shifts == 0)
|
|
{
|
|
str[pos++] = (value >> 16) & 0x0ff;
|
|
str[pos++] = (value >> 8) & 0x0ff;
|
|
}
|
|
|
|
if (pos > 0)
|
|
{
|
|
str[pos++] = 0;
|
|
ret = std::string(str, pos - 1);
|
|
}
|
|
free(str);
|
|
|
|
return ret;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// refer
|
|
refer::refer() : ref_(1)
|
|
{
|
|
on_born();
|
|
}
|
|
refer::~refer()
|
|
{
|
|
on_dead();
|
|
}
|
|
|
|
void refer::on_born(void)
|
|
{}
|
|
void refer::on_dead(void)
|
|
{}
|
|
|
|
int32_t refer::add_ref(void)
|
|
{
|
|
SIMPLE_LOCK(mutex_);
|
|
|
|
return ++ref_;
|
|
}
|
|
int32_t refer::release(void)
|
|
{
|
|
int32_t ref = 0;
|
|
|
|
{
|
|
SIMPLE_LOCK(mutex_);
|
|
ref = --ref_;
|
|
}
|
|
|
|
if (ref == 0)
|
|
delete this;
|
|
|
|
return ref;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// util
|
|
chronograph::chronograph()
|
|
{
|
|
reset();
|
|
}
|
|
chronograph::~chronograph()
|
|
{}
|
|
|
|
bool chronograph::now(TIMEV* tv)
|
|
{
|
|
struct timezone tz = { 0 };
|
|
|
|
return gettimeofday(tv, &tz) == 0;
|
|
}
|
|
bool chronograph::now(uint64_t* seconds, uint64_t* u_seconds)
|
|
{
|
|
TIMEV tv = { 0 };
|
|
struct timezone tz = { 0 };
|
|
|
|
if (gettimeofday(&tv, &tz) == 0)
|
|
{
|
|
if (seconds)
|
|
*seconds = tv.tv_sec;
|
|
if (u_seconds)
|
|
*u_seconds = tv.tv_usec;
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
std::string chronograph::now(bool with_ms/*whether with milliseconds*/) // return '2022-11-30 10:38:42.123', no '.123' if with_ms was false
|
|
{
|
|
TIMEV tv = { 0 };
|
|
|
|
if (!chronograph::now(&tv))
|
|
return "";
|
|
|
|
char buf[40] = { 0 };
|
|
time_t t = tv.tv_sec;
|
|
struct tm* l = localtime(&t);
|
|
|
|
if (with_ms)
|
|
sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d.%06d", l->tm_year + 1900, l->tm_mon + 1, l->tm_mday
|
|
, l->tm_hour, l->tm_min, l->tm_sec, tv.tv_usec);
|
|
else
|
|
sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", l->tm_year + 1900, l->tm_mon + 1, l->tm_mday
|
|
, l->tm_hour, l->tm_min, l->tm_sec);
|
|
|
|
return buf;
|
|
}
|
|
|
|
uint64_t chronograph::elapse_s(void)
|
|
{
|
|
TIMEV tv = { 0 };
|
|
|
|
chronograph::now(&tv);
|
|
|
|
return tv.tv_sec - bgn_.tv_sec;
|
|
}
|
|
uint64_t chronograph::elapse_ms(void)
|
|
{
|
|
TIMEV tv = { 0 };
|
|
uint64_t dif = 0;
|
|
|
|
chronograph::now(&tv);
|
|
dif = SEC_2_MS(tv.tv_sec - bgn_.tv_sec);
|
|
dif += tv.tv_usec / MSEC_2_US(1);
|
|
dif -= bgn_.tv_usec / MSEC_2_US(1);
|
|
|
|
return dif;
|
|
}
|
|
uint64_t chronograph::elapse_us(void)
|
|
{
|
|
TIMEV tv = { 0 };
|
|
uint64_t dif = 0;
|
|
|
|
chronograph::now(&tv);
|
|
dif = SEC_2_US(tv.tv_sec - bgn_.tv_sec);
|
|
dif += tv.tv_usec;
|
|
dif -= bgn_.tv_usec;
|
|
|
|
return dif;
|
|
}
|
|
void chronograph::reset()
|
|
{
|
|
chronograph::now(&bgn_);
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// global_info
|
|
uint32_t global_info::page_size = 0;
|
|
uint32_t global_info::page_map_size = 0;
|
|
uint32_t global_info::cluster_size = 0;
|
|
uint32_t global_info::gray_pallete[] = {0};
|
|
|
|
global_info::global_info()
|
|
{
|
|
global_info::page_size = utils::get_page_size(&global_info::page_map_size);
|
|
|
|
std::string path(utils::get_local_data_path());
|
|
unsigned long long cluster = 0;
|
|
|
|
utils::get_disk_space(path.c_str(), nullptr, nullptr, &cluster);
|
|
global_info::cluster_size = cluster;
|
|
for(int i = 0; i < _countof(global_info::gray_pallete); ++i)
|
|
global_info::gray_pallete[i] = MAKELONG(MAKEWORD(i, i), MAKEWORD(i, 0));
|
|
|
|
printf("Page size: %u\nMap size: %u\nCluster : %u\n", global_info::page_size, global_info::page_map_size, global_info::cluster_size);
|
|
}
|
|
global_info::~global_info()
|
|
{}
|
|
|
|
static global_info g_si;
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// event
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// windows event ...
|
|
#if OS_WIN
|
|
int __stdcall sem_init(sem_t* handle, int, int)
|
|
{
|
|
if (!handle)
|
|
{
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
*handle = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (*handle)
|
|
{
|
|
ResetEvent(*handle);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
errno = GetLastError();
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
void __stdcall sem_destroy(sem_t* handle)
|
|
{
|
|
if (*handle)
|
|
{
|
|
CloseHandle(*handle);
|
|
*handle = NULL;
|
|
}
|
|
}
|
|
int __stdcall sem_trywait(sem_t* handle)
|
|
{
|
|
return WaitForSingleObject(*handle, 1) == WAIT_TIMEOUT ? -1 : 0;
|
|
}
|
|
void __stdcall sem_wait(sem_t* handle)
|
|
{
|
|
if (WaitForSingleObject(*handle, INFINITE) == WAIT_OBJECT_0)
|
|
ResetEvent(*handle);
|
|
}
|
|
int __stdcall sem_timedwait(sem_t* handle, struct timespec* to)
|
|
{
|
|
DWORD elapse = to->tv_sec * 1000;
|
|
elapse += to->tv_nsec / (1000 * 1000);
|
|
|
|
int ret = WaitForSingleObject(*handle, elapse) == WAIT_TIMEOUT ? -1 : 0;
|
|
|
|
if (ret == 0)
|
|
ResetEvent(*handle);
|
|
|
|
return ret;
|
|
}
|
|
void __stdcall sem_post(sem_t* handle)
|
|
{
|
|
SetEvent(*handle);
|
|
}
|
|
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// platform_event (base on semaphore)
|
|
platform_event::platform_event(const char* info) : waiting_(false), dbg_info_(info ? info : "")
|
|
{
|
|
int err = sem_init(&sem_, 0, 0);
|
|
|
|
if (err == -1)
|
|
{
|
|
err = errno;
|
|
utils::to_log(LOG_LEVEL_FATAL, "(%p)sem_init failed: %d\n", this, err);
|
|
}
|
|
}
|
|
platform_event::~platform_event()
|
|
{
|
|
sem_destroy(&sem_);
|
|
}
|
|
|
|
bool platform_event::try_wait(void)
|
|
{
|
|
return sem_trywait(&sem_) == 0;
|
|
}
|
|
bool platform_event::wait(unsigned timeout)
|
|
{
|
|
bool waited = true;
|
|
|
|
if(log_)
|
|
utils::to_log(LOG_LEVEL_DEBUG, "platform_event(%p - %s) --> waiting...\n", this, dbg_info_.c_str());
|
|
waiting_ = true;
|
|
if (timeout == USB_TIMEOUT_INFINITE)
|
|
sem_wait(&sem_);
|
|
else
|
|
{
|
|
struct timespec to = {0};
|
|
#if !OS_WIN
|
|
if(clock_gettime(CLOCK_REALTIME, &to) == -1)
|
|
{
|
|
utils::to_log(LOG_LEVEL_DEBUG, "clock_gettime failed: %d - %s\n", errno, strerror(errno));
|
|
to.tv_sec = time(nullptr);
|
|
}
|
|
#endif
|
|
to.tv_sec += timeout / 1000;
|
|
to.tv_nsec += (long)((timeout % 1000) * 1000 * 1000);
|
|
waited = sem_timedwait(&sem_, &to) == 0;
|
|
}
|
|
if (log_)
|
|
utils::to_log(LOG_LEVEL_DEBUG, "platform_event(%p - %s) --> %s.\n", this, dbg_info_.c_str(), waited ? "waited" : "wait timeout");
|
|
waiting_ = false;
|
|
|
|
return waited;
|
|
}
|
|
void platform_event::trigger(void)
|
|
{
|
|
sem_post(&sem_);
|
|
}
|
|
void platform_event::reset(void)
|
|
{
|
|
sem_init(&sem_, 0, 0);
|
|
}
|
|
bool platform_event::is_waiting(void)
|
|
{
|
|
return waiting_;
|
|
}
|
|
void platform_event::set_debug_info(const char* info)
|
|
{
|
|
dbg_info_ = info ? info : "";
|
|
}
|
|
void platform_event::enable_log(bool enable)
|
|
{
|
|
log_ = enable;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// shared_memory
|
|
shared_memory::shared_memory(unsigned long long key, size_t size) : key_(key), obj_((void*)-1), first_(true), bytes_(size), len_(0)
|
|
{
|
|
unsigned int* ptr = (unsigned int*)&key_;
|
|
utils::to_log(LOG_LEVEL_DEBUG, "shared memory key = 0x%x%08x\n", ptr[1], ptr[0]);
|
|
|
|
init();
|
|
}
|
|
shared_memory::~shared_memory()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
void shared_memory::init(void)
|
|
{
|
|
#if OS_WIN
|
|
char name[40] = { 0 };
|
|
DWORD* key = (DWORD*)&key_;
|
|
HANDLE h = NULL;
|
|
|
|
sprintf(name, "scanner_0x%08x-%08x", key[1], key[0]);
|
|
h = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, bytes_, name);
|
|
if (h == NULL)
|
|
return;
|
|
|
|
first_ = !(GetLastError() == ERROR_ALREADY_EXISTS);
|
|
obj_ = (void*)h;
|
|
#else
|
|
int obj = shmget(key_, bytes_, IPC_EXCL | IPC_CREAT | 0666);
|
|
if (obj < 0)
|
|
{
|
|
unsigned int* v = (unsigned int*)&key_;
|
|
if (errno == EEXIST)
|
|
{
|
|
first_ = false;
|
|
obj = shmget(key_, bytes_, 0600);
|
|
if (obj == -1)
|
|
obj = shmget(key_, bytes_, 0);
|
|
utils::to_log(LOG_LEVEL_DEBUG, "open existing: shmget(0x%x%08x) = %d\n", v[1], v[0], obj);
|
|
obj_ = (void*)obj;
|
|
|
|
std::string prev(read()), proc("");
|
|
utils::to_log(LOG_LEVEL_DEBUG, "shared memory content: %s\n", prev.c_str());
|
|
if (prev.length())
|
|
{
|
|
proc = prev;
|
|
size_t pos = proc.find("pid: ");
|
|
if (pos != std::string::npos)
|
|
proc.erase(0, pos + 5);
|
|
pos = proc.find(")");
|
|
if (pos != std::string::npos)
|
|
proc.erase(pos);
|
|
proc = shared_memory::get_proc_name_by_pid(atoi(proc.c_str()));
|
|
if (proc.length())
|
|
{
|
|
pos = prev.find("(");
|
|
if (pos == std::string::npos)
|
|
pos = prev.length();
|
|
if (strcasecmp(proc.c_str(), prev.substr(0, pos).c_str()))
|
|
proc = "";
|
|
}
|
|
}
|
|
if (proc.empty())
|
|
{
|
|
first_ = true;
|
|
clear();
|
|
obj = shmget(key_, bytes_, IPC_EXCL | IPC_CREAT | 0600);
|
|
utils::to_log(LOG_LEVEL_DEBUG, "%s is not existing and reopen it\n", prev.c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
utils::to_log(LOG_LEVEL_DEBUG, "shmget(0x%x%08x) = %d\n", v[1], v[0], errno);
|
|
return;
|
|
}
|
|
}
|
|
obj_ = (void*)obj;
|
|
utils::to_log(LOG_LEVEL_DEBUG, "shared memory id = %d[%s], \n", obj, first_ ? "created" : "opened");
|
|
#endif
|
|
|
|
if (first_)
|
|
{
|
|
pid_t pid = getpid();
|
|
std::string me("");
|
|
char buf[40] = { 0 };
|
|
unsigned int* pn = (unsigned int*)&pid;
|
|
|
|
if (sizeof(pid) > 4 && pn[1])
|
|
sprintf(buf, "(pid: 0x%x%08x)", pn[1], pn[0]);
|
|
else
|
|
sprintf(buf, "(pid: %u)", pn[0]);
|
|
|
|
me = utils::get_module_full_path();
|
|
pid = me.rfind(PATH_SEPARATOR[0]);
|
|
if ((size_t)pid != std::string::npos)
|
|
me.erase(0, pid + 1);
|
|
|
|
me += buf;
|
|
write(me.c_str(), me.length());
|
|
}
|
|
}
|
|
void shared_memory::clear(void)
|
|
{
|
|
if (obj_ != (void*)-1)
|
|
#if OS_WIN
|
|
CloseHandle((HANDLE)obj_);
|
|
#else
|
|
{
|
|
if (first_)
|
|
{
|
|
struct shmid_ds ds = { 0 };
|
|
int* h = (int*)&obj_;
|
|
shmctl(*h, IPC_RMID, &ds);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
obj_ = (void*)-1;
|
|
}
|
|
char* shared_memory::get_buf(void)
|
|
{
|
|
#if OS_WIN
|
|
char* buf = (char*)MapViewOfFile((HANDLE)obj_, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
|
|
if (!buf)
|
|
buf = (char*)-1;
|
|
#else
|
|
int* h = (int*)&obj_;
|
|
char* buf = (char*)shmat(*h, 0, 0);
|
|
utils::to_log(LOG_LEVEL_DEBUG, "shared memory %d buffer = %p, error = %d\n", *h, buf, errno);
|
|
#endif
|
|
|
|
return buf;
|
|
}
|
|
void shared_memory::release_buf(void* buf)
|
|
{
|
|
#if OS_WIN
|
|
UnmapViewOfFile(buf);
|
|
#else
|
|
shmdt(buf);
|
|
#endif
|
|
}
|
|
|
|
#if !OS_WIN
|
|
std::string shared_memory::get_proc_name_by_pid(pid_t pid)
|
|
{
|
|
char path[512] = { 0 };
|
|
unsigned int* v = (unsigned int*)&pid;
|
|
std::string ret("");
|
|
|
|
if (sizeof(pid) > 4 && v[1])
|
|
sprintf(path, "/proc/%lld/status", pid);
|
|
else
|
|
sprintf(path, "/proc/%u/status", pid);
|
|
|
|
FILE* src = fopen(path, "rb");
|
|
if (src)
|
|
{
|
|
char val[512] = { 0 };
|
|
|
|
memset(path, 0, sizeof(path));
|
|
fgets(path, sizeof(path) - 1, src);
|
|
fclose(src);
|
|
sscanf(path, "%*s %s", val);
|
|
ret = val;
|
|
}
|
|
if (sizeof(pid) > 4 && v[1])
|
|
{
|
|
utils::to_log(LOG_LEVEL_DEBUG, "PID(%lld) name is: %s\n", pid, ret.c_str());
|
|
}
|
|
else
|
|
{
|
|
utils::to_log(LOG_LEVEL_DEBUG, "PID(%u) name is: %s\n", pid, ret.c_str());
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
bool shared_memory::is_ok(void)
|
|
{
|
|
return obj_ != (void*)-1;
|
|
}
|
|
bool shared_memory::is_first(void)
|
|
{
|
|
return is_ok() && first_;
|
|
}
|
|
std::string shared_memory::read(void)
|
|
{
|
|
if (obj_ == (void*)-1)
|
|
return "";
|
|
|
|
char* buf = get_buf();
|
|
if (buf == (char*)-1)
|
|
return "";
|
|
|
|
std::string ret("");
|
|
size_t len = 0;
|
|
int off = 4; // sizeof(size_t);
|
|
|
|
memcpy(&len, buf, off);
|
|
ret = std::string(buf + off, len);
|
|
release_buf(buf);
|
|
|
|
return ret;
|
|
}
|
|
int shared_memory::write(const char* data, size_t len)
|
|
{
|
|
if (len > bytes_)
|
|
return E2BIG;
|
|
|
|
char* buf = get_buf();
|
|
int off = 4; // sizeof(len);
|
|
if (buf == (char*)-1)
|
|
return errno;
|
|
|
|
memcpy(buf, &len, off);
|
|
memcpy(buf + off, data, len);
|
|
len_ = len;
|
|
release_buf(buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// safe_thread
|
|
safe_thread::safe_thread() : excep_que_("thread-exception")
|
|
{
|
|
excep_que_.enable_wait_log(false);
|
|
notify_thread_.reset(new std::thread(&safe_thread::thread_notify_exception, this));
|
|
}
|
|
safe_thread::~safe_thread()
|
|
{
|
|
run_ = false;
|
|
excep_que_.trigger();
|
|
|
|
if(notify_thread_->joinable())
|
|
notify_thread_->join();
|
|
|
|
for(auto& v: threads_)
|
|
{
|
|
if(v.thread.get() && v.thread->joinable())
|
|
v.thread->join();
|
|
}
|
|
threads_.clear();
|
|
}
|
|
|
|
void safe_thread::thread_worker(std::function<void(void)> func, std::string name, void* addr)
|
|
{
|
|
try
|
|
{
|
|
utils::to_log(LOG_LEVEL_DEBUG, "+++ safe_thread of '%s(addr: %p) - id: %p' is running ...\n", name.c_str(), addr, GetCurrentThreadId());
|
|
func();
|
|
utils::to_log(LOG_LEVEL_DEBUG, "--- safe_thread of '%s - %p' exited.\n", name.c_str(), GetCurrentThreadId());
|
|
return;
|
|
}
|
|
catch (std::exception e)
|
|
{
|
|
utils::to_log(LOG_LEVEL_FATAL, "Exception in thread '%p - %s': %s\n", GetCurrentThreadId(), name.c_str(), e.what());
|
|
}
|
|
catch (...)
|
|
{
|
|
utils::to_log(LOG_LEVEL_FATAL, "Unknown exception in thread '%p - %s'!\n", GetCurrentThreadId(), name.c_str());
|
|
}
|
|
excep_que_.save(name, true);
|
|
}
|
|
void safe_thread::thread_notify_exception(void)
|
|
{
|
|
while(run_)
|
|
{
|
|
std::string name("");
|
|
if(excep_que_.take(name, true))
|
|
{
|
|
if(excep_handler_)
|
|
excep_handler_(name.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void safe_thread::set_exception_handler(std::function<void(const char*)> on_exception)
|
|
{
|
|
excep_handler_ = on_exception;
|
|
}
|
|
int safe_thread::start(std::function<void(void)> f, const char* thread_name, void* addr)
|
|
{
|
|
SAFETHRD st;
|
|
|
|
st.name = thread_name ? thread_name : "";
|
|
st.thread.reset(new std::thread(&safe_thread::thread_worker, this, f, thread_name, addr));
|
|
|
|
{
|
|
SIMPLE_LOCK(lock_);
|
|
threads_.push_back(st);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
int safe_thread::stop(const char* thread_name)
|
|
{
|
|
int ret = ENOENT;
|
|
|
|
SIMPLE_LOCK(lock_);
|
|
for(int i = 0; i < threads_.size(); ++i)
|
|
{
|
|
if(!thread_name || threads_[i].name == thread_name)
|
|
{
|
|
if(threads_[i].thread.get() && threads_[i].thread->joinable())
|
|
threads_[i].thread->join();
|
|
threads_.erase(threads_.begin() + i);
|
|
ret = 0;
|
|
if(thread_name)
|
|
break;
|
|
else
|
|
i--;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
} |