newSCPCommand populates an exec.Cmd to run an scp command for the files specified in cmdArgs. cmdArgs is parsed such that scp flags precede the files to copy in the command. For example: scp -F ./config local/file remote:file
(ctx context.Context, port int, dst string, cmdArgs []string)
| 105 | // cmdArgs is parsed such that scp flags precede the files to copy in the command. |
| 106 | // For example: scp -F ./config local/file remote:file |
| 107 | func newSCPCommand(ctx context.Context, port int, dst string, cmdArgs []string) (*exec.Cmd, error) { |
| 108 | connArgs := []string{ |
| 109 | "-P", strconv.Itoa(port), |
| 110 | "-o", "NoHostAuthenticationForLocalhost=yes", |
| 111 | "-o", "PasswordAuthentication=no", |
| 112 | "-C", // compression |
| 113 | } |
| 114 | |
| 115 | cmdArgs, command, err := parseSCPArgs(cmdArgs) |
| 116 | if err != nil { |
| 117 | return nil, err |
| 118 | } |
| 119 | |
| 120 | cmdArgs = append(cmdArgs, connArgs...) |
| 121 | |
| 122 | for _, arg := range command { |
| 123 | // Replace "remote:" prefix with (e.g.) "root@localhost:". |
| 124 | if rest := strings.TrimPrefix(arg, "remote:"); rest != arg { |
| 125 | arg = dst + ":" + rest |
| 126 | } |
| 127 | cmdArgs = append(cmdArgs, arg) |
| 128 | } |
| 129 | |
| 130 | exe, err := safeexec.LookPath("scp") |
| 131 | if err != nil { |
| 132 | return nil, fmt.Errorf("failed to execute scp: %w", err) |
| 133 | } |
| 134 | |
| 135 | // Beware: invalid syntax causes scp to exit 1 with |
| 136 | // no error message, so don't let that happen. |
| 137 | cmd := exec.CommandContext(ctx, exe, cmdArgs...) |
| 138 | |
| 139 | cmd.Stdin = nil |
| 140 | cmd.Stdout = os.Stderr |
| 141 | cmd.Stderr = os.Stderr |
| 142 | |
| 143 | return cmd, nil |
| 144 | } |
| 145 | |
| 146 | func parseSCPArgs(args []string) (cmdArgs, command []string, err error) { |
| 147 | return parseArgs(args, "cFiJloPS") |
no test coverage detected