2022-10-08 16:04:55 +08:00
|
|
|
extern "C" {
|
|
|
|
|
#include "terminalshell.h"
|
|
|
|
|
#include "common/processing.h"
|
2022-10-14 17:19:36 +08:00
|
|
|
#include "common/thread.h"
|
2022-10-08 16:04:55 +08:00
|
|
|
}
|
|
|
|
|
#include "util/windows/wmi.hpp"
|
|
|
|
|
|
|
|
|
|
#include <inttypes.h>
|
|
|
|
|
#include <processthreadsapi.h>
|
|
|
|
|
#include <wchar.h>
|
|
|
|
|
|
|
|
|
|
struct ProcessInfo
|
|
|
|
|
{
|
|
|
|
|
uint32_t pid;
|
|
|
|
|
FFstrbuf psName;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static bool getProcessInfo(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe)
|
|
|
|
|
{
|
|
|
|
|
wchar_t query[256] = {};
|
2022-10-15 11:36:24 +08:00
|
|
|
swprintf(query, 256, L"SELECT %ls %ls ParentProcessId FROM Win32_Process WHERE ProcessId = %" PRIu32,
|
|
|
|
|
pname ? L"Name," : L"",
|
|
|
|
|
pname ? L"ExecutablePath," : L"",
|
|
|
|
|
pid);
|
2022-10-08 16:04:55 +08:00
|
|
|
|
|
|
|
|
IEnumWbemClassObject* pEnumerator = ffQueryWmi(query, nullptr);
|
|
|
|
|
if(!pEnumerator)
|
|
|
|
|
return false;
|
|
|
|
|
|
2022-10-15 11:36:24 +08:00
|
|
|
IWbemClassObject *pclsObj = nullptr;
|
2022-10-08 16:04:55 +08:00
|
|
|
ULONG uReturn = 0;
|
|
|
|
|
|
|
|
|
|
if(FAILED(pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn)) || uReturn == 0)
|
|
|
|
|
{
|
|
|
|
|
pEnumerator->Release();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(ppid)
|
|
|
|
|
{
|
|
|
|
|
uint64_t value;
|
|
|
|
|
ffGetWmiObjUnsigned(pclsObj, L"ParentProcessId", &value);
|
|
|
|
|
*ppid = (uint32_t) value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(pname)
|
|
|
|
|
ffGetWmiObjString(pclsObj, L"Name", pname);
|
|
|
|
|
|
|
|
|
|
if(exe)
|
|
|
|
|
ffGetWmiObjString(pclsObj, L"ExecutablePath", exe);
|
|
|
|
|
|
|
|
|
|
pclsObj->Release();
|
|
|
|
|
pEnumerator->Release();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-14 16:28:03 +08:00
|
|
|
extern "C" bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version);
|
2022-10-08 16:04:55 +08:00
|
|
|
|
|
|
|
|
static uint32_t getShellInfo(FFTerminalShellResult* result, uint32_t pid)
|
|
|
|
|
{
|
|
|
|
|
uint32_t ppid;
|
|
|
|
|
|
2022-10-14 18:38:18 +08:00
|
|
|
if(pid == 0 || !getProcessInfo(pid, &ppid, &result->shellProcessName, &result->shellExe))
|
2022-10-08 16:04:55 +08:00
|
|
|
return 0;
|
|
|
|
|
result->shellExeName = result->shellExe.chars + ffStrbufLastIndexC(&result->shellExe, '\\') + 1;
|
|
|
|
|
|
2022-10-09 16:51:53 +08:00
|
|
|
ffStrbufSet(&result->shellPrettyName, &result->shellProcessName);
|
|
|
|
|
if(ffStrbufEndsWithIgnCaseS(&result->shellPrettyName, ".exe"))
|
|
|
|
|
ffStrbufSubstrBefore(&result->shellPrettyName, result->shellPrettyName.length - 4);
|
2022-10-08 16:04:55 +08:00
|
|
|
|
2022-10-15 11:36:24 +08:00
|
|
|
//Common programs that are between terminal and own process, but are not the shell
|
|
|
|
|
if(
|
|
|
|
|
ffStrbufIgnCaseEqualS(&result->shellPrettyName, "sudo") ||
|
|
|
|
|
ffStrbufIgnCaseEqualS(&result->shellPrettyName, "su") ||
|
|
|
|
|
ffStrbufIgnCaseEqualS(&result->shellPrettyName, "doas") ||
|
|
|
|
|
ffStrbufIgnCaseEqualS(&result->shellPrettyName, "strace") ||
|
|
|
|
|
ffStrbufIgnCaseEqualS(&result->shellPrettyName, "sshd") ||
|
|
|
|
|
ffStrbufIgnCaseEqualS(&result->shellPrettyName, "gdb") ||
|
|
|
|
|
ffStrbufIgnCaseEqualS(&result->shellPrettyName, "lldb") ||
|
|
|
|
|
ffStrbufIgnCaseEqualS(&result->shellPrettyName, "guake-wrapped") ||
|
|
|
|
|
ffStrbufContainIgnCaseS(&result->shellPrettyName, "debug")
|
|
|
|
|
) {
|
|
|
|
|
ffStrbufClear(&result->shellProcessName);
|
|
|
|
|
ffStrbufClear(&result->shellPrettyName);
|
|
|
|
|
ffStrbufClear(&result->shellExe);
|
|
|
|
|
result->shellExeName = nullptr;
|
|
|
|
|
return getShellInfo(result, ppid);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-14 16:28:03 +08:00
|
|
|
ffStrbufClear(&result->shellVersion);
|
|
|
|
|
fftsGetShellVersion(&result->shellExe, result->shellPrettyName.chars, &result->shellVersion);
|
|
|
|
|
|
2022-10-15 11:36:24 +08:00
|
|
|
if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "pwsh"))
|
2022-10-09 16:51:53 +08:00
|
|
|
ffStrbufSetS(&result->shellPrettyName, "PowerShell");
|
2022-10-15 11:36:24 +08:00
|
|
|
else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "powershell"))
|
2022-10-09 21:33:54 +08:00
|
|
|
ffStrbufSetS(&result->shellPrettyName, "Windows PowerShell");
|
2022-10-15 11:36:24 +08:00
|
|
|
else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "powershell_ise"))
|
2022-10-12 00:13:49 +08:00
|
|
|
ffStrbufSetS(&result->shellPrettyName, "Windows PowerShell ISE");
|
2022-10-15 11:36:24 +08:00
|
|
|
else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "cmd"))
|
2022-10-09 21:33:54 +08:00
|
|
|
ffStrbufSetS(&result->shellPrettyName, "Command Prompt");
|
2022-10-15 11:36:24 +08:00
|
|
|
else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "nu"))
|
2022-10-14 18:38:18 +08:00
|
|
|
ffStrbufSetS(&result->shellPrettyName, "nushell");
|
2022-10-15 11:36:24 +08:00
|
|
|
else if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "explorer"))
|
2022-10-12 00:13:49 +08:00
|
|
|
{
|
|
|
|
|
ffStrbufSetS(&result->terminalPrettyName, "Windows Explorer"); // Started without shell
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2022-10-08 16:04:55 +08:00
|
|
|
|
|
|
|
|
return ppid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t getTerminalInfo(FFTerminalShellResult* result, uint32_t pid)
|
|
|
|
|
{
|
|
|
|
|
uint32_t ppid;
|
|
|
|
|
|
2022-10-14 18:38:18 +08:00
|
|
|
if(pid == 0 || !getProcessInfo(pid, &ppid, &result->terminalProcessName, &result->terminalExe))
|
2022-10-08 16:04:55 +08:00
|
|
|
return 0;
|
|
|
|
|
result->terminalExeName = result->terminalExe.chars + ffStrbufLastIndexC(&result->terminalExe, '\\');
|
|
|
|
|
|
2022-10-09 16:51:53 +08:00
|
|
|
ffStrbufSet(&result->terminalPrettyName, &result->terminalProcessName);
|
|
|
|
|
if(ffStrbufEndsWithIgnCaseS(&result->terminalPrettyName, ".exe"))
|
2022-10-09 17:47:30 +08:00
|
|
|
ffStrbufSubstrBefore(&result->terminalPrettyName, result->terminalPrettyName.length - 4);
|
2022-10-08 16:04:55 +08:00
|
|
|
|
2022-10-14 18:38:18 +08:00
|
|
|
if(
|
2022-10-15 11:36:24 +08:00
|
|
|
ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "pwsh") ||
|
|
|
|
|
ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "cmd") ||
|
|
|
|
|
ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "bash") ||
|
|
|
|
|
ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "zsh") ||
|
|
|
|
|
ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "fish") ||
|
|
|
|
|
ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "nu") ||
|
|
|
|
|
ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "powershell") ||
|
|
|
|
|
ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "powershell_ise")
|
2022-10-14 18:38:18 +08:00
|
|
|
) {
|
|
|
|
|
//We are nested shell
|
|
|
|
|
ffStrbufClear(&result->terminalProcessName);
|
|
|
|
|
ffStrbufClear(&result->terminalPrettyName);
|
|
|
|
|
ffStrbufClear(&result->terminalExe);
|
2022-10-15 11:36:24 +08:00
|
|
|
result->terminalExeName = nullptr;
|
2022-10-14 18:38:18 +08:00
|
|
|
return getTerminalInfo(result, ppid);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-15 11:36:24 +08:00
|
|
|
if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "WindowsTerminal"))
|
2022-10-09 16:51:53 +08:00
|
|
|
ffStrbufSetS(&result->terminalPrettyName, "Windows Terminal");
|
2022-10-15 11:36:24 +08:00
|
|
|
else if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "conhost"))
|
2022-10-09 16:51:53 +08:00
|
|
|
ffStrbufSetS(&result->terminalPrettyName, "Console Window Host");
|
2022-10-15 11:36:24 +08:00
|
|
|
else if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "Code"))
|
|
|
|
|
ffStrbufSetS(&result->terminalPrettyName, "Visual Studio Code");
|
|
|
|
|
else if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "explorer"))
|
2022-10-12 00:13:49 +08:00
|
|
|
ffStrbufSetS(&result->terminalPrettyName, "Windows Explorer");
|
2022-10-08 16:04:55 +08:00
|
|
|
|
|
|
|
|
return ppid;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-14 18:38:18 +08:00
|
|
|
static void getTerminalFromEnv(FFTerminalShellResult* result)
|
|
|
|
|
{
|
|
|
|
|
if(
|
|
|
|
|
result->terminalProcessName.length > 0 &&
|
|
|
|
|
!ffStrbufStartsWithIgnCaseS(&result->terminalProcessName, "login") &&
|
|
|
|
|
ffStrbufIgnCaseCompS(&result->terminalProcessName, "(login)") != 0 &&
|
|
|
|
|
ffStrbufIgnCaseCompS(&result->terminalProcessName, "systemd") != 0 &&
|
|
|
|
|
ffStrbufIgnCaseCompS(&result->terminalProcessName, "init") != 0 &&
|
|
|
|
|
ffStrbufIgnCaseCompS(&result->terminalProcessName, "(init)") != 0 &&
|
|
|
|
|
ffStrbufIgnCaseCompS(&result->terminalProcessName, "0") != 0
|
|
|
|
|
) return;
|
|
|
|
|
|
2022-10-15 11:36:24 +08:00
|
|
|
const char* term = nullptr;
|
2022-10-14 18:38:18 +08:00
|
|
|
|
|
|
|
|
//SSH
|
2022-10-15 11:36:24 +08:00
|
|
|
if(getenv("SSH_CONNECTION") != nullptr)
|
2022-10-14 18:38:18 +08:00
|
|
|
term = getenv("SSH_TTY");
|
|
|
|
|
|
|
|
|
|
//Windows Terminal
|
|
|
|
|
if(!term && (
|
2022-10-15 11:36:24 +08:00
|
|
|
getenv("WT_SESSION") != nullptr ||
|
|
|
|
|
getenv("WT_PROFILE_ID") != nullptr
|
2022-10-14 18:38:18 +08:00
|
|
|
)) term = "Windows Terminal";
|
|
|
|
|
|
|
|
|
|
//Alacritty
|
|
|
|
|
if(!term && (
|
2022-10-15 11:36:24 +08:00
|
|
|
getenv("ALACRITTY_SOCKET") != nullptr ||
|
|
|
|
|
getenv("ALACRITTY_LOG") != nullptr ||
|
|
|
|
|
getenv("ALACRITTY_WINDOW_ID") != nullptr
|
2022-10-14 18:38:18 +08:00
|
|
|
)) term = "Alacritty";
|
|
|
|
|
|
|
|
|
|
//Normal Terminal
|
|
|
|
|
if(!term)
|
|
|
|
|
term = getenv("TERM");
|
|
|
|
|
|
|
|
|
|
if(term)
|
|
|
|
|
{
|
|
|
|
|
ffStrbufSetS(&result->terminalProcessName, term);
|
|
|
|
|
ffStrbufSetS(&result->terminalPrettyName, term);
|
|
|
|
|
ffStrbufSetS(&result->terminalExe, term);
|
|
|
|
|
result->terminalExeName = "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-08 16:04:55 +08:00
|
|
|
const FFTerminalShellResult* ffDetectTerminalShell(const FFinstance* instance)
|
|
|
|
|
{
|
2022-10-14 17:19:36 +08:00
|
|
|
FF_UNUSED(instance);
|
|
|
|
|
|
|
|
|
|
static FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER;
|
2022-10-08 16:04:55 +08:00
|
|
|
static FFTerminalShellResult result;
|
|
|
|
|
static bool init = false;
|
2022-10-14 17:19:36 +08:00
|
|
|
ffThreadMutexLock(&mutex);
|
2022-10-08 16:04:55 +08:00
|
|
|
if(init)
|
2022-10-14 17:19:36 +08:00
|
|
|
{
|
|
|
|
|
ffThreadMutexUnlock(&mutex);
|
2022-10-08 16:04:55 +08:00
|
|
|
return &result;
|
2022-10-14 17:19:36 +08:00
|
|
|
}
|
2022-10-08 16:04:55 +08:00
|
|
|
init = true;
|
|
|
|
|
|
|
|
|
|
ffStrbufInit(&result.shellProcessName);
|
|
|
|
|
ffStrbufInitA(&result.shellExe, 128);
|
|
|
|
|
result.shellExeName = result.shellExe.chars;
|
2022-10-09 16:51:53 +08:00
|
|
|
ffStrbufInit(&result.shellPrettyName);
|
2022-10-08 16:04:55 +08:00
|
|
|
ffStrbufInit(&result.shellVersion);
|
|
|
|
|
|
|
|
|
|
ffStrbufInit(&result.terminalProcessName);
|
|
|
|
|
ffStrbufInitA(&result.terminalExe, 128);
|
|
|
|
|
result.terminalExeName = result.terminalExe.chars;
|
2022-10-09 16:51:53 +08:00
|
|
|
ffStrbufInit(&result.terminalPrettyName);
|
2022-10-08 16:04:55 +08:00
|
|
|
|
|
|
|
|
ffStrbufInit(&result.userShellExe);
|
|
|
|
|
result.userShellExeName = result.userShellExe.chars;
|
|
|
|
|
ffStrbufInit(&result.userShellVersion);
|
|
|
|
|
|
|
|
|
|
uint32_t ppid = GetCurrentProcessId();
|
|
|
|
|
if(!getProcessInfo(ppid, &ppid, nullptr, nullptr))
|
2022-10-14 17:19:36 +08:00
|
|
|
goto exit;
|
2022-10-08 16:04:55 +08:00
|
|
|
|
|
|
|
|
ppid = getShellInfo(&result, ppid);
|
2022-10-14 17:19:36 +08:00
|
|
|
getTerminalInfo(&result, ppid);
|
2022-10-14 18:38:18 +08:00
|
|
|
if(result.terminalProcessName.length == 0)
|
|
|
|
|
getTerminalFromEnv(&result);
|
2022-10-08 16:04:55 +08:00
|
|
|
|
2022-10-14 17:19:36 +08:00
|
|
|
exit:
|
|
|
|
|
ffThreadMutexUnlock(&mutex);
|
2022-10-08 16:04:55 +08:00
|
|
|
return &result;
|
|
|
|
|
}
|