/* ICFS: Interactively Controlled File System Copyright (C) 2024-2025 Fedir Kovalov This program can be distributed under the terms of the GNU GPLv2. See the file LICENSE. */ #include "proc_operations.h" #include #include #include #include #include /** * @brief Returns the PID of the main thread (i.e., the process ID) of the * process that the given thread ID (tid) belongs to. * * In Linux, threads within the same process share the same thread group ID * (TGID), which is equal to the PID of the main thread. This function retrieves * the TGID by parsing `/proc//status`, effectively returning the main * thread's PID. * * @param tid The thread ID (TID) of any thread in the process. * @return pid_t The process ID (main thread's PID), or 0 on error (invalid tid * or file read failure). */ pid_t get_main_thread_pid(pid_t tid) { // Validate input if (tid <= 0) { // Invalid TID: TIDs are always positive in Linux return 0; } char path[PATH_MAX]; snprintf(path, sizeof(path), "/proc/%d/status", tid); FILE *fp = fopen(path, "r"); if (!fp) { return 0; // Could not open the file } pid_t tgid = 0; char line[256]; while (fgets(line, sizeof(line), fp)) { if (sscanf(line, "Tgid: %d", &tgid) == 1) { break; } } fclose(fp); // Debugging check: If the provided tid is not the main thread's tid, // this logs a message but does not affect the return value. // This is useful for detecting cases where non-main threads are being // inspected. if (tgid != tid) { fprintf(stderr, "[ICFS] The tid and and pid wasn't equal. tid:%d, pid:%d.\n", tid, tgid); } return tgid; } /** * @brief Retrieves the full path of the executable for a given process ID. * * This function reads the `/proc//exe` symbolic link, which points to the * executable file of the process. The returned string is dynamically allocated * and must be freed by the caller. * * @param pid The process ID to query. * @return char* Dynamically allocated string containing the executable path, or * NULL on failure. */ char *get_process_name_by_pid(const int pid) { char path[1024]; sprintf(path, "/proc/%d/exe", pid); size_t size = 128; char *name = malloc(size); if (name == NULL) { fprintf(stderr, "[ICFS] Could not get process name by pid %d", pid); perror(""); return NULL; } while (1) { ssize_t len = readlink(path, name, size); if (len == -1) { fprintf(stderr, "[ICFS] Could not get process name by pid %d", pid); perror(""); free(name); return NULL; } // If the buffer was too small, double its size and try again if ((size_t)len >= size) { size *= 2; char *new_name = realloc(name, size); if (!new_name) { free(name); return NULL; } name = new_name; } else { // readlink does not null-terminate, so we must do it manually name[len] = '\0'; break; } } return name; } /** * @brief Finds the parent process ID of a given process. * * This function parses the `/proc//status` file to extract the `PPid` * field, which represents the parent process ID. This is useful for tracing * process lineage. * * @param pid The process ID of the process to find the parent of. * @return pid_t The parent process ID, or 0 if the parent could not be * determined. */ pid_t get_parent_pid(pid_t pid) { pid_t ppid = 0; char path[256]; snprintf(path, sizeof(path), "/proc/%d/status", pid); // Use %d for signed pid_t FILE *file = fopen(path, "r"); if (file == NULL) { perror("[ICFS] Failed to open /proc//status"); return 0; } char line[256]; while (fgets(line, sizeof(line), file)) { // The "PPid:" field in `/proc//status` is followed by a tab character if (sscanf(line, "PPid:\t%d", &ppid) == 1) { fclose(file); return ppid; } } fclose(file); return 0; // Parent PID not found }