sshclient

package
v0.5.38 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 14, 2026 License: BSD-3-Clause Imports: 26 Imported by: 1

README

SSH client

This package manages ssh connections to other hosts ("client" mode), using the Go standard ssh library. It provides a similar interface as the exec package, e.g., with Run, Output, Start functions to execute commands, and its Config is based on exec.Config.

The Client type is used to establish a connection to a host, which persists until closed.

Each command execution process creates its own ssh.Session object that manages the process of running the command, within the scope of a persistently open Client connection. This is much faster than opening a new connection for each command. A map of open sessions is maintained in case of a Start command that runs the command asynchronously.

Environment and interactive commands

Each command runs in a new shell on the host that is not interactive, for which a standard ~/.bashrc returns before it sets the PATH and other variables of the user. So when it connects, the client gets the environment of an interactive login shell of the user once, and each command starts by setting the variables that differ (Config.LoginEnv, on by default).

A command run with its input from a terminal gets that input while it runs, in a terminal on the host with the same size, so that interactive programs and Control+C work, and Client.Shell runs an interactive shell on the host. With RunContext etc, a command is interrupted when its context is done.

Lost connections

A connection can be lost without being closed, as when a laptop sleeps or changes networks, and then everything waiting on it would wait forever. So the client sends a keepalive request to the host every Config.KeepAlive (15 seconds by default), and closes the connection if the host does not reply within Config.Timeout (also 15 seconds, which also limits connecting), so that the commands waiting on it fail. The next command, or Client.CheckConnection, connects again to the same host, keeping the current directory and environment, as when the network comes back. While the host cannot be reached, it tries again only after the Timeout, so that commands fail fast.

A good reference for similar package: https://github.com/helloyi/go-sshclient

Copying files

Files are copied to and from hosts with the SFTP protocol of https://github.com/pkg/sftp, on the connection of the client: Copy copies files between any hosts, including the local one, as cp -r does, with wildcards, Client.OpenFile opens a file on the host, and Client.ReadDir and Client.Stat list its directories. These are in all builds, returning an error on the web (js), where SSH can not connect, while Client.SFTP, the SFTP client itself, is not in js builds.

The sshtest package provides an SSH server for tests, which runs commands and serves SFTP in the test process, and can simulate the loss of the network (Server.NetworkDown and NetworkUp).

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Copy added in v0.5.7

func Copy(ctx context.Context, dst Location, srcs ...Location) error

Copy copies the files of the given source locations to the given destination location, as cp -r does, using the SFTP protocol on the connections of the remote hosts. A source can have the wildcards of path.Match, such as *.tsv, and a directory is copied with all of its contents. The sources are copied into the destination if it is a directory: if it exists, ends with a /, is . or .., or if there are several sources (a directory that does not exist is made); otherwise, the destination is the new name of the single source file or directory. The modes of the files are kept. The copy stops when the context is done; use context.Background() for no limit.

func CopyProgress added in v0.5.7

func CopyProgress(ctx context.Context, progress func(p Progress), dst Location, srcs ...Location) error

CopyProgress copies files, as Copy, calling the given function, if non-nil, with the progress of the copy: when a file starts, as its bytes are copied, and at the end, when Finished is true. It is called often, so a function that shows the progress should show it at intervals. It is not called concurrently, but it can be called on another goroutine than that of CopyProgress.

Types

type Client

type Client struct {
	Config

	// ssh client is present (non-nil) after a successful Connect,
	// and nil while the connection is lost.
	Client *ssh.Client

	// keeps track of sessions that are being waited upon
	Sessions map[string]*ssh.Session
	// contains filtered or unexported fields
}

Client represents a persistent connection to an ssh host. Commands are run by creating [ssh.Session]s from this client.

The connection is checked with a keepalive request every Config.KeepAlive, and closed if the host does not reply within Config.Timeout, as happens when the network is lost, so that commands waiting on it fail instead of hanging. A lost connection is made again the next time it is used (see Client.CheckConnection), as when the network comes back.

func NewClient

func NewClient(cfg *Config) *Client

NewClient returns a new Client using given Config configuration parameters.

func (*Client) CheckConnection added in v0.5.15

func (cl *Client) CheckConnection() error

CheckConnection returns nil if the client is connected, and otherwise tries to connect again if the connection was lost, as when the network was lost, returning an error if that fails. It only tries again after the Config.Timeout since the last try, returning an error until then, so that commands fail fast while the host cannot be reached. It returns an error if the client was never connected, or was closed.

func (*Client) Close

func (cl *Client) Close()

Close terminates any open Sessions and then closes the Client connection, which is not made again.

func (*Client) CloseSessions

func (cl *Client) CloseSessions()

CloseSessions terminates any open Sessions that are still Waiting for the associated process to finish.

func (*Client) Connect

func (cl *Client) Connect(host string) error

Connect connects to given host, which can either be just the host or user@host. If host is empty, the Config default host will be used if non-empty, or an error is returned. If successful, creates a Client that can be used for future sessions. Otherwise, returns error. This updates the Host (and User) fields in the config, for future reference.

func (*Client) CopyHostToLocal

func (cl *Client) CopyHostToLocal(ctx context.Context, hostFilename string, w io.Writer) error

CopyHostToLocal copies the given file on the remote host to the given writer. A relative host path is relative to the current Dir of the Client.

func (*Client) CopyHostToLocalFile

func (cl *Client) CopyHostToLocalFile(ctx context.Context, hostFilename, localFilename string) error

CopyHostToLocalFile copies the given file or directory on the remote host to the given local path (see Copy). A relative host path is relative to the current Dir of the Client.

func (*Client) CopyLocalFileToHost

func (cl *Client) CopyLocalFileToHost(ctx context.Context, localFilename, hostFilename string) error

CopyLocalFileToHost copies the given local file or directory to the given path on the remote host (see Copy). A relative host path is relative to the current Dir of the Client.

func (*Client) CopyLocalToHost

func (cl *Client) CopyLocalToHost(ctx context.Context, r io.Reader, size int64, hostFilename string) error

CopyLocalToHost copies the data of the given reader to the given file on the remote host. The size is not needed. A relative host path is relative to the current Dir of the Client.

func (*Client) Exec

func (cl *Client) Exec(sio *exec.StdIOState, start, output bool, cmd string, args ...string) (string, error)

Exec executes the command, piping its stdout and stderr to the config writers. If the command fails, it will return an error with the command output. The given cmd and args may include references to environment variables in $FOO format, in which case these will be expanded before the command is run.

Ran reports if the command ran (rather than was not found or not executable). Code reports the exit code the command returned if it ran. If err == nil, ran is always true and code is always 0.

func (*Client) ExecContext added in v0.5.15

func (cl *Client) ExecContext(ctx context.Context, sio *exec.StdIOState, start, output bool, cmd string, args ...string) (string, error)

ExecContext is Client.Exec with a context: if it is not nil, a command that is run (not started) is interrupted when the context is done, as a shell does for Control+C: it gets an interrupt signal, and its session is closed if it has not ended after exec.InterruptWait, which ends it with a hangup. A command that is run with its standard input from a terminal gets the input of that terminal while it runs, such that interactive programs such as more and editors work.

func (*Client) GetHomeDir

func (cl *Client) GetHomeDir() error

GetHomeDir runs "pwd" on the host to get the users home dir, called right after connecting.

func (*Client) NewSession

func (cl *Client) NewSession(interact bool) (*ssh.Session, error)

NewSession creates a new session, sets its input / outputs based on config. Only works if Client already connected, and connects again if the connection was lost (see Client.CheckConnection).

func (*Client) OpenFile added in v0.5.7

func (cl *Client) OpenFile(name string, flag int) (io.ReadWriteCloser, error)

OpenFile opens the given file on the remote host, with the given flags of os.OpenFile, such as os.O_CREATE|os.O_WRONLY|os.O_TRUNC to write it, or os.O_APPEND to add to it, through SFTP. A relative path is relative to the current Dir of the Client.

func (*Client) Output

func (cl *Client) Output(sio *exec.StdIOState, cmd string, args ...string) (string, error)

Output runs the command and returns the text from stdout.

func (*Client) OutputContext added in v0.5.15

func (cl *Client) OutputContext(ctx context.Context, sio *exec.StdIOState, cmd string, args ...string) (string, error)

OutputContext is Client.Output with a context, with which the command is interrupted (see Client.ExecContext).

func (*Client) ReadDir added in v0.5.16

func (cl *Client) ReadDir(dir string) ([]fs.FileInfo, error)

ReadDir returns the entries in the given directory on the remote host, sorted by name, through SFTP. Symbolic links are returned as such: use Client.Stat for what they point to. A relative path is relative to the current Dir of the Client.

func (*Client) Run

func (cl *Client) Run(sio *exec.StdIOState, cmd string, args ...string) error

Run runs given command, using config input / outputs. Must have already made a successful Connect.

func (*Client) RunContext added in v0.5.15

func (cl *Client) RunContext(ctx context.Context, sio *exec.StdIOState, cmd string, args ...string) error

RunContext is Client.Run with a context, with which the command is interrupted (see Client.ExecContext).

func (*Client) SFTP added in v0.5.7

func (cl *Client) SFTP() (*sftp.Client, error)

SFTP returns the SFTP client of the connection to the host, which is made when first needed, for copying files to and from the host. It is not in js builds, which leave out SFTP: use the methods of Client such as Client.ReadDir and Client.OpenFile for code that is also built for the web.

func (*Client) Shell added in v0.5.15

func (cl *Client) Shell(sio *exec.StdIOState) error

Shell runs an interactive login shell on the host, in the current directory of the client, with the terminal of the given standard input, whose input it gets while it runs (see Client.ExecContext), until it ends, as with exit or Control+D. Its exit status is not an error, as it is that of the last command in it. It needs a terminal.

func (*Client) Start

func (cl *Client) Start(sio *exec.StdIOState, cmd string, args ...string) error

Start starts the given command with arguments.

func (*Client) Stat added in v0.5.16

func (cl *Client) Stat(name string) (fs.FileInfo, error)

Stat returns the information about the given file on the remote host, following symbolic links, through SFTP. A relative path is relative to the current Dir of the Client.

func (*Client) WaitSession

func (cl *Client) WaitSession(name string, ses *ssh.Session) error

WaitSession adds the session to list of open sessions, and calls Wait on it. It should be called in a goroutine, and will only return when the command is completed or terminated. The given name is used to save the session in a map, for later reference. If left blank, the name will be a number that increases with each such session created.

type Config

type Config struct {
	exec.Config

	// user name and ssh key info
	User User

	// host name / ip address to connect to. can be blank, in which
	// case it must be specified in the Client.Connect call.
	Host string

	// home directory of user on remote host,
	// which is captured at initial connection time.
	HomeDir string

	// KeepAlive is the interval between keepalive requests to the host,
	// which detect a lost connection, as when the network is lost
	// (see [Client]). 0 turns them off.
	KeepAlive time.Duration

	// Timeout is the time to wait for the connection to the host, and for
	// the replies to keepalive requests. 0 is no limit.
	Timeout time.Duration

	// LoginEnv is whether the commands get the environment variables of an
	// interactive login shell of the user on the host, such as PATH, which
	// it gets once when it connects. The commands are otherwise run by a
	// shell that is not interactive, for which a standard ~/.bashrc returns
	// before it sets them.
	LoginEnv bool
}

Config contains the configuration information that controls the behavior of ssh connections and commands. It is used to establish a Client connection to a remote host. It builds on the shared settings in exec.Config

func NewConfig

func NewConfig(cfg *exec.Config) *Config

NewConfig returns a new ssh Config based on given exec.Config parameters.

type Location added in v0.5.7

type Location struct {
	Client *Client
	Path   string
}

Location is a file path on a host, for Copy: on the host of the given connected Client, or on the local host if the Client is nil. A relative path on a host is relative to the current Dir of its Client.

func (Location) String added in v0.5.7

func (l Location) String() string

type Progress added in v0.5.7

type Progress struct {

	// File is the path of the source file being copied.
	File string

	// Done and Size are the bytes of the file copied so far, and its size.
	Done, Size int64

	// TotalDone and Total are the bytes of all of the files copied so far,
	// and their total size.
	TotalDone, Total int64

	// FileNum is the 1-based number of the file being copied, of NFiles.
	FileNum, NFiles int

	// Finished is whether all of the files are copied.
	Finished bool
}

Progress is the progress of a CopyProgress.

type User

type User struct {
	// user name to connect with
	User string

	// path to ssh keys: ~/.ssh by default
	KeyPath string

	// name of the private ssh key file in KeyPath. If it does not exist,
	// the other standard ones are used: id_ed25519, id_ecdsa or id_rsa.
	KeyFile string `default:"id_rsa"`
}

User holds user-specific ssh connection configuration settings, including Key info.

func (*User) Defaults

func (us *User) Defaults()

Directories

Path Synopsis
Package sshtest provides an SSH server for testing code that uses cogentcore.org/core/base/sshclient, running in the test process on the local host: it runs commands with sh in its root directory, which is the home directory of the remote host, and serves SFTP.
Package sshtest provides an SSH server for testing code that uses cogentcore.org/core/base/sshclient, running in the test process on the local host: it runs commands with sh in its root directory, which is the home directory of the remote host, and serves SFTP.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
⌂ 换网址