Files
fastfetch/src/common/processing_linux.c
T

384 lines
11 KiB
C
Raw Normal View History

2021-04-18 15:19:21 +02:00
#include "fastfetch.h"
#include "common/processing.h"
#include "common/io/io.h"
2023-07-10 16:14:04 +08:00
#include "common/time.h"
#include "util/stringUtils.h"
2024-05-28 23:46:13 +08:00
#include "util/mallocHelper.h"
2021-04-18 15:19:21 +02:00
2022-06-18 14:25:31 +02:00
#include <stdlib.h>
2021-04-18 15:19:21 +02:00
#include <unistd.h>
2023-07-10 16:14:04 +08:00
#include <signal.h>
#include <poll.h>
2023-07-11 20:28:59 +08:00
#include <fcntl.h>
#include <errno.h>
2023-07-15 08:25:57 +08:00
#include <sys/wait.h>
2023-07-11 20:28:59 +08:00
#if defined(__FreeBSD__) || defined(__APPLE__)
#include <sys/types.h>
#include <sys/user.h>
#include <sys/sysctl.h>
#endif
2024-06-17 14:44:34 +08:00
#if defined(__APPLE__)
2024-05-28 23:46:13 +08:00
#include <libproc.h>
2024-06-17 14:44:34 +08:00
#elif defined(__sun)
#include <procfs.h>
2024-05-28 23:46:13 +08:00
#endif
2023-12-04 15:10:58 +08:00
enum { FF_PIPE_BUFSIZ = 8192 };
2021-04-18 15:19:21 +02:00
static inline int ffPipe2(int *fds, int flags)
{
#ifdef __APPLE__
if(pipe(fds) == -1)
return -1;
fcntl(fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | flags);
fcntl(fds[1], F_SETFL, fcntl(fds[1], F_GETFL) | flags);
return 0;
#else
return pipe2(fds, flags);
#endif
}
2023-07-10 21:02:13 +08:00
const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool useStdErr)
{
int pipes[2];
const int timeout = instance.config.general.processingTimeout;
if(ffPipe2(pipes, O_CLOEXEC) == -1)
return "pipe() failed";
pid_t childPid = fork();
if(childPid == -1)
{
close(pipes[0]);
close(pipes[1]);
return "fork() failed";
}
//Child
if(childPid == 0)
{
2023-12-04 15:10:58 +08:00
int nullFile = open("/dev/null", O_WRONLY | O_CLOEXEC);
2023-07-10 21:02:13 +08:00
dup2(pipes[1], useStdErr ? STDERR_FILENO : STDOUT_FILENO);
dup2(nullFile, useStdErr ? STDOUT_FILENO : STDERR_FILENO);
setenv("LANG", "C", 1);
execvp(argv[0], argv);
_exit(127);
}
//Parent
close(pipes[1]);
int FF_AUTO_CLOSE_FD childPipeFd = pipes[0];
char str[FF_PIPE_BUFSIZ];
2023-07-11 20:28:59 +08:00
while(1)
{
2023-07-11 20:28:59 +08:00
if (timeout >= 0)
{
2023-07-11 20:28:59 +08:00
struct pollfd pollfd = { childPipeFd, POLLIN, 0 };
if (poll(&pollfd, 1, timeout) == 0)
{
kill(childPid, SIGTERM);
waitpid(childPid, NULL, 0);
return "poll(&pollfd, 1, timeout) timeout (try increasing --processing-timeout)";
2023-07-11 20:28:59 +08:00
}
else if (pollfd.revents & POLLERR)
{
kill(childPid, SIGTERM);
waitpid(childPid, NULL, 0);
2023-07-11 20:28:59 +08:00
return "poll(&pollfd, 1, timeout) error";
}
}
2023-07-11 20:28:59 +08:00
ssize_t nRead = read(childPipeFd, str, FF_PIPE_BUFSIZ);
if (nRead > 0)
ffStrbufAppendNS(buffer, (uint32_t) nRead, str);
else if (nRead == 0)
{
int stat_loc = 0;
if (waitpid(childPid, &stat_loc, 0) == childPid)
{
if (!WIFEXITED(stat_loc))
return "child process exited abnormally";
if (WEXITSTATUS(stat_loc) == 127)
return "command was not found";
// We only handle 127 as an error. See `getTerminalVersionUrxvt` in `terminalshell.c`
return NULL;
}
return "waitpid() failed";
}
else if (nRead < 0)
break;
};
return "read(childPipeFd, str, FF_PIPE_BUFSIZ) failed";
}
void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath)
{
assert(processName->length > 0);
ffStrbufClear(exe);
#ifdef __linux__
char filePath[64];
snprintf(filePath, sizeof(filePath), "/proc/%d/cmdline", (int)pid);
2024-05-28 23:46:13 +08:00
if(ffReadFileBuffer(filePath, exe))
{
ffStrbufRecalculateLength(exe); //Trim the arguments
2024-05-28 23:46:13 +08:00
ffStrbufTrimRightSpace(exe);
ffStrbufTrimLeft(exe, '-'); //Login shells start with a dash
}
2024-05-28 23:46:13 +08:00
if (exePath)
{
2024-05-28 23:46:13 +08:00
snprintf(filePath, sizeof(filePath), "/proc/%d/exe", (int)pid);
ffStrbufEnsureFixedLengthFree(exePath, PATH_MAX);
ssize_t length = readlink(filePath, exePath->chars, exePath->allocated - 1);
if (length > 0) // doesn't contain trailing NUL
{
exePath->chars[length] = '\0';
exePath->length = (uint32_t) length;
}
}
#elif defined(__APPLE__)
size_t len = 0;
int mibs[] = { CTL_KERN, KERN_PROCARGS2, pid };
if (sysctl(mibs, sizeof(mibs) / sizeof(*mibs), NULL, &len, NULL, 0) == 0)
2024-05-28 23:46:13 +08:00
{// try get arg0
#ifndef MAC_OS_X_VERSION_10_15
//don't know why if don't let len longer, proArgs2 and len will change during the following sysctl() in old MacOS version.
len++;
#endif
FF_AUTO_FREE char* const procArgs2 = malloc(len);
if (sysctl(mibs, sizeof(mibs) / sizeof(*mibs), procArgs2, &len, NULL, 0) == 0)
{
// https://gist.github.com/nonowarn/770696#file-getargv-c-L46
uint32_t argc = *(uint32_t*) procArgs2;
const char* realExePath = procArgs2 + sizeof(argc);
const char* arg0 = memchr(realExePath, '\0', len - (size_t) (realExePath - procArgs2));
2024-05-28 23:46:13 +08:00
if (exePath)
ffStrbufSetNS(exePath, (uint32_t) (arg0 - realExePath), realExePath);
do arg0++; while (*arg0 == '\0');
assert(arg0 < procArgs2 + len);
2024-05-30 09:27:35 +08:00
if (argc > 1)
2024-05-30 09:27:35 +08:00
{
// #977
const char* p = strrchr(arg0, '/');
2024-05-30 09:27:35 +08:00
if (p)
p++;
else
p = arg0;
if (ffStrStartsWithIgnCase(p, "python")) // /opt/homebrew/Cellar/python@3.12/3.12.3/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python /Users/carter/.local/bin/xonsh
arg0 = p + strlen(p) + 1;
2024-05-30 09:27:35 +08:00
}
if (*arg0 == '-') arg0++; // Login shells
ffStrbufSetS(exe, arg0);
}
}
2024-05-28 23:46:13 +08:00
else
{
ffStrbufEnsureFixedLengthFree(exe, PATH_MAX);
int length = proc_pidpath(pid, exe->chars, exe->allocated);
if (length > 0)
{
exe->length = (uint32_t) length;
if (exePath)
ffStrbufSet(exePath, exe);
}
}
#elif defined(__FreeBSD__)
size_t size = ARG_MAX;
FF_AUTO_FREE char* args = malloc(size);
static_assert(ARG_MAX > PATH_MAX, "");
2024-05-28 23:46:13 +08:00
if(exePath && sysctl(
(int[]){CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, pid}, 4,
args, &size,
NULL, 0
) == 0)
ffStrbufSetNS(exePath, (uint32_t) (size - 1), args);
size = ARG_MAX;
if(sysctl(
(int[]){CTL_KERN, KERN_PROC, KERN_PROC_ARGS, pid}, 4,
args, &size,
NULL, 0
) == 0)
{
char* arg0 = args;
size_t arg0Len = strlen(args);
if (size > arg0Len + 1)
{
char* p = (char*) memrchr(args, '/', arg0Len);
if (p)
p++;
else
p = arg0;
if (ffStrStartsWith(p, "python")) // /usr/local/bin/python3.9 /home/carter/.local/bin/xonsh
{
arg0 += arg0Len + 1;
}
}
if (arg0[0] == '-') arg0++;
ffStrbufSetS(exe, arg0);
}
2024-06-17 14:44:34 +08:00
#elif defined(__sun)
char filePath[PATH_MAX];
snprintf(filePath, sizeof(filePath), "/proc/%d/psinfo", (int) pid);
psinfo_t proc;
if (ffReadFileData(filePath, sizeof(proc), &proc) == sizeof(proc))
{
ffStrbufSetS(exe, proc.pr_psargs);
ffStrbufSubstrBeforeFirstC(exe, ' ');
}
2024-06-17 15:18:02 +08:00
if (exePath)
2024-06-17 14:44:34 +08:00
{
2024-06-17 15:18:02 +08:00
snprintf(filePath, sizeof(filePath), "/proc/%d/path/a.out", (int) pid);
ffStrbufEnsureFixedLengthFree(exePath, PATH_MAX);
ssize_t length = readlink(filePath, exePath->chars, exePath->allocated - 1);
if (length > 0) // doesn't contain trailing NUL
{
exePath->chars[length] = '\0';
exePath->length = (uint32_t) length;
}
2024-06-17 14:44:34 +08:00
}
#endif
if(exe->length == 0)
ffStrbufSet(exe, processName);
assert(exe->length > 0);
uint32_t lastSlashIndex = ffStrbufLastIndexC(exe, '/');
if(lastSlashIndex < exe->length)
*exeName = exe->chars + lastSlashIndex + 1;
}
2024-05-28 23:46:13 +08:00
const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, int32_t* tty)
{
if (pid <= 0)
return "Invalid pid";
#ifdef __linux__
2024-05-28 23:46:13 +08:00
char procFilePath[64];
if (ppid)
{
snprintf(procFilePath, sizeof(procFilePath), "/proc/%d/stat", (int)pid);
char buf[PROC_FILE_BUFFSIZ];
ssize_t nRead = ffReadFileData(procFilePath, sizeof(buf) - 1, buf);
if(nRead <= 8)
2024-05-28 23:46:13 +08:00
return "ffReadFileData(/proc/pid/stat, PROC_FILE_BUFFSIZ-1, buf) failed";
buf[nRead] = '\0';
*ppid = 0;
static_assert(sizeof(*ppid) == sizeof(int), "");
ffStrbufEnsureFixedLengthFree(name, 255);
int tty_;
if(
sscanf(buf, "%*s (%255[^)]) %*c %d %*d %*d %d", name->chars, ppid, &tty_) < 2 || //stat (comm) state ppid pgrp session tty
name->chars[0] == '\0'
)
return "sscanf(stat) failed";
ffStrbufRecalculateLength(name);
if (tty)
*tty = tty_ & 0xFF;
}
else
{
snprintf(procFilePath, sizeof(procFilePath), "/proc/%d/comm", (int)pid);
ssize_t nRead = ffReadFileBuffer(procFilePath, name);
if(nRead <= 0)
2024-05-28 23:46:13 +08:00
return "ffReadFileBuffer(/proc/pid/comm, name) failed";
ffStrbufTrimRightSpace(name);
}
#elif defined(__APPLE__)
struct kinfo_proc proc;
size_t size = sizeof(proc);
if(sysctl(
(int[]){CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}, 4,
&proc, &size,
NULL, 0
))
return "sysctl(KERN_PROC_PID) failed";
2024-05-28 23:46:13 +08:00
ffStrbufSetS(name, proc.kp_proc.p_comm); //trancated to 16 chars
if (ppid)
*ppid = (pid_t)proc.kp_eproc.e_ppid;
if (tty)
{
*tty = ((proc.kp_eproc.e_tdev >> 24) & 0xFF) == 0x10
? proc.kp_eproc.e_tdev & 0xFFFFFF
: -1;
}
#elif defined(__FreeBSD__)
struct kinfo_proc proc;
size_t size = sizeof(proc);
if(sysctl(
(int[]){CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}, 4,
&proc, &size,
NULL, 0
))
return "sysctl(KERN_PROC_PID) failed";
2024-05-28 23:46:13 +08:00
ffStrbufSetS(name, proc.ki_comm);
if (ppid)
*ppid = (pid_t)proc.ki_ppid;
if (tty)
{
if (proc.ki_tdev != NODEV && proc.ki_flag & P_CONTROLT)
{
const char* ttyName = devname(proc.ki_tdev, S_IFCHR);
if (ffStrStartsWith(ttyName, "pts/"))
*tty = (int32_t) strtol(ttyName + strlen("pts/"), NULL, 10);
else
*tty = -1;
}
else
*tty = -1;
}
2024-06-17 13:35:26 +08:00
#elif defined(__sun)
2024-06-17 14:44:34 +08:00
char path[128];
snprintf(path, sizeof(path), "/proc/%d/psinfo", (int) pid);
psinfo_t proc;
if (ffReadFileData(path, sizeof(proc), &proc) != sizeof(proc))
return "ffReadFileData(psinfo) failed";
2024-06-17 13:35:26 +08:00
2024-06-17 14:44:34 +08:00
ffStrbufSetS(name, proc.pr_fname);
2024-06-17 13:35:26 +08:00
if (ppid)
2024-06-17 14:44:34 +08:00
*ppid = proc.pr_ppid;
2024-06-17 13:35:26 +08:00
if (tty)
2024-06-17 14:44:34 +08:00
*tty = (int) proc.pr_ttydev;
2024-06-17 13:35:26 +08:00
#else
return "Unsupported platform";
#endif
return NULL;
}