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 }