Files
fastfetch/src/detection/terminalshell/terminalshell_linux.c
T

328 lines
9.4 KiB
C
Raw Normal View History

2021-06-14 23:22:14 +02:00
#include "fastfetch.h"
2022-10-02 17:52:09 +08:00
#include "detection/host/host.h"
2022-07-25 11:48:48 +02:00
#include "common/io.h"
#include "common/parsing.h"
#include "common/processing.h"
2022-10-08 16:04:55 +08:00
#include "terminalshell.h"
2021-06-14 23:22:14 +02:00
#include <ctype.h>
#include <string.h>
2022-06-18 14:25:31 +02:00
#include <stdlib.h>
2021-06-14 23:22:14 +02:00
#include <unistd.h>
#include <pthread.h>
static void setExeName(FFstrbuf* exe, const char** exeName)
{
assert(exe->length > 0);
2021-06-14 23:22:14 +02:00
uint32_t lastSlashIndex = ffStrbufLastIndexC(exe, '/');
if(lastSlashIndex < exe->length)
*exeName = exe->chars + lastSlashIndex + 1;
}
static void getProcessInformation(const char* pid, FFstrbuf* processName, FFstrbuf* exe, const char** exeName)
{
assert(processName->length > 0);
2021-06-14 23:22:14 +02:00
FFstrbuf cmdlineFilePath;
ffStrbufInit(&cmdlineFilePath);
2022-05-23 20:01:18 +02:00
ffStrbufAppendS(&cmdlineFilePath, "/proc/");
2021-06-14 23:22:14 +02:00
ffStrbufAppendS(&cmdlineFilePath, pid);
ffStrbufAppendS(&cmdlineFilePath, "/cmdline");
ffStrbufClear(exe);
if(ffAppendFileBuffer(cmdlineFilePath.chars, exe))
{
ffStrbufSubstrBeforeFirstC(exe, '\0'); //Trim the arguments
ffStrbufTrimLeft(exe, '-'); //Happens in TTY
}
2021-06-14 23:22:14 +02:00
if(exe->length == 0)
ffStrbufSet(exe, processName);
setExeName(exe, exeName);
ffStrbufDestroy(&cmdlineFilePath);
}
static void getTerminalShell(FFTerminalShellResult* result, const char* pid)
{
FFstrbuf statFilePath;
ffStrbufInit(&statFilePath);
2022-05-23 20:01:18 +02:00
ffStrbufAppendS(&statFilePath, "/proc/");
2021-06-14 23:22:14 +02:00
ffStrbufAppendS(&statFilePath, pid);
ffStrbufAppendS(&statFilePath, "/stat");
FILE* stat = fopen(statFilePath.chars, "r");
ffStrbufDestroy(&statFilePath);
if(stat == NULL)
return;
char name[256];
name[0] = '\0';
char ppid[256];
ppid[0] = '\0';
if(
2021-06-15 09:21:38 +02:00
fscanf(stat, "%*s (%255[^)]) %*c %255s", name, ppid) != 2 || //stat (comm) state ppid
!ffStrSet(name) ||
!ffStrSet(ppid) ||
2021-06-14 23:22:14 +02:00
*ppid == '-' ||
strcasecmp(ppid, "0") == 0
2021-06-15 09:21:38 +02:00
) {
fclose(stat);
return;
}
fclose(stat);
2021-06-14 23:22:14 +02:00
//Common programs that are between terminal and own process, but are not the shell
if(
2022-07-08 11:24:11 +02:00
strcasecmp(name, "sudo") == 0 ||
strcasecmp(name, "su") == 0 ||
strcasecmp(name, "doas") == 0 ||
strcasecmp(name, "strace") == 0 ||
strcasecmp(name, "sshd") == 0 ||
strcasecmp(name, "gdb") == 0 ||
strcasecmp(name, "guake-wrapped") == 0
2021-06-14 23:22:14 +02:00
) {
getTerminalShell(result, ppid);
return;
}
//Known shells
if(
strcasecmp(name, "bash") == 0 ||
strcasecmp(name, "sh") == 0 ||
strcasecmp(name, "zsh") == 0 ||
strcasecmp(name, "ksh") == 0 ||
strcasecmp(name, "fish") == 0 ||
strcasecmp(name, "dash") == 0 ||
2022-10-08 16:04:55 +08:00
strcasecmp(name, "pwsh") == 0 ||
2021-06-14 23:22:14 +02:00
strcasecmp(name, "git-shell") == 0
) {
ffStrbufSetS(&result->shellProcessName, name); // prevent from `fishbash`
2021-06-14 23:22:14 +02:00
getProcessInformation(pid, &result->shellProcessName, &result->shellExe, &result->shellExeName);
getTerminalShell(result, ppid);
return;
}
ffStrbufSetS(&result->terminalProcessName, name);
2021-06-14 23:22:14 +02:00
getProcessInformation(pid, &result->terminalProcessName, &result->terminalExe, &result->terminalExeName);
}
static void getTerminalFromEnv(FFTerminalShellResult* result)
{
if(
2021-06-15 09:21:38 +02:00
result->terminalProcessName.length > 0 &&
!ffStrbufStartsWithIgnCaseS(&result->terminalProcessName, "login") &&
ffStrbufIgnCaseCompS(&result->terminalProcessName, "(login)") != 0 &&
2021-06-14 23:22:14 +02:00
ffStrbufIgnCaseCompS(&result->terminalProcessName, "systemd") != 0 &&
ffStrbufIgnCaseCompS(&result->terminalProcessName, "init") != 0 &&
2021-06-15 09:21:38 +02:00
ffStrbufIgnCaseCompS(&result->terminalProcessName, "(init)") != 0 &&
ffStrbufIgnCaseCompS(&result->terminalProcessName, "0") != 0
2021-06-14 23:22:14 +02:00
) return;
char* term = NULL;
//SSH
if(getenv("SSH_CONNECTION") != NULL)
term = getenv("SSH_TTY");
//Windows Terminal
2022-06-18 14:51:10 +02:00
if(!ffStrSet(term) && (
getenv("WT_SESSION") != NULL ||
getenv("WT_PROFILE_ID") != NULL
)) term = "Windows Terminal";
2022-09-21 15:15:36 +08:00
//Alacritty
if(!ffStrSet(term) && (
getenv("ALACRITTY_SOCKET") != NULL ||
getenv("ALACRITTY_LOG") != NULL ||
getenv("ALACRITTY_WINDOW_ID") != NULL
)) term = "Alacritty";
2022-06-18 00:45:28 +02:00
//Termux
if(!ffStrSet(term) && (
getenv("TERMUX_VERSION") != NULL ||
getenv("TERMUX_MAIN_PACKAGE_FORMAT") != NULL ||
getenv("TMUX_TMPDIR") != NULL
)) term = "Termux";
2022-09-20 11:16:08 +02:00
//Konsole
if(!ffStrSet(term) && (
getenv("KONSOLE_VERSION") != NULL
)) term = "konsole";
2022-10-02 17:52:09 +08:00
//MacOS, mintty
2022-09-04 09:02:11 -07:00
if(!ffStrSet(term))
term = getenv("TERM_PROGRAM");
//We are in WSL but not in Windows Terminal
2022-10-02 17:52:09 +08:00
if(!ffStrSet(term))
{
const FFHostResult* host = ffDetectHost();
if(ffStrbufCompS(&host->productName, FF_HOST_PRODUCT_NAME_WSL) == 0 ||
ffStrbufCompS(&host->productName, FF_HOST_PRODUCT_NAME_MSYS) == 0) //TODO better WSL or MSYS detection
term = "conhost";
2022-10-02 17:52:09 +08:00
}
//Normal Terminal
if(!ffStrSet(term))
term = getenv("TERM");
2021-06-14 23:22:14 +02:00
//TTY
if(!ffStrSet(term) || strcasecmp(term, "linux") == 0)
2021-06-14 23:22:14 +02:00
term = ttyname(STDIN_FILENO);
if(ffStrSet(term))
{
ffStrbufSetS(&result->terminalProcessName, term);
ffStrbufSetS(&result->terminalExe, term);
setExeName(&result->terminalExe, &result->terminalExeName);
}
2021-06-14 23:22:14 +02:00
}
static void getUserShellFromEnv(FFTerminalShellResult* result)
{
ffStrbufAppendS(&result->userShellExe, getenv("SHELL"));
if(result->userShellExe.length == 0)
return;
2021-06-14 23:22:14 +02:00
setExeName(&result->userShellExe, &result->userShellExeName);
//If shell detection via processes failed
if(result->shellProcessName.length == 0 && result->userShellExe.length > 0)
{
ffStrbufAppendS(&result->shellProcessName, result->userShellExeName);
ffStrbufSet(&result->shellExe, &result->userShellExe);
setExeName(&result->shellExe, &result->shellExeName);
}
}
static void getShellVersionBash(FFstrbuf* exe, FFstrbuf* version)
{
ffProcessAppendStdOut(version, (char* const[]) {
"env",
"-i",
exe->chars,
"--norc",
"--noprofile",
"-c",
"printf \"%s\" \"$BASH_VERSION\"",
NULL
});
ffStrbufSubstrBeforeFirstC(version, '(');
}
static void getShellVersionZsh(FFstrbuf* exe, FFstrbuf* version)
{
ffProcessAppendStdOut(version, (char* const[]) {
exe->chars,
2021-06-15 12:35:44 +02:00
"--version",
2021-06-14 23:22:14 +02:00
NULL
});
2021-06-15 12:35:44 +02:00
ffStrbufTrimRight(version, '\n');
ffStrbufSubstrBeforeLastC(version, ' ');
ffStrbufSubstrAfterFirstC(version, ' ');
2021-06-14 23:22:14 +02:00
}
2021-06-15 09:51:55 +02:00
static void getShellVersionFish(FFstrbuf* exe, FFstrbuf* version)
{
ffProcessAppendStdOut(version, (char* const[]) {
exe->chars,
2021-06-15 12:35:44 +02:00
"--version",
2021-06-15 09:51:55 +02:00
NULL
});
2021-06-15 12:35:44 +02:00
ffStrbufTrimRight(version, '\n');
ffStrbufSubstrAfterLastC(version, ' ');
2021-06-15 09:51:55 +02:00
}
2021-06-14 23:22:14 +02:00
static void getShellVersionGeneric(FFstrbuf* exe, const char* exeName, FFstrbuf* version)
{
FFstrbuf command;
ffStrbufInit(&command);
ffStrbufAppendS(&command, "printf \"%s\" \"$");
ffStrbufAppendTransformS(&command, exeName, toupper);
ffStrbufAppendS(&command, "_VERSION\"");
ffProcessAppendStdOut(version, (char* const[]) {
"env",
"-i",
exe->chars,
"-c",
command.chars,
NULL
});
ffStrbufSubstrBeforeFirstC(version, '(');
ffStrbufRemoveStrings(version, 2, "-release", "release");
ffStrbufDestroy(&command);
}
static void getShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version)
{
ffStrbufClear(version);
2021-06-14 23:22:14 +02:00
if(strcasecmp(exeName, "bash") == 0)
getShellVersionBash(exe, version);
else if(strcasecmp(exeName, "zsh") == 0)
getShellVersionZsh(exe, version);
2022-10-08 16:04:55 +08:00
else if(strcasecmp(exeName, "fish") == 0 || strcasecmp(exeName, "pwsh") == 0)
2021-06-15 09:51:55 +02:00
getShellVersionFish(exe, version);
2021-06-14 23:22:14 +02:00
else
getShellVersionGeneric(exe, exeName, version);
}
2022-10-08 16:04:55 +08:00
const FFTerminalShellResult*
2022-10-08 16:07:59 +08:00
#if defined(__MSYS__) || defined(_WIN32)
2022-10-08 16:04:55 +08:00
ffDetectTerminalShellPosix
#else
ffDetectTerminalShell
#endif
(const FFinstance* instance)
2021-06-14 23:22:14 +02:00
{
2021-10-19 19:34:55 +02:00
FF_UNUSED(instance);
2021-06-14 23:22:14 +02:00
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static FFTerminalShellResult result;
static bool init = false;
pthread_mutex_lock(&mutex);
if(init)
{
pthread_mutex_unlock(&mutex);
return &result;
}
init = true;
ffStrbufInit(&result.shellProcessName);
2021-11-17 10:59:31 +01:00
ffStrbufInitA(&result.shellExe, 128);
2021-06-14 23:22:14 +02:00
result.shellExeName = result.shellExe.chars;
ffStrbufInit(&result.shellVersion);
ffStrbufInit(&result.terminalProcessName);
2021-11-17 10:59:31 +01:00
ffStrbufInitA(&result.terminalExe, 128);
2021-06-14 23:22:14 +02:00
result.terminalExeName = result.terminalExe.chars;
ffStrbufInit(&result.userShellExe);
result.userShellExeName = result.userShellExe.chars;
ffStrbufInit(&result.userShellVersion);
2022-01-09 16:51:57 +01:00
char ppid[32];
snprintf(ppid, sizeof(ppid) - 1, "%i", getppid());
2021-06-14 23:22:14 +02:00
getTerminalShell(&result, ppid);
getTerminalFromEnv(&result);
getUserShellFromEnv(&result);
getShellVersion(&result.shellExe, result.shellExeName, &result.shellVersion);
if(strcasecmp(result.shellExeName, result.userShellExeName) != 0)
getShellVersion(&result.userShellExe, result.userShellExeName, &result.userShellVersion);
else
ffStrbufSet(&result.userShellVersion, &result.shellVersion);
pthread_mutex_unlock(&mutex);
return &result;
}