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

422 lines
14 KiB
C
Raw Normal View History

2023-02-01 15:56:25 +01:00
#include "terminalshell.h"
#include "common/io/io.h"
#include "common/parsing.h"
#include "common/processing.h"
#include "common/thread.h"
2023-02-01 15:56:25 +01:00
#include "util/stringUtils.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>
2022-10-11 14:59:09 +08:00
#ifdef __APPLE__
#include <libproc.h>
2022-10-26 19:49:25 +08:00
#elif defined(__FreeBSD__)
#include <sys/types.h>
#include <sys/user.h>
#include <sys/sysctl.h>
2022-10-11 14:59:09 +08:00
#endif
2021-06-14 23:22:14 +02:00
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;
}
2022-10-11 14:59:09 +08:00
static void getProcessInformation(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, const char** exeName)
2021-06-14 23:22:14 +02:00
{
assert(processName->length > 0);
2022-10-11 14:59:09 +08:00
ffStrbufClear(exe);
2022-10-14 16:39:48 +08:00
#ifdef __linux__
2021-06-14 23:22:14 +02:00
2022-10-11 14:59:09 +08:00
char cmdlineFilePath[64];
snprintf(cmdlineFilePath, sizeof(cmdlineFilePath), "/proc/%d/cmdline", (int)pid);
if(ffAppendFileBuffer(cmdlineFilePath, exe))
{
ffStrbufSubstrBeforeFirstC(exe, '\0'); //Trim the arguments
ffStrbufTrimLeft(exe, '-'); //Happens in TTY
}
2021-06-14 23:22:14 +02:00
2022-10-11 14:59:09 +08:00
#elif defined(__APPLE__)
int length = proc_pidpath((int)pid, exe->chars, exe->allocated);
if(length > 0)
exe->length = (uint32_t)length;
2023-08-24 10:49:59 +08:00
#elif defined(__FreeBSD__)
2022-10-11 14:59:09 +08:00
2022-10-26 19:49:25 +08:00
size_t size = exe->allocated;
if(!sysctl(
(int[]){CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, pid}, 4,
2023-02-14 16:33:58 +08:00
exe->chars, &size,
2022-10-26 19:49:25 +08:00
NULL, 0
))
2023-09-16 20:46:49 +08:00
exe->length = (uint32_t)size - 1;
2022-10-11 14:59:09 +08:00
#endif
2021-06-14 23:22:14 +02:00
if(exe->length == 0)
ffStrbufSet(exe, processName);
setExeName(exe, exeName);
}
2022-10-11 14:59:09 +08:00
static const char* getProcessNameAndPpid(pid_t pid, char* name, pid_t* ppid)
2021-06-14 23:22:14 +02:00
{
2022-10-11 14:59:09 +08:00
const char* error = NULL;
2021-06-14 23:22:14 +02:00
2022-10-14 16:39:48 +08:00
#ifdef __linux__
2021-06-14 23:22:14 +02:00
2022-10-11 14:59:09 +08:00
char statFilePath[64];
snprintf(statFilePath, sizeof(statFilePath), "/proc/%d/stat", (int)pid);
FILE* stat = fopen(statFilePath, "r");
2021-06-14 23:22:14 +02:00
if(stat == NULL)
2022-10-11 14:59:09 +08:00
return "fopen(statFilePath, \"r\") failed";
*ppid = 0;
if(
fscanf(stat, "%*s (%255[^)]) %*c %d", name, ppid) != 2 || //stat (comm) state ppid
!ffStrSet(name) ||
*ppid == 0
)
error = "fscanf(stat) failed";
fclose(stat);
2021-06-14 23:22:14 +02:00
2022-10-11 14:59:09 +08:00
#elif defined(__APPLE__)
struct proc_bsdshortinfo proc;
if(proc_pidinfo(pid, PROC_PIDT_SHORTBSDINFO, 0, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) <= 0)
error = "proc_pidinfo(pid) failed";
else
{
*ppid = (pid_t)proc.pbsi_ppid;
strncpy(name, proc.pbsi_comm, 16); //trancated to 16 chars
}
#else
2022-10-26 19:49:25 +08:00
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
))
error = "sysctl(KERN_PROC_PID) failed";
else
{
*ppid = (pid_t)proc.ki_ppid;
strncpy(name, proc.ki_comm, COMMLEN);
}
2022-10-11 14:59:09 +08:00
#endif
return error;
}
static void getTerminalShell(FFTerminalShellResult* result, pid_t pid)
{
2021-06-14 23:22:14 +02:00
char name[256];
name[0] = '\0';
2022-10-11 14:59:09 +08:00
pid_t ppid = 0;
2021-06-14 23:22:14 +02:00
2022-10-11 14:59:09 +08:00
if(getProcessNameAndPpid(pid, name, &ppid))
2021-06-15 09:21:38 +02:00
return;
2021-06-14 23:22:14 +02:00
//Common programs that are between terminal and own process, but are not the shell
if(
strcasecmp(name, "sh") == 0 || //This prevents us from detecting things like pipes and redirects, i hope nobody uses plain `sh` as shell
strcasecmp(name, "sudo") == 0 ||
strcasecmp(name, "su") == 0 ||
strcasecmp(name, "strace") == 0 ||
strcasecmp(name, "sshd") == 0 ||
strcasecmp(name, "gdb") == 0 ||
strcasecmp(name, "lldb") == 0 ||
strcasecmp(name, "login") == 0 ||
strcasecmp(name, "ltrace") == 0 ||
strcasecmp(name, "perf") == 0 ||
strcasecmp(name, "guake-wrapped") == 0 ||
strcasestr(name, "debug") != NULL ||
strcasestr(name, "command-not-found") != NULL ||
ffStrEndsWith(name, ".sh")
2021-06-14 23:22:14 +02:00
) {
getTerminalShell(result, ppid);
return;
}
//Known shells
2023-05-24 14:48:56 +08:00
if (
strcasecmp(name, "ash") == 0 ||
2021-06-14 23:22:14 +02:00
strcasecmp(name, "bash") == 0 ||
strcasecmp(name, "zsh") == 0 ||
strcasecmp(name, "ksh") == 0 ||
2023-07-20 16:11:02 +08:00
strcasecmp(name, "mksh") == 0 ||
strcasecmp(name, "oksh") == 0 ||
2022-10-27 16:30:49 +08:00
strcasecmp(name, "csh") == 0 ||
strcasecmp(name, "tcsh") == 0 ||
2021-06-14 23:22:14 +02:00
strcasecmp(name, "fish") == 0 ||
strcasecmp(name, "dash") == 0 ||
2022-10-08 16:04:55 +08:00
strcasecmp(name, "pwsh") == 0 ||
strcasecmp(name, "nu") == 0 ||
2023-05-24 14:48:56 +08:00
strcasecmp(name, "git-shell") == 0 ||
2023-11-15 16:30:46 +08:00
strcasecmp(name, "elvish") == 0 ||
strcasecmp(name, "oil.ovm") == 0 ||
2023-05-24 14:48:56 +08:00
(strcasecmp(name, "python") == 0 && getenv("XONSH_VERSION"))
2021-06-14 23:22:14 +02:00
) {
if (result->shellProcessName.length == 0)
{
result->shellPid = (uint32_t) pid;
ffStrbufSetS(&result->shellProcessName, name);
getProcessInformation(pid, &result->shellProcessName, &result->shellExe, &result->shellExeName);
}
2021-06-14 23:22:14 +02:00
getTerminalShell(result, ppid);
return;
}
#ifdef __APPLE__
// https://github.com/fastfetch-cli/fastfetch/discussions/501
if (ffStrEndsWith(name, " (figterm)"))
getProcessNameAndPpid(ppid, name, &ppid);
#endif
result->terminalPid = (uint32_t) pid;
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") &&
!ffStrbufIgnCaseEqualS(&result->terminalProcessName, "(login)") &&
#ifdef __APPLE__
!ffStrbufIgnCaseEqualS(&result->terminalProcessName, "launchd") &&
!ffStrbufIgnCaseEqualS(&result->terminalProcessName, "stable") && //for WarpTerminal
#else
!ffStrbufIgnCaseEqualS(&result->terminalProcessName, "systemd") &&
!ffStrbufIgnCaseEqualS(&result->terminalProcessName, "init") &&
!ffStrbufIgnCaseEqualS(&result->terminalProcessName, "(init)") &&
#endif
2021-06-15 09:21:38 +02:00
ffStrbufIgnCaseCompS(&result->terminalProcessName, "0") != 0
2021-06-14 23:22:14 +02:00
) return;
2022-10-14 18:38:18 +08:00
const char* term = NULL;
//SSH
if(getenv("SSH_CONNECTION") != NULL)
term = getenv("SSH_TTY");
#ifdef __linux__
//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";
//ConEmu
if(!ffStrSet(term) && (
getenv("ConEmuPID") != NULL
)) term = "ConEmu";
#endif
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";
#ifdef __ANDROID__
2022-06-18 00:45:28 +02:00
//Termux
if(!ffStrSet(term) && (
getenv("TERMUX_VERSION") != NULL ||
getenv("TERMUX_MAIN_PACKAGE_FORMAT") != NULL
2023-09-03 20:49:46 +08:00
)) term = "com.termux";
#endif
2022-06-18 00:45:28 +02:00
#ifdef __linux__
2022-09-20 11:16:08 +02:00
//Konsole
if(!ffStrSet(term) && (
getenv("KONSOLE_VERSION") != NULL
)) term = "konsole";
#endif
2022-09-20 11:16:08 +02:00
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");
if(!ffStrSet(term))
term = getenv("LC_TERMINAL");
//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)
2021-06-14 23:22:14 +02:00
{
//If shell detection via processes failed
2023-09-13 14:47:13 +08:00
if(result->shellProcessName.length == 0 && instance.state.platform.userShell.length > 0)
2021-06-14 23:22:14 +02:00
{
2023-09-13 14:47:13 +08:00
ffStrbufSet(&result->shellExe, &instance.state.platform.userShell);
2021-06-14 23:22:14 +02:00
setExeName(&result->shellExe, &result->shellExeName);
2023-09-13 14:47:13 +08:00
ffStrbufAppendS(&result->shellProcessName, result->shellExeName);
2021-06-14 23:22:14 +02:00
}
}
static void getShellVersionGeneric(FFstrbuf* exe, const char* exeName, FFstrbuf* version)
{
FF_STRBUF_AUTO_DESTROY command = ffStrbufCreate();
2021-06-14 23:22:14 +02:00
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, (const char*[]) { "-release", "release" });
2021-06-14 23:22:14 +02:00
}
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version);
2021-06-14 23:22:14 +02:00
static void getShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version)
{
ffStrbufClear(version);
if(!fftsGetShellVersion(exe, exeName, version))
2021-06-14 23:22:14 +02:00
getShellVersionGeneric(exe, exeName, version);
}
bool fftsGetTerminalVersion(FFstrbuf* processName, FFstrbuf* exe, FFstrbuf* version);
const FFTerminalShellResult* ffDetectTerminalShell()
2021-06-14 23:22:14 +02:00
{
static FFTerminalShellResult result;
static bool init = false;
if(init)
return &result;
init = true;
#ifdef __APPLE__
const uint32_t exePathLen = PROC_PIDPATHINFO_MAXSIZE;
#elif defined(MAXPATH)
const uint32_t exePathLen = MAXPATH;
#elif defined(PATH_MAX)
const uint32_t exePathLen = PATH_MAX;
#else
const uint32_t exePathLen = 260;
#endif
2021-06-14 23:22:14 +02:00
ffStrbufInit(&result.shellProcessName);
ffStrbufInitA(&result.shellExe, exePathLen);
2021-06-14 23:22:14 +02:00
result.shellExeName = result.shellExe.chars;
ffStrbufInit(&result.shellVersion);
result.shellPid = 0;
2021-06-14 23:22:14 +02:00
ffStrbufInit(&result.terminalProcessName);
ffStrbufInitA(&result.terminalExe, exePathLen);
2021-06-14 23:22:14 +02:00
result.terminalExeName = result.terminalExe.chars;
result.terminalPid = 0;
2021-06-14 23:22:14 +02:00
2022-10-11 14:59:09 +08:00
getTerminalShell(&result, getppid());
2021-06-14 23:22:14 +02:00
getTerminalFromEnv(&result);
getUserShellFromEnv(&result);
2021-06-14 23:22:14 +02:00
getShellVersion(&result.shellExe, result.shellExeName, &result.shellVersion);
2022-10-14 19:02:51 +08:00
if(ffStrbufEqualS(&result.shellProcessName, "pwsh"))
2023-07-25 16:09:09 +08:00
ffStrbufInitStatic(&result.shellPrettyName, "PowerShell");
2022-10-14 19:02:51 +08:00
else if(ffStrbufEqualS(&result.shellProcessName, "nu"))
2023-07-25 16:09:09 +08:00
ffStrbufInitStatic(&result.shellPrettyName, "nushell");
2023-05-24 14:48:56 +08:00
else if(ffStrbufIgnCaseEqualS(&result.shellProcessName, "python") && getenv("XONSH_VERSION"))
2023-07-25 16:09:09 +08:00
ffStrbufInitStatic(&result.shellPrettyName, "xonsh");
2023-11-15 16:30:46 +08:00
else if(ffStrbufIgnCaseEqualS(&result.shellProcessName, "oil.ovm"))
ffStrbufInitStatic(&result.shellPrettyName, "Oils");
2022-10-14 19:02:51 +08:00
else
{
2023-06-12 17:26:37 +02:00
// https://github.com/fastfetch-cli/fastfetch/discussions/280#discussioncomment-3831734
2022-10-14 19:02:51 +08:00
ffStrbufInitS(&result.shellPrettyName, result.shellExeName);
}
2022-10-09 16:51:53 +08:00
if(result.terminalExeName[0] == '.' && ffStrEndsWith(result.terminalExeName, "-wrapped"))
{
// For NixOS. Ref: #510 and https://github.com/NixOS/nixpkgs/pull/249428
// We use terminalProcessName when detecting version and font, overriding it for simplification
ffStrbufSetNS(
&result.terminalProcessName,
(uint32_t) (strlen(result.terminalExeName) - strlen(".-wrapped")),
result.terminalExeName + 1);
}
2023-06-22 13:14:38 +08:00
if(ffStrbufEqualS(&result.terminalProcessName, "wezterm-gui"))
2023-07-25 16:09:09 +08:00
ffStrbufInitStatic(&result.terminalPrettyName, "WezTerm");
if(ffStrbufStartsWithS(&result.terminalProcessName, "tmux:"))
ffStrbufInitStatic(&result.terminalPrettyName, "tmux");
2023-09-03 20:49:46 +08:00
#if defined(__ANDROID__)
else if(ffStrbufEqualS(&result.terminalProcessName, "com.termux"))
ffStrbufInitStatic(&result.terminalPrettyName, "Termux");
#elif defined(__linux__) || defined(__FreeBSD__)
2023-06-22 13:14:38 +08:00
else if(ffStrbufStartsWithS(&result.terminalProcessName, "gnome-terminal-"))
2023-10-08 09:24:13 +08:00
ffStrbufInitStatic(&result.terminalPrettyName, "GNOME Terminal");
else if(ffStrbufStartsWithS(&result.terminalProcessName, "kgx"))
ffStrbufInitStatic(&result.terminalPrettyName, "GNOME Console");
2023-10-24 15:47:53 +08:00
else if(ffStrbufEqualS(&result.terminalProcessName, "urxvt") ||
ffStrbufEqualS(&result.terminalProcessName, "urxvtd") ||
ffStrbufEqualS(&result.terminalProcessName, "rxvt")
)
ffStrbufInitStatic(&result.terminalPrettyName, "rxvt-unicode");
#elif defined(__APPLE__)
else if(ffStrbufEqualS(&result.terminalProcessName, "iTerm.app") || ffStrbufStartsWithS(&result.terminalProcessName, "iTermServer-"))
2023-07-25 16:09:09 +08:00
ffStrbufInitStatic(&result.terminalPrettyName, "iTerm");
2022-10-14 19:02:51 +08:00
else if(ffStrbufEqualS(&result.terminalProcessName, "Apple_Terminal"))
2023-07-25 16:09:09 +08:00
ffStrbufInitStatic(&result.terminalPrettyName, "Apple Terminal");
else if(ffStrbufEqualS(&result.terminalProcessName, "WarpTerminal"))
2023-07-25 16:09:09 +08:00
ffStrbufInitStatic(&result.terminalPrettyName, "Warp");
#endif
2022-10-14 19:02:51 +08:00
else if(strncmp(result.terminalExeName, result.terminalProcessName.chars, result.terminalProcessName.length) == 0) // if exeName starts with processName, print it. Otherwise print processName
2022-10-09 16:51:53 +08:00
ffStrbufInitS(&result.terminalPrettyName, result.terminalExeName);
else
ffStrbufInitCopy(&result.terminalPrettyName, &result.terminalProcessName);
ffStrbufInit(&result.terminalVersion);
fftsGetTerminalVersion(&result.terminalProcessName, &result.terminalExe, &result.terminalVersion);
2021-06-14 23:22:14 +02:00
return &result;
}