63 lines
1.5 KiB
Go
63 lines
1.5 KiB
Go
package agent
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"os/exec"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// termsBySID are the running terminals by their session, so the panel can
|
|
// ask where one of them is.
|
|
var termsBySID sync.Map // sid -> *PTY
|
|
|
|
// Cwd is the folder the terminal is in now: that of the program in front
|
|
// (the shell, or what it runs), else of the shell itself.
|
|
func (p *PTY) Cwd() (string, error) {
|
|
pid := 0
|
|
if g, err := unix.IoctlGetInt(int(p.f.Fd()), unix.TIOCGPGRP); err == nil && g > 0 {
|
|
pid = g
|
|
}
|
|
if pid > 0 {
|
|
if d, err := procCwd(pid); err == nil {
|
|
return d, nil
|
|
}
|
|
}
|
|
return procCwd(p.cmd.Process.Pid)
|
|
}
|
|
|
|
// procCwd is a process's working folder: /proc on Linux, lsof elsewhere.
|
|
func procCwd(pid int) (string, error) {
|
|
if runtime.GOOS == "linux" {
|
|
return os.Readlink("/proc/" + strconv.Itoa(pid) + "/cwd")
|
|
}
|
|
out, err := exec.Command("lsof", "-a", "-p", strconv.Itoa(pid), "-d", "cwd", "-Fn").Output()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
for _, l := range strings.Split(string(out), "\n") {
|
|
if strings.HasPrefix(l, "n/") {
|
|
return l[1:], nil
|
|
}
|
|
}
|
|
return "", errors.New("папка терминала не найдена")
|
|
}
|
|
|
|
// termCwd answers the panel: where the terminal of session sid is now.
|
|
func termCwd(sid string) (any, error) {
|
|
v, ok := termsBySID.Load(sid)
|
|
if !ok {
|
|
return nil, errors.New("терминал не найден")
|
|
}
|
|
d, err := v.(*PTY).Cwd()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return map[string]string{"path": d}, nil
|
|
}
|