#include "utils.h" #include "ini_file.h" #include #include #if OS_WIN #include #include #include #include #include #include #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 #include #include #include #include #include #include #include #include #include #include #include #include #define PRODUCT_VENDOR "HuaGo" static std::mutex ini_lock_; static std::map 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 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 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 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 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); } static time_t file_time_2_utc(FILETIME ft) { SYSTEMTIME sys = { 0 }; struct tm t = { 0 }; if (FileTimeToSystemTime(&ft, &sys)) { t.tm_year = sys.wYear - 1900; t.tm_mon = sys.wMonth - 1; t.tm_mday = sys.wDay; t.tm_hour = sys.wHour; t.tm_min = sys.wMinute; t.tm_sec = sys.wSecond; } return mktime(&t); } #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 int parse_utf8_char(const char*& str, char ch[]) { uint8_t first = *str, ind = 0; int ret = EINVAL; if(first < 0x80) { ret = 0; ch[ind++] = *str++; } else if(first >= 0x0c00) { ret = 0; first &= 0x0fc; while(first & 0x80) { ch[ind] = str[ind]; if(ch[ind] == 0) { ret = ENODATA; break; } ind++; first <<= 1; } if(ret == 0) str += ind; } ch[ind++] = 0; return ret; } int utf16_2_8(unsigned short* &in, char ch[]) { int len = 0; int ret = 0, used = 0; if(in[0] >= 0 && in[0] <= 0x7f) { ch[len++] = *in++; } else if(in[0] >= 0x0dc00 && in[0] <= 0x0dfff) { ret = EINVAL; } else { unsigned int val = in[0]; if(in[0] >= 0x0d800 && in[0] <= 0x0dbff) { if(in[1] < 0x0dc00 || in[1] > 0x0dfff) ret = EINVAL; else { used = 1; val = 0x10000 + (((val & 0x3ff) << 10) | (in[1] & 0x3ff)); } } if(ret == 0) { static unsigned char lead[] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; char *ptr = ch; used++; len = 4; if(val < 0x80) len = 1; else if(val < 0x800) len = 2; else if(val < 0x10000) len = 3; ptr += len; switch(len) { case 4: *--ptr = (val | 0x80) & 0x0bf; val >>= 6; case 3: *--ptr = (val | 0x80) & 0x0bf; val >>= 6; case 2: *--ptr = (val | 0x80) & 0x0bf; val >>= 6; case 1: *--ptr = val | lead[len]; } } } ch[len++] = 0; in += used; return ret; } 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; while (!dst) { std::string dir(file); size_t pos = dir.rfind(PATH_SEPARATOR[0]); if(pos != std::string::npos) { dir.erase(pos); create_folder(dir.c_str()); dst = fopen(file, append ? "a+b" : "wb"); if(dst) break; } 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(); } int get_stack_size(void) { int size = 0; #if OS_WIN ULONG_PTR low = 0, up = 0; GetCurrentThreadStackLimits(&low, &up); size = up - low; #else pthread_attr_t attr; pthread_getattr_np(pthread_self(), &attr); // pthread_attr_init(&attr); pthread_attr_getstacksize(&attr, (size_t*)&size); pthread_attr_destroy(&attr); #endif return size; } 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_file_time(const char* file, uint64_t* born, uint64_t* modify, uint64_t* last_access) { int err = 0; #if OS_WIN if (born) { WIN32_FILE_ATTRIBUTE_DATA wfd = { 0 }; if (GetFileAttributesExA(file, GetFileExInfoStandard, &wfd)) { if (born) *born = file_time_2_utc(wfd.ftCreationTime); if (modify) *modify = file_time_2_utc(wfd.ftLastWriteTime); if (last_access) *last_access = file_time_2_utc(wfd.ftLastAccessTime); } else { err = GetLastError(); } } #else if(born) { std::string str(get_command_result((std::string("stat -c %W ") + file).c_str())); if(str.empty()) err = ENOENT; else *born = atoi(str.c_str()); } if(modify && err == 0) { std::string str(get_command_result((std::string("stat -c %Y ") + file).c_str())); if(str.empty()) err = ENOENT; else *modify = atoi(str.c_str()); } if(last_access && err == 0) { std::string str(get_command_result((std::string("stat -c %X ") + file).c_str())); if(str.empty()) err = ENOENT; else *last_access = atoi(str.c_str()); } #endif 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 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::stack_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)); path = utils::get_command_result("ulimit -s"); global_info::stack_size = SIZE_KB(atoi(path.c_str())); printf("Page size: %u\nMap size: %u\nCluster : %u\nStck size: %u\n", global_info::page_size, global_info::page_map_size, global_info::cluster_size, global_info::stack_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_nsec += MSEC_2_NS(timeout); to.tv_sec += to.tv_nsec / SEC_2_NS(1); to.tv_nsec %= SEC_2_NS(1); /*/ to.tv_sec += timeout / 1000; to.tv_nsec += MSEC_2_NS(timeout % 1000); if(to.tv_nsec >= SEC_2_NS(1)) { to.tv_sec++; to.tv_nsec -= SEC_2_NS(1); } ////////*////////////////// 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 func, std::string name, void* addr) { printf("stack size of thread '%s': %u\n", name.c_str(), utils::get_stack_size()); 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()); } } } #if OS_WIN DWORD WINAPI #else void* #endif safe_thread::raw_thread(void* lp) { safe_thread *obj = ((LPCRAWTHRD)lp)->obj; std::function func = ((LPCRAWTHRD)lp)->func; std::string name(((LPCRAWTHRD)lp)->name); void* addr = ((LPCRAWTHRD)lp)->addr; delete lp; obj->thread_worker(func, name, addr); return 0; } void safe_thread::set_exception_handler(std::function on_exception) { excep_handler_ = on_exception; } int safe_thread::start(std::function f, std::size_t stack, const char* thread_name, void* addr) { SAFETHRD st; st.name = thread_name ? thread_name : ""; if(stack == 0) { st.raw = false; st.thread.reset(new std::thread(&safe_thread::thread_worker, this, f, thread_name, addr)); } else { // st.thread.reset(new std::thread(std::thread(&safe_thread::thread_worker, this, f, thread_name, addr), std::move(stack))); LPCRAWTHRD param = new CRAWTHRD; int err = 0; param->obj = this; param->func = f; param->name = thread_name; param->addr = addr; #if OS_WIN st.thread_raw = CreateThread(NULL, stack, &safe_thread::raw_thread, param, 0, nullptr); if(!st.thread_raw) err = GetLastError(); #else pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, stack); st.raw = true; err = pthread_create(&st.thread_raw, &attr, &safe_thread::raw_thread, param); if(err) err = errno; pthread_attr_destroy(&attr); #endif if(err) { delete param; return err; } } { 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].raw) { #if OS_WIN WaitForSingleObject(threads_[i].thread_raw, INFINITE); CloseHandle(threads_[i].thread_raw); #else pthread_join(threads_[i].thread_raw, nullptr); #endif } else { 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; } //////////////////////////////////////////////////////////////////////////////////////////////// // safe_file safe_file::safe_file(const char* path) { set_file_path(path); } safe_file::~safe_file() {} uint32_t safe_file::checksum(const void* data, size_t bytes, uint32_t prev) { uint32_t chk = prev, mask[] = {0, 0x0ff, 0x0ffff, 0x0ffffff}; const uint32_t *d = (const uint32_t*)data; for(int i = 0; i < bytes / 4; ++i) chk ^= *d++; chk ^= *d & mask[bytes % 4]; return chk; } std::string safe_file::load_with_check(const char* file, bool ignore_lost) { FILE* src = fopen(file, "rb"); std::string cont(""); if(src) { long l = 0; fseek(src, 0, SEEK_END); l = ftell(src); fseek(src, 0, SEEK_SET); if(l > sizeof(SFHEAD)) { SFHEAD head; fread(&head, 1, sizeof(head), src); if(head.len == l - sizeof(head)) { char buf[256] = {0}; int r = fread(buf, 1, sizeof(buf), src); while(r > 0) { cont += std::string(buf, r); r = fread(buf, 1, sizeof(buf), src); } uint32_t chk = checksum(&head, sizeof(head)); chk = checksum(cont.c_str(), cont.length(), chk); if(chk) { utils::to_log(LOG_LEVEL_FATAL, "Checksum(%08x) of safe_file(%s) failed!\n", chk, file); cont = ""; } else { iotimes_ = head.iotimes; } } else { utils::to_log(LOG_LEVEL_FATAL, "safe_file(%s) length(%d) is mismatched(%d)!\n", file, (int)l, head.len); } } else { utils::to_log(LOG_LEVEL_FATAL, "safe_file(%s) length(%d) is too small!\n", file, (int)l); } fclose(src); } else if(!ignore_lost) { utils::to_log(LOG_LEVEL_FATAL, "open safe_file(%s) failed: %d(%s).\n", file, errno, strerror(errno)); } return std::move(cont); } void safe_file::set_file_path(const char* path) { path_ = path ? path : ""; iotimes_ = 0; } std::string safe_file::load(void) { std::string cont(load_with_check((path_ + tmp_appendix_).c_str(), true)); if(cont.empty()) cont = load_with_check(path_.c_str(), false); return std::move(cont); } int safe_file::save(const void* data, uint16_t bytes) { // io time ... if(iotimes_ == 0) { load(); } SFHEAD head; FILE *dst = nullptr; head.chksum = 0; head.iotimes = ++iotimes_; head.len = bytes; head.time = time(nullptr); head.chksum = checksum(&head, sizeof(head)); head.chksum = checksum(data, bytes, head.chksum); dst = fopen((path_ + tmp_appendix_).c_str(), "wb"); if(!dst) return errno; fwrite(&head, 1, sizeof(head), dst); fwrite(data, 1, bytes, dst); fclose(dst); return rename((path_ + tmp_appendix_).c_str(), path_.c_str()); }