Skip to content

x/crypto/ssh: Signal method doesn't work #16597

@purpleidea

Description

@purpleidea

Starting a process over SSH using the /x/crypto/ssh library and then trying to send a signal to it, seems to have no effect. I've found a few musings on the internet about users with the same problem.

I've attached a full reproducer, including the crappy workaround.

package main

import (
    "bytes"
    "fmt"
    "golang.org/x/crypto/ssh"
    "time"
)

func main() {

    // An SSH client is represented with a ClientConn.
    //
    // To authenticate with the remote server you must pass at least one
    // implementation of AuthMethod via the Auth field in ClientConfig.
    config := &ssh.ClientConfig{
        User: "someuser", // XXX
        Auth: []ssh.AuthMethod{
            ssh.Password("somepass"), // XXX
        },
    }
    client, err := ssh.Dial("tcp", "localhost:22", config)
    if err != nil {
        panic("Failed to dial: " + err.Error())
    }

    // Each ClientConn can support multiple interactive sessions,
    // represented by a Session.
    session, err := client.NewSession()
    if err != nil {
        panic("Failed to create session: " + err.Error())
    }
    defer session.Close()

    // Once a Session is created, you can execute a single command on
    // the remote side using the Run method.
    var b bytes.Buffer
    session.Stdout = &b

    go func() {
        time.Sleep(2 * time.Second)
        // XXX none of these signals work! :(
        //      fmt.Println("Running signal!")
        //      session.Signal(ssh.SIGINT)
        //      session.Signal(ssh.SIGKILL)
        //      session.Signal(ssh.SIGQUIT)
        //      session.Signal(ssh.SIGHUP)
        //      session.Signal(ssh.SIGPIPE)
        //      fmt.Println("Done running signal!")

        s2, err := client.NewSession()
        if err != nil {
            panic("Failed to create session: " + err.Error())
        }
        defer s2.Close()
        var c bytes.Buffer
        s2.Stdout = &c
        if err := s2.Run("echo start && pidof sleep && killall sleep"); err != nil {
            fmt.Println("s2 error!")
        }
        fmt.Println("Sig run done!")
        fmt.Println(c.String())

    }()

    if err := session.Run("echo $0 && /usr/bin/sleep 10s"); err != nil {

        if e, ok := err.(*ssh.ExitError); ok {
            fmt.Printf("Remote: Exit msg: %s", e.Waitmsg.Msg()) // XXX (does this ever return anything useful?)
            fmt.Printf("Remote: Exit signal: %s", e.Waitmsg.Signal())
            fmt.Printf("Remote: Error: Output...\n%s", b.String())
            fmt.Printf("Exited (%d) with: %s", e.Waitmsg.ExitStatus(), e.Error())

        } else if e, ok := err.(*ssh.ExitMissingError); ok {
            fmt.Printf("Exit code missing: %s", e.Error())
        }

        panic("Failed to run: " + err.Error())
    }
    fmt.Println("Done!")
    fmt.Println(b.String())
}

Uncomment the signal you want, and you'll see that they all do nothing to kill the running sleep command.

Tested with go version: 1.5.4 (but I have no reason to expect this is fixed with newer versions!) on Fedora GNU/Linux 24 as the SSH server, running OpenSSH.

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions