From 53c248c453d99c97219aa4dca582074082dd48be Mon Sep 17 00:00:00 2001 From: disarray2077 <86157825+disarray2077@users.noreply.github.com> Date: Sun, 11 May 2025 19:28:12 -0300 Subject: [PATCH] linux: Implement `Process` methods and `Path.GetActualPathName` --- BeefySysLib/platform/posix/PosixCommon.cpp | 264 ++++++++++++++++++++- 1 file changed, 257 insertions(+), 7 deletions(-) diff --git a/BeefySysLib/platform/posix/PosixCommon.cpp b/BeefySysLib/platform/posix/PosixCommon.cpp index 9e54babe..4c16148b 100644 --- a/BeefySysLib/platform/posix/PosixCommon.cpp +++ b/BeefySysLib/platform/posix/PosixCommon.cpp @@ -832,6 +832,17 @@ BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetComputerName(char* outStr, int* inOutS // BfpProcess +struct BfpProcess +{ + pid_t mProcessId; + String mImageName; + + BfpProcess() + { + mProcessId = -1; + } +}; + BFP_EXPORT intptr BFP_CALLTYPE BfpProcess_GetCurrentId() { return getpid(); @@ -844,18 +855,87 @@ BFP_EXPORT bool BFP_CALLTYPE BfpProcess_IsRemoteMachine(const char* machineName) BFP_EXPORT BfpProcess* BFP_CALLTYPE BfpProcess_GetById(const char* machineName, int processId, BfpProcessResult* outResult) { +#ifdef BF_PLATFORM_LINUX + pid_t pid = static_cast(processId); + + char proc_dir[64]; + snprintf(proc_dir, sizeof(proc_dir), "/proc/%d", pid); + + if (access(proc_dir, F_OK) != 0) + { + OUTRESULT(BfpProcessResult_NotFound); + return NULL; + } + + BfpProcess* process = new BfpProcess(); + process->mProcessId = pid; + return process; +#else NOT_IMPL; return NULL; +#endif } BFP_EXPORT void BFP_CALLTYPE BfpProcess_Enumerate(const char* machineName, BfpProcess** outProcesses, int* inOutProcessesSize, BfpProcessResult* outResult) { +#ifdef BF_PLATFORM_LINUX + DIR* procDir = opendir("/proc"); + if (!procDir) + { + *inOutProcessesSize = 0; + return; + } + + Beefy::Array processList; + struct dirent* entry; + while ((entry = readdir(procDir)) != NULL) + { + const char* name = entry->d_name; + bool is_pid = name[0] != '\0'; + for (const char* p = name; *p; ++p) + { + if (*p < '0' || *p > '9') + { + is_pid = false; + break; + } + } + + if (is_pid) + { + pid_t pid = static_cast(atoi(name)); + if (pid == 0) + continue; + + BfpProcess* proc = new BfpProcess(); + proc->mProcessId = pid; + processList.Add(proc); + } + } + + closedir(procDir); + + if (static_cast(processList.size()) > *inOutProcessesSize) + { + *inOutProcessesSize = static_cast(processList.size()); + OUTRESULT(BfpProcessResult_InsufficientBuffer); + for (BfpProcess* p_del : processList) + delete p_del; + return; + } + + for (size_t i = 0; i < processList.size(); ++i) + outProcesses[i] = processList[i]; + *inOutProcessesSize = static_cast(processList.size()); + OUTRESULT(BfpProcessResult_Ok); +#else NOT_IMPL; +#endif } BFP_EXPORT void BFP_CALLTYPE BfpProcess_Release(BfpProcess* process) { - NOT_IMPL; + delete process; } BFP_EXPORT bool BFP_CALLTYPE BfpProcess_WaitFor(BfpProcess* process, int waitMS, int* outExitCode, BfpProcessResult* outResult) @@ -865,18 +945,49 @@ BFP_EXPORT bool BFP_CALLTYPE BfpProcess_WaitFor(BfpProcess* process, int waitMS, BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetMainWindowTitle(BfpProcess* process, char* outTitle, int* inOutTitleSize, BfpProcessResult* outResult) { - NOT_IMPL; + String title; + TryStringOut(title, outTitle, inOutTitleSize, (BfpResult*)outResult); } BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetProcessName(BfpProcess* process, char* outName, int* inOutNameSize, BfpProcessResult* outResult) { - NOT_IMPL; + if (process->mImageName.IsEmpty()) + { +#ifdef BF_PLATFORM_LINUX + char path[PATH_MAX]; + snprintf(path, sizeof(path), "/proc/%d/exe", process->mProcessId); + char name_buf[PATH_MAX] = {0}; + ssize_t len = readlink(path, name_buf, sizeof(name_buf) - 1); + if (len != -1) + { + name_buf[len] = '\0'; + process->mImageName.Append(basename(name_buf)); + } + else + { + // Only 15 characters of the process name are returned by this, but it works for all processes. + snprintf(path, sizeof(path), "/proc/%d/comm", process->mProcessId); + FILE* fp = fopen(path, "r"); + if (fp) + { + if (fgets(name_buf, sizeof(name_buf), fp)) { + size_t len = strcspn(name_buf, "\n"); + name_buf[len] = '\0'; + process->mImageName.Append(name_buf, len); + } + fclose(fp); + } + } +#else + NOT_IMPL; +#endif + } + TryStringOut(process->mImageName, outName, inOutNameSize, (BfpResult*)outResult); } BFP_EXPORT int BFP_CALLTYPE BfpProcess_GetProcessId(BfpProcess* process) { - NOT_IMPL; - return 0; + return process->mProcessId; } // BfpSpawn @@ -2392,9 +2503,148 @@ BFP_EXPORT void BFP_CALLTYPE BfpFile_GetFullPath(const char* inPath, char* outPa TryStringOut(str, outPath, inOutPathSize, (BfpResult*)outResult); } -BFP_EXPORT void BFP_CALLTYPE BfpFile_GetActualPath(const char* inPath, char* outPath, int* inOutPathSize, BfpFileResult* outResult) +BFP_EXPORT void BFP_CALLTYPE BfpFile_GetActualPath(const char* inPathC, char* outPathC, int* inOutPathSize, BfpFileResult* outResult) { - NOT_IMPL; + String inPath = inPathC; + String outPath; + + // Check for '/../' backtracking - handle those first + { + int i = 0; + int32 lastComponentStart = -1; + + while (i < inPath.mLength) + { + // Skip until path separator + while ((i < inPath.mLength) && (inPath[i] != DIR_SEP_CHAR) && (inPath[i] != DIR_SEP_CHAR_ALT)) + ++i; + + if (lastComponentStart != -1) + { + if ((i - lastComponentStart == 2) && (inPath[lastComponentStart] == '.') && (inPath[lastComponentStart + 1] == '.')) + { + // Backtrack + while ((lastComponentStart > 0) && + ((inPath[lastComponentStart - 1] == DIR_SEP_CHAR) || (inPath[lastComponentStart - 1] == DIR_SEP_CHAR_ALT))) + lastComponentStart--; + while ((lastComponentStart > 0) && (inPath[lastComponentStart - 1] != DIR_SEP_CHAR) && (inPath[lastComponentStart - 1] != DIR_SEP_CHAR_ALT)) + lastComponentStart--; + inPath.Remove(lastComponentStart, i - lastComponentStart + 1); + i = lastComponentStart; + continue; + } + else if ((i - lastComponentStart == 1) && (inPath[lastComponentStart] == '.')) + { + inPath.Remove(lastComponentStart, i - lastComponentStart + 1); + i = lastComponentStart; + continue; + } + } + + ++i; + // Ignore multiple slashes in a row + while ((i < inPath.mLength) && ((inPath[i] == DIR_SEP_CHAR) || (inPath[i] == DIR_SEP_CHAR_ALT))) + ++i; + + lastComponentStart = i; + } + } + + int32 i = 0; + int length = (int)inPath.length(); + + if (length >= 1) + { + // Handle root paths starting with '/' or '\' + if ((inPath[0] == DIR_SEP_CHAR) || (inPath[0] == DIR_SEP_CHAR_ALT)) + { + i++; // start after initial slash + outPath.Append(DIR_SEP_CHAR); + } + else + { + // Relative path - prepend current working directory + char cwd[PATH_MAX]; + if (getcwd(cwd, PATH_MAX) != NULL) + { + outPath.Append(cwd); + if (outPath[outPath.length() - 1] != DIR_SEP_CHAR) + outPath.Append(DIR_SEP_CHAR); + } + } + } + + int32 lastComponentStart = i; + bool addSeparator = false; + String subName; + + while (i < length) + { + // skip until path separator + while ((i < length) && (inPath[i] != DIR_SEP_CHAR) && (inPath[i] != DIR_SEP_CHAR_ALT)) + ++i; + + if (addSeparator) + outPath.Append(DIR_SEP_CHAR); + + subName.Clear(); + subName = inPath.Substring(0, i); + for (int j = 0; j < (int)subName.length(); j++) + if (subName[j] == DIR_SEP_CHAR_ALT) + subName[j] = DIR_SEP_CHAR; + + String parentPath; + String componentName; + int32 lastSep = subName.LastIndexOf(DIR_SEP_CHAR); + + if (lastSep != -1) + { + parentPath = subName.Substring(0, lastSep); + if (parentPath.length() == 0) + parentPath = "/"; + componentName = subName.Substring(lastSep + 1); + } + else + { + parentPath = "."; + componentName = subName; + } + + bool found = false; + DIR* dir = opendir(parentPath.c_str()); + if (dir != NULL) + { + struct dirent* entry = NULL; + while ((entry = readdir(dir)) != NULL) + { + // Linux is case-sensitive, but we do a case-insensitive comparison + // to help find the correct case for the file + if (strcasecmp(entry->d_name, componentName.c_str()) == 0) + { + outPath.Append(entry->d_name); + found = true; + break; + } + } + closedir(dir); + } + + if (!found) + { + // If not found, use the original component name + outPath.Append(inPath.Substring(lastComponentStart, i - lastComponentStart)); + } + + ++i; + // Ignore multiple slashes in a row + while ((i < length) && ((inPath[i] == DIR_SEP_CHAR) || (inPath[i] == DIR_SEP_CHAR_ALT))) + ++i; + + lastComponentStart = i; + addSeparator = true; + } + + TryStringOut(outPath, outPathC, inOutPathSize, (BfpResult*)outResult); } // BfpFindFileData