github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/ssh/ssh.go (about)

     1  package ssh
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"os/exec"
     8  
     9  	"github.com/mutagen-io/mutagen/pkg/platform"
    10  )
    11  
    12  // CompressionFlag returns a flag that can be passed to scp or ssh to enable
    13  // compression. Note that while SSH does have a CompressionLevel configuration
    14  // option, this only applies to SSHv1. SSHv2 defaults to a DEFLATE level of 6,
    15  // which is what we want anyway.
    16  func CompressionFlag() string {
    17  	return "-C"
    18  }
    19  
    20  // ConnectTimeoutFlag returns a flag that can be passed to scp or ssh to limit
    21  // connection time. The provided timeout is in seconds. The timeout must be
    22  // greater than 0, otherwise this function will panic.
    23  func ConnectTimeoutFlag(timeout uint64) string {
    24  	// Validate the timeout.
    25  	if timeout < 1 {
    26  		panic("invalid timeout value")
    27  	}
    28  
    29  	// Format the flag.
    30  	return fmt.Sprintf("-oConnectTimeout=%d", timeout)
    31  }
    32  
    33  // ServerAliveFlags returns a set of flags that can be passed to scp or ssh to
    34  // enable use of server alive messages. The provided interval is in seconds.
    35  // Both the interval and count must be greater than 0, otherwise this function
    36  // will panic.
    37  func ServerAliveFlags(interval, countMax int) []string {
    38  	// Validate the interval and count.
    39  	if interval < 1 {
    40  		panic("invalid interval value")
    41  	} else if countMax < 1 {
    42  		panic("invalid count value")
    43  	}
    44  
    45  	// Format the flags.
    46  	return []string{
    47  		fmt.Sprintf("-oServerAliveInterval=%d", interval),
    48  		fmt.Sprintf("-oServerAliveCountMax=%d", countMax),
    49  	}
    50  }
    51  
    52  // sshCommandPath returns the full path to use for invoking ssh. It will use the
    53  // MUTAGEN_SSH_PATH environment variable if provided, otherwise falling back to
    54  // a platform-specific implementation.
    55  func sshCommandPath() (string, error) {
    56  	// If MUTAGEN_SSH_PATH is specified, then use it to perform the lookup.
    57  	if searchPath := os.Getenv("MUTAGEN_SSH_PATH"); searchPath != "" {
    58  		return platform.FindCommand("ssh", []string{searchPath})
    59  	}
    60  
    61  	// Otherwise fall back to the platform-specific implementation.
    62  	return sshCommandPathForPlatform()
    63  }
    64  
    65  // SSHCommand prepares (but does not start) an SSH command with the specified
    66  // arguments and scoped to lifetime of the provided context.
    67  func SSHCommand(ctx context.Context, args ...string) (*exec.Cmd, error) {
    68  	// Identify the command name or path.
    69  	nameOrPath, err := sshCommandPath()
    70  	if err != nil {
    71  		return nil, fmt.Errorf("unable to identify 'ssh' command: %w", err)
    72  	}
    73  
    74  	// Create the command.
    75  	return exec.CommandContext(ctx, nameOrPath, args...), nil
    76  }
    77  
    78  // scpCommandPath returns the full path to use for invoking scp. It will use the
    79  // MUTAGEN_SSH_PATH environment variable if provided, otherwise falling back to
    80  // a platform-specific implementation.
    81  func scpCommandPath() (string, error) {
    82  	// If MUTAGEN_SSH_PATH is specified, then use it to perform the lookup.
    83  	if searchPath := os.Getenv("MUTAGEN_SSH_PATH"); searchPath != "" {
    84  		return platform.FindCommand("scp", []string{searchPath})
    85  	}
    86  
    87  	// Otherwise fall back to the platform-specific implementation.
    88  	return scpCommandPathForPlatform()
    89  }
    90  
    91  // SCPCommand prepares (but does not start) an SCP command with the specified
    92  // arguments and scoped to lifetime of the provided context.
    93  func SCPCommand(ctx context.Context, args ...string) (*exec.Cmd, error) {
    94  	// Identify the command name or path.
    95  	nameOrPath, err := scpCommandPath()
    96  	if err != nil {
    97  		return nil, fmt.Errorf("unable to identify 'scp' command: %w", err)
    98  	}
    99  
   100  	// Create the command.
   101  	return exec.CommandContext(ctx, nameOrPath, args...), nil
   102  }