summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSaikari <[email protected]>2026-01-27 05:54:28 +0300
committerSaikari <[email protected]>2026-01-27 05:54:28 +0300
commit41e460c869c15699d63f39e4fe433d88225818dc (patch)
tree0faa42100418d0db0919c5d3a24613c5f3f130e4
parent6e6cf755b207fe128e6e8a534d3df15f56d9c8a4 (diff)
add function to find shell from parent process in Linux
-rw-r--r--xmake/core/base/tty.lua149
1 files changed, 80 insertions, 69 deletions
diff --git a/xmake/core/base/tty.lua b/xmake/core/base/tty.lua
index 27788419b..788327d20 100644
--- a/xmake/core/base/tty.lua
+++ b/xmake/core/base/tty.lua
@@ -235,6 +235,84 @@ function tty.flush()
return tty
end
+-- find the shell from the parent process (linux)
+function tty._find_shell_from_parent()
+ if os.host() ~= "linux" or not os.isfile("/proc/self/stat") then
+ return
+ end
+
+ local shell
+ local pid = os.getpid()
+ for i = 1, 10 do
+ local stat = io.readfile("/proc/" .. pid .. "/stat")
+ if not stat or #stat == 0 then
+ local tmpfile = os.tmpfile()
+ os.runv("cp", {"/proc/" .. pid .. "/stat", tmpfile})
+ stat = io.readfile(tmpfile)
+ os.rm(tmpfile)
+ end
+
+ -- find last ')' to handle "pid (comm) state ppid"
+ local start = stat and stat:find(")", 1, true)
+ while start do
+ local next_p = stat:find(")", start + 1, true)
+ if not next_p then break end
+ start = next_p
+ end
+
+ if start then
+ local suffix = stat:sub(start + 1)
+ local fields = {}
+ for field in suffix:gmatch("%S+") do
+ table.insert(fields, field)
+ if #fields >= 2 then break end
+ end
+
+ local ppid = tonumber(fields[2])
+ if not ppid or ppid == 0 then break end
+
+ local shell_name = nil
+ local shell_path = nil
+ if os.isfile("/proc/" .. ppid .. "/exe") then
+ local ok, link = pcall(os.readlink, "/proc/" .. ppid .. "/exe")
+ if ok and link then
+ shell_path = link
+ end
+ end
+ if not shell_path and os.isfile("/proc/" .. ppid .. "/comm") then
+ shell_name = io.readfile("/proc/" .. ppid .. "/comm")
+ if not shell_name or #shell_name == 0 then
+ local tmpfile = os.tmpfile()
+ os.runv("cp", {"/proc/" .. ppid .. "/comm", tmpfile})
+ shell_name = io.readfile(tmpfile)
+ os.rm(tmpfile)
+ end
+ if shell_name then
+ shell_name = shell_name:match("^%s*(.-)%s*$")
+ end
+ end
+ if shell_path then
+ shell_name = path.filename(shell_path)
+ end
+
+ if shell_name then
+ shell_name = shell_name:gsub("^-", "")
+ for _, name in ipairs({"zsh", "bash", "fish", "nu", "elvish", "pwsh", "sh"}) do
+ if shell_name == name then
+ shell = name
+ break
+ end
+ end
+ if shell then break end
+ end
+ pid = ppid
+ else
+ break
+ end
+ end
+ return shell
+end
+
-- get shell name
function tty.shell()
local shell = tty._SHELL
@@ -258,75 +336,8 @@ function tty.shell()
end
end
-- try to find the shell from the parent process (linux)
- if not shell and os.host() == "linux" and os.isfile("/proc/self/stat") then
- local pid = os.getpid()
- for i = 1, 10 do
- local stat = io.readfile("/proc/" .. pid .. "/stat")
- if not stat or #stat == 0 then
- local tmpfile = os.tmpfile()
- os.runv("cp", {"/proc/" .. pid .. "/stat", tmpfile})
- stat = io.readfile(tmpfile)
- os.rm(tmpfile)
- end
-
- -- find last ')' to handle "pid (comm) state ppid"
- local start = stat and stat:find(")", 1, true)
- while start do
- local next_p = stat:find(")", start + 1, true)
- if not next_p then break end
- start = next_p
- end
-
- if start then
- local suffix = stat:sub(start + 1)
- local fields = {}
- for field in suffix:gmatch("%S+") do
- table.insert(fields, field)
- if #fields >= 2 then break end
- end
-
- local ppid = tonumber(fields[2])
- if not ppid or ppid == 0 then break end
-
- local shell_name = nil
- local shell_path = nil
- if os.isfile("/proc/" .. ppid .. "/exe") then
- local ok, link = pcall(os.readlink, "/proc/" .. ppid .. "/exe")
- if ok and link then
- shell_path = link
- end
- end
- if not shell_path and os.isfile("/proc/" .. ppid .. "/comm") then
- shell_name = io.readfile("/proc/" .. ppid .. "/comm")
- if not shell_name or #shell_name == 0 then
- local tmpfile = os.tmpfile()
- os.runv("cp", {"/proc/" .. ppid .. "/comm", tmpfile})
- shell_name = io.readfile(tmpfile)
- os.rm(tmpfile)
- end
- if shell_name then
- shell_name = shell_name:match("^%s*(.-)%s*$")
- end
- end
- if shell_path then
- shell_name = path.filename(shell_path)
- end
-
- if shell_name then
- shell_name = shell_name:gsub("^-", "")
- for _, name in ipairs({"zsh", "bash", "fish", "nu", "elvish", "pwsh", "sh"}) do
- if shell_name == name then
- shell = name
- break
- end
- end
- if shell then break end
- end
- pid = ppid
- else
- break
- end
- end
+ if not shell then
+ shell = tty._find_shell_from_parent()
end
if not shell then