Pty doesn't get closed, althoguh close() syscall is called

Viewed 51

I'm implementing features of an ssh server, so given a shell request I open a pty-tty pair.
A snippet:

import (
    "github.com/creack/pty"
    ...
)

func attachPty(channel ssh.Channel, shell *exec.Cmd) {
    mypty, err := pty.Start(shell)
    go func() {
        io.Copy(channel, mypty) // (1) ; could also be substituted with read() syscall, same problem
    }
    go func() {
        io.Copy(mypty, channel) // (2) - this returns on channel exit with eof, so let's close mypty
        if err := syscall.Close(int(mypty.Fd())); err != nil {
            fmt.Printf("error closing fd") // no error is printed out, /proc/fd shows it's successfuly closed
        }
    }
}

Once the ssh channel gets closed, I close the pty. My expected behavior is that it should send SIGHUP to the shell.

If I comment out the (1) copy (src: mypty, dst: channel), it works!
However - when it's not commented out:

  • The (1) copy doesn't return, meaning the read syscall from mypty is still blocking, and doesn't return eof => master device doesn't get closed?
  • shell doesn't get SIGHUP

I'm not sure why if I comment out the (1) copy it works, maybe the kernel reference counts the reads?

My leads:

Go notes:

  • I close the fd directly, because otherwise using the usual os.File.close() doesn't actually close the fd for some reason, it stays open in /proc/<pid>/fd

  • substituting the (1) copy with a direct read syscall would lead to the same outcome

Thank you!

0 Answers
Related