Marged
Marged

Reputation: 10953

Called program fails if run through os.exec

I am not sure if this is a problem of the called program or if the problem is caused by the way I call the program. Because of this I start at the source code.

I need to call ssh from a program (if you are interested in the reasons I will mention them below) but ssh silently exits.

When I call ssh -v user@remotehost from shell this succeeds:

But when I do the same from within my program (myssh -v user@remotehost only this happens:

Neither the debug output on stderr is shown nor do I reach the remote hosts shell.

This is my sourcecode:

package main

import (
        "fmt"
        "log"
        "os"
        "os/exec"
)

func main() {
        params := os.Args[1:]
        fmt.Printf("passing this to ssh: %s\n", params)
        cmd := exec.Command("ssh", params...)
        err := cmd.Run()
        if err != nil {
                log.Fatal(err)
        }
}

Reason why I wrote this code: I use Ansible which calls ssh. I need to "manipulate" the parameters that Ansible passes to ssh. So far I asked on the OpenSSH and Ansible mailing lists, there is no means in OpenSSH and Ansible to change the parameters (for others it is, but not those I need). The best suggestion I got and that I want to implement is to provide an alternative ssh command to Ansible, use that to receive and modify the parameters and pass them on to the real ssh.

Upvotes: 0

Views: 45

Answers (1)

Joshua Vega
Joshua Vega

Reputation: 599

Are you capturing Stdout and Stderr from cmd? This is where the respective outputs of the command is sent to. The documentation for exec.Command has a really good example on this.

In your case, you'll also want to setup Stdin so that you can pass the password for example.

Here's a really basic example based on your code:

package main

import (
  "bytes"
  "fmt"
  "log"
  "os"
  "os/exec"
)

func main() {
  params := os.Args[1:]
  fmt.Println("Passing this to ssh: %s", params)

  var stdin  bytes.Buffer
  var stdout bytes.Buffer
  var stderr bytes.Bufer

  cmd := exec.Command("ssh", params...)
  cmd.Stdin  = &stdin
  cmd.Stdout = &stdout
  cmd.Stderr = &stderr

  if err := cmd.Run(); err != nil {
    log.Fatal(err)
  }

  fmt.Println("Stdout: %s", stdout.String())
  fmt.Println("stderr: %s", stderr.String())
}

Since stdin, stdout, and stderr are all bytes.Buffers, you can read and write from them just like any other buffer.

You might also want to consider using the golang.org/x/crypto/ssh package which provides a native SSH interface for Go instead of using sub-processes.

Upvotes: 1

Related Questions