Files
andrey 94084fb351 Merge branch 'master' into webasm
Переработка ветки под систему фиче-флагов master:
- CMake-секция PIP_WASM (платформа, аналог PICO/FREERTOS): принудительный
  OFF PIP_HAS_SOCKET/PROCESS/DYNLIB/SERIAL и PIP_BUILD_CONSOLE/USB/OPENCL/
  FFTW/LUA/MQTT_CLIENT/HTTP_SERVER/HTTP_CLIENT до pip_resolve_feature_flags();
  таблица зависимостей довыключает client_server/cloud/introspection
- Файлы, покрытые фиче-флагами master (piethernet/piserial/pican/pilibrary/
  piprocess): взята master-версия, EMSCRIPTEN-стубы webasm удалены как
  мёртвый код (нет флага — нет класса)
- Сохранены EMSCRIPTEN-гварды там, где флаги не покрывают поведение:
  pihidevice (компилируется под wasm), piinit (initSignals/initLocale/
  initUsername/initOSname/initHWInfo), pisysteminfo (instance/machineKey),
  pithread (gettid-фоллбэк), picout (EM_ASM console.log),
  pibase_macros (PIP_MIN_MSLEEP), pipiplatform.h (EMSCRIPTEN/ARCH_BITS)
- Исправлен AUTO-merge в PISystemInfo::machineKey() (семантический баг:
  пустой результат при PIP_HAS_FILESYSTEM=OFF)

Проверено: нативный ctest 364/364 (build-verify), wasm-сборка (build_wasm,
лишь libpip.a + libpip_io_utils.a + pcre2), node-смоук-тест PICout/PISystemInfo
2026-09-19 11:01:39 +03:00

568 lines
14 KiB
C++

/*
PIP - Platform Independent Primitives
Initialization
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "piinit.h"
#include "piincludes_p.h"
#if !defined(_PIP_INIT_STUB_)
# include "pidir.h"
# include "piliterals_string.h"
# include "piobject.h"
# include "piprocess.h"
# include "piresourcesstorage.h"
# include "pisignals.h"
# include "pisysteminfo.h"
# include "pitime.h"
# ifdef ESP_PLATFORM
# include "esp_system.h"
# endif
# ifdef WINDOWS
# ifdef PIP_HAS_SOCKET
# include <winsock2.h>
# else
# include <windows.h>
# endif
extern FILETIME __pi_ftjan1970;
extern PINtQueryTimerResolution getTimerResolutionAddr;
extern PINtSetTimerResolution setTimerResolutionAddr;
void __PISetTimerResolution() {
if (setTimerResolutionAddr == NULL || getTimerResolutionAddr == NULL) return;
ULONG _max(0), _min(0), _cur(0);
// printf("getTimerResolution ...\n");
LONG q = getTimerResolutionAddr(&_max, &_min, &_cur);
// printf("getTimerResolution %d %lu %lu %lu\n", q, _min, _max, _cur);
if (q == 0) setTimerResolutionAddr(_min, TRUE, &_cur);
// printf("setTimerResolution %lu\n", cur);
}
# else
# include <pthread.h>
# ifndef EMSCRIPTEN
# include <pwd.h>
# endif // EMSCRIPTEN
# include <sys/utsname.h>
# ifdef BLACKBERRY
# include <signal.h>
# else
# include <csignal>
# endif
# endif
# ifdef MAC_OS
# include <mach/clock.h>
# include <mach/mach.h>
# include <mach/mach_traps.h>
extern clock_serv_t __pi_mac_clock;
# endif
# ifdef PIP_ICU
# define U_NOEXCEPT
# include <unicode/uclean.h>
# include <unicode/ucnv.h>
# endif
# ifdef HAS_LOCALE
static locale_t currentLocale_t = 0;
# endif
PRIVATE_DEFINITION_START(PIInit)
# ifdef WINDOWS
HMODULE ntlib = nullptr;
# endif
bool delete_locs = false;
PRIVATE_DEFINITION_END(PIInit)
void __sighandler__(PISignals::Signal s) {
// piCout << Hex << int(s);
if (s == PISignals::StopTTYInput || s == PISignals::StopTTYOutput) piMSleep(10);
# if defined(PIP_INTROSPECTION) && defined(PIP_HAS_FILESYSTEM)
if (s == PISignals::UserDefined1) {
# ifdef PIP_HAS_PROCESS
int pid = PIProcess::currentPID();
# else
int pid = 0;
# endif
dumpApplicationToFile(PIDir::home().path() + PIDir::separator + PIStringAscii("_PIP_DUMP_") + PIString::fromNumber(pid));
}
# endif
}
void PIInit::initPlatform() {
# ifdef WINDOWS
# ifdef PIP_HAS_SOCKET
// WinSock inint
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
# endif
// Timers init
SYSTEMTIME jan1970 = {1970, 1, 4, 1, 0, 0, 0, 0};
SystemTimeToFileTime(&jan1970, &__pi_ftjan1970);
LARGE_INTEGER pf;
pf.QuadPart = -1;
if (QueryPerformanceFrequency(&pf) != 0) __pi_perf_freq = pf.QuadPart;
if (__pi_perf_freq == 0) __pi_perf_freq = -1;
// Sleep precision init
PRIVATE->ntlib = LoadLibraryA("ntdll.dll");
if (PRIVATE->ntlib) {
getTimerResolutionAddr = (PINtQueryTimerResolution)GetProcAddress(PRIVATE->ntlib, "NtQueryTimerResolution");
setTimerResolutionAddr = (PINtSetTimerResolution)GetProcAddress(PRIVATE->ntlib, "NtSetTimerResolution");
__PISetTimerResolution();
}
# endif
# ifdef MAC_OS
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &__pi_mac_clock);
# endif
}
void PIInit::initSignals() {
# ifndef ANDROID
PISignals::setSlot(__sighandler__);
PISignals::grabSignals(PISignals::UserDefined1);
# ifndef WINDOWS
# ifndef EMSCRIPTEN
PISignals::grabSignals(PISignals::StopTTYInput | PISignals::StopTTYOutput);
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGALRM);
sigprocmask(SIG_BLOCK, &ss, 0);
pthread_sigmask(SIG_BLOCK, &ss, 0);
signal(SIGPIPE, SIG_IGN);
# endif // EMSCRIPTEN
# endif // WINDOWS
# endif // ANDROID
}
void PIInit::initLocale() {
# ifdef WINDOWS
// Windows branch
# ifdef PIP_ICU
UErrorCode e((UErrorCode)0);
u_init(&e);
PRIVATE->delete_locs = true;
CPINFOEX cpinfo;
int l = 0;
GetCPInfoEx(CP_OEMCP, 0, &cpinfo);
for (l = 0; l < MAX_PATH; ++l)
if (cpinfo.CodePageName[l] == '\0' || cpinfo.CodePageName[l] == ' ') break;
__sysoemname__ = new char[256];
piZeroMemory(__sysoemname__, 256);
memcpy(__sysoemname__, "ibm-", 4);
memcpy(&(__sysoemname__[4]), cpinfo.CodePageName, l);
# else
__syslocname__ = (char *)CP_ACP;
__sysoemname__ = (char *)CP_OEMCP;
__utf8name__ = (char *)CP_UTF8;
# endif
setlocale(LC_ALL, "");
setlocale(LC_NUMERIC, "C");
# else // WINDOWS
// Non-Windows branch
# ifdef HAS_LOCALE
// std::cout << "has locale" << std::endl;
if (currentLocale_t != 0) {
freelocale(currentLocale_t);
currentLocale_t = 0;
}
currentLocale_t = newlocale(LC_ALL, setlocale(LC_ALL, "C"), 0);
setlocale(LC_CTYPE, "en_US.UTF-8");
# else // HAS_LOCALE
# ifndef EMSCRIPTEN
setlocale(LC_ALL, "");
setlocale(LC_NUMERIC, "C");
# endif // EMSCRIPTEN
# endif // HAS_LOCALE
PRIVATE->delete_locs = false;
__syslocname__ = __sysoemname__ = 0;
__utf8name__ = const_cast<char *>("UTF-8");
# ifdef PIP_ICU
UErrorCode e((UErrorCode)0);
u_init(&e);
# endif // PIP_ICU
# endif // WINDOWS
}
void PIInit::initPaths() {
# ifdef WINDOWS
// execute command
int argc_(0);
wchar_t ** argv_ = CommandLineToArgvW(GetCommandLineW(), &argc_);
if (argc_ > 0 && argv_ != 0) sinfo->execCommand = argv_[0];
LocalFree(argv_);
# else
# ifndef PIP_EMBEDDED
PIStringList ifpathes;
ifpathes << "/bin/ifconfig"_a << "/sbin/ifconfig"_a << "/usr/bin/ifconfig"_a << "/usr/sbin/ifconfig"_a;
for (const auto & i: ifpathes) {
if (fileExists(i)) {
sinfo->ifconfigPath = i;
break;
}
}
# endif // PIP_EMBEDDED
# endif // WINDOWS
}
void PIInit::initHostname() {
# ifdef PIP_HAS_SOCKET
char cbuff[1024];
piZeroMemory(cbuff, 1024);
if (gethostname(cbuff, 1023) == 0) {
sinfo->hostname = cbuff;
}
# endif
}
void PIInit::initUsername() {
# ifdef WINDOWS
char cbuff[1024];
piZeroMemory(cbuff, 1024);
ulong unlen = 1023;
if (GetUserNameA(cbuff, &unlen) != 0) sinfo->user = cbuff;
# else
# if !defined(ESP_PLATFORM) && !defined(EMSCRIPTEN)
passwd * ps = getpwuid(getuid());
if (ps)
sinfo->user = ps->pw_name;
else {
char cbuff[1024];
piZeroMemory(cbuff, 1024);
char * l = getlogin();
if (l) sinfo->user = l;
}
# endif // WINDOWS
# endif // WINDOWS
}
void PIInit::initOSname() {
# ifndef LINUX
sinfo->OS_name =
# ifdef WINDOWS
"Windows"_a;
# elif defined(QNX)
"QNX"_a;
# elif defined(MAC_OS)
"MacOS"_a;
# elif defined(ANDROID)
"Android"_a;
# elif defined(FREE_BSD)
"FreeBSD"_a;
# elif defined(FREERTOS)
"FreeRTOS"_a;
# elif defined(EMSCRIPTEN)
"Emscripten"_a;
# else
"Unknown"_a;
# endif
# endif
}
void PIInit::initHWInfo() {
# ifdef WINDOWS
// OS version
OSVERSIONINFOEX os_ver;
piZeroMemory(os_ver);
os_ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (GetVersionEx((LPOSVERSIONINFO)&os_ver) != FALSE) {
PIConstChars ver_name;
# define _VERSION_(major, minor) (major << 8 | minor)
DWORD win_ver = _VERSION_(os_ver.dwMajorVersion, os_ver.dwMinorVersion);
switch (win_ver) {
case _VERSION_(5, 0): ver_name = "2000"; break;
case _VERSION_(5, 1): ver_name = "XP"; break;
case _VERSION_(5, 2): ver_name = "Server 2003"; break;
default:
if (os_ver.wProductType == VER_NT_WORKSTATION) {
switch (win_ver) {
case _VERSION_(6, 0): ver_name = "Vista"; break;
case _VERSION_(6, 1): ver_name = "7"; break;
case _VERSION_(6, 2): ver_name = "8"; break;
case _VERSION_(6, 3): ver_name = "8.1"; break;
case _VERSION_(10, 0):
if (os_ver.dwBuildNumber < 22000)
ver_name = "10";
else
ver_name = "11";
break;
}
} else {
switch (win_ver) {
case _VERSION_(6, 0): ver_name = "Server 2008"; break;
case _VERSION_(6, 1): ver_name = "Server 2008 R2"; break;
case _VERSION_(6, 2): ver_name = "Server 2012"; break;
case _VERSION_(6, 3): ver_name = "Server 2012 R2"; break;
case _VERSION_(10, 0): ver_name = "Server 2016"; break;
}
}
}
# undef _VERSION
sinfo->OS_version = ver_name;
if (sinfo->OS_version.isEmpty()) sinfo->OS_version = "%1.%2"_a.arg(os_ver.dwMajorVersion).arg(os_ver.dwMinorVersion);
if (os_ver.dwBuildNumber != 0) sinfo->OS_version += " (build %1)"_a.arg(os_ver.dwBuildNumber);
} else {
// fallback
DWORD dwVersion = GetVersion();
DWORD dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
DWORD dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
sinfo->OS_version = "%1.%2"_a.arg(dwMajorVersion).arg(dwMinorVersion);
}
// architecture
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
sinfo->processorsCount = sysinfo.dwNumberOfProcessors;
switch (sysinfo.wProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_AMD64: sinfo->architecture = "x86_64"_a; break;
case PROCESSOR_ARCHITECTURE_ARM: sinfo->architecture = "arm"_a; break;
case PROCESSOR_ARCHITECTURE_IA64: sinfo->architecture = "Intel Itanium-based"_a; break;
case PROCESSOR_ARCHITECTURE_INTEL: sinfo->architecture = "x86"_a; break;
case PROCESSOR_ARCHITECTURE_UNKNOWN:
default: sinfo->architecture = "unknown"_a; break;
}
// logical processors
DWORD procinfo_bytes = 0;
PIByteArray procinfo_buffer;
GetLogicalProcessorInformationEx(RelationProcessorCore, nullptr, &procinfo_bytes);
piCout << procinfo_bytes;
if (procinfo_bytes > 0) {
int cntP = 0, cntE = 0;
procinfo_buffer.resize(procinfo_bytes);
GetLogicalProcessorInformationEx(RelationProcessorCore,
(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)procinfo_buffer.data(),
&procinfo_bytes);
DWORD offset = 0;
while (offset < procinfo_bytes) {
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)procinfo_buffer.data(offset);
offset += info->Size;
if (info->Processor.EfficiencyClass == 0)
cntE++;
else
cntP++;
}
sinfo->processorsCountP = cntP;
sinfo->processorsCountE = cntE;
} else {
printf("GetLogicalProcessorInformationEx error\n");
}
# else
# ifdef EMSCRIPTEN
sinfo->processorsCount = 1;
sinfo->OS_version = "WASM"_a;
sinfo->architecture = "wasm32"_a;
# elif defined(ESP_PLATFORM)
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
sinfo->processorsCount = chip_info.cores;
sinfo->architecture = "Xtensa LX6";
// printf("silicon revision %d, ", chip_info.revision);
sinfo->OS_version = esp_get_idf_version();
# else
sinfo->processorsCount = piMaxi(1, int(sysconf(_SC_NPROCESSORS_ONLN)));
struct utsname uns;
if (uname(&uns) == 0) {
sinfo->OS_version = uns.release;
sinfo->architecture = uns.machine;
# ifdef LINUX
sinfo->OS_name = uns.sysname;
# endif
}
# endif
# endif // WINDOWS
}
void PIInit::initFinalize() {
if (sinfo->processorsCountP + sinfo->processorsCountE != sinfo->processorsCount) {
sinfo->processorsCountE = 0;
sinfo->processorsCountP = sinfo->processorsCount;
}
}
PIInit::PIInit() {
sinfo = PISystemInfo::instance();
sinfo->execDateTime = PIDateTime::current();
initPlatform();
initSignals();
initLocale();
initPaths();
initHostname();
initUsername();
initOSname();
initHWInfo();
initFinalize();
}
PIInit::~PIInit() {
PIResourcesStorage::instance()->clear();
# ifdef WINDOWS
# ifdef PIP_HAS_SOCKET
WSACleanup();
# endif
if (PRIVATE->ntlib) FreeLibrary(PRIVATE->ntlib);
PRIVATE->ntlib = nullptr;
# endif
# ifdef MAC_OS
mach_port_deallocate(mach_task_self(), __pi_mac_clock);
# endif
if (PRIVATE->delete_locs) {
if (__syslocname__) delete[] __syslocname__;
if (__sysoemname__) delete[] __sysoemname__;
}
# ifdef PIP_ICU
u_cleanup();
# endif
}
PIInit * PIInit::instance() {
static PIInit ret;
return &ret;
}
bool PIInit::isBuildOptionEnabled(PIInit::BuildOption o) {
switch (o) {
case boICU:
return
# ifdef PIP_ICU
true;
# else
false;
# endif
case boUSB:
return
# ifdef PIP_USB
true;
# else
false;
# endif
case boCrypt:
return
# ifdef PIP_CRYPT
true;
# else
false;
# endif
case boIntrospection:
return
# ifdef PIP_INTROSPECTION
true;
# else
false;
# endif
case boFFTW:
return
# ifdef PIP_FFTW
true;
# else
false;
# endif
case boCompress:
return
# ifdef PIP_COMPRESS
true;
# else
false;
# endif
case boOpenCL:
return
# ifdef PIP_OPENCL
true;
# else
false;
# endif
case boCloud:
return
# ifdef PIP_CLOUD
true;
# else
false;
# endif
case boConsole:
return
# ifdef PIP_CONSOLE
true;
# else
false;
# endif
default: return false;
}
return false;
}
PIStringList PIInit::buildOptions() {
PIStringList ret;
if (isBuildOptionEnabled(boICU)) ret << "ICU";
if (isBuildOptionEnabled(boUSB)) ret << "USB";
if (isBuildOptionEnabled(boCrypt)) ret << "Crypt";
if (isBuildOptionEnabled(boIntrospection)) ret << "Introspection";
if (isBuildOptionEnabled(boFFTW)) ret << "FFTW";
if (isBuildOptionEnabled(boCompress)) ret << "Compress";
if (isBuildOptionEnabled(boOpenCL)) ret << "OpenCL";
if (isBuildOptionEnabled(boCloud)) ret << "Cloud";
if (isBuildOptionEnabled(boConsole)) ret << "Console";
return ret;
}
bool PIInit::fileExists(const PIString & p) {
FILE * f = fopen(p.data(), "r");
if (f == 0) return false;
fclose(f);
return true;
}
#endif // _PIP_INIT_STUB_