diff --git a/internal/agent/terminal.go b/internal/agent/terminal.go index 488589f..5a6c46e 100644 --- a/internal/agent/terminal.go +++ b/internal/agent/terminal.go @@ -20,8 +20,16 @@ type PTY struct { // `su -l`, which only a root agent can do without a password. func StartPTY(user string) (*PTY, error) { var cmd *exec.Cmd + env := append(os.Environ(), "TERM=xterm-256color") if user == "" || user == currentUser() { cmd = exec.Command(defaultShell(), "-l") + // A login shell does not change directory by itself: it would start + // where the service runs, "/". Start it at home, and say where home + // is, as a root service often has no $HOME. + if home := homeDir(""); isDir(home) { + cmd.Dir = home + env = append(env, "HOME="+home) + } } else { if !validUserName(user) { return nil, fmt.Errorf("недопустимое имя пользователя %q", user) @@ -37,8 +45,13 @@ func StartPTY(user string) (*PTY, error) { return nil, fmt.Errorf("su не найден: %w", err) } cmd = exec.Command(su, "-l", user) + // su -l goes to the user's home; starting there too keeps the shell + // out of "/" should it not. + if home := homeDir(user); isDir(home) { + cmd.Dir = home + } } - cmd.Env = append(os.Environ(), "TERM=xterm-256color") + cmd.Env = env f, err := pty.Start(cmd) if err != nil { return nil, err @@ -46,6 +59,11 @@ func StartPTY(user string) (*PTY, error) { return &PTY{cmd: cmd, f: f}, nil } +func isDir(p string) bool { + info, err := os.Stat(p) + return err == nil && info.IsDir() +} + // Read reads terminal output. func (p *PTY) Read(b []byte) (int, error) { return p.f.Read(b) }