diff --git a/cmd/limactl/copy.go b/cmd/limactl/copy.go index 4b5fc3e3cf6..9193093771e 100644 --- a/cmd/limactl/copy.go +++ b/cmd/limactl/copy.go @@ -34,6 +34,7 @@ func newCopyCommand() *cobra.Command { copyCommand.Flags().BoolP("recursive", "r", false, "copy directories recursively") copyCommand.Flags().BoolP("verbose", "v", false, "enable verbose output") + copyCommand.Flags().BoolP("rsync", "", false, "use rsync for copying instead of scp") return copyCommand } @@ -49,10 +50,24 @@ func copyAction(cmd *cobra.Command, args []string) error { return err } - arg0, err := exec.LookPath("scp") + useRsync, err := cmd.Flags().GetBool("rsync") if err != nil { return err } + + var arg0 string + if useRsync { + arg0, err = exec.LookPath("rsync") + if err != nil { + return err + } + } else { + arg0, err = exec.LookPath("scp") + if err != nil { + return err + } + } + instances := make(map[string]*store.Instance) scpFlags := []string{} scpArgs := []string{} @@ -67,6 +82,9 @@ func copyAction(cmd *cobra.Command, args []string) error { if verbose { scpFlags = append(scpFlags, "-v") + if useRsync { + scpFlags = append(scpFlags, "--progress") + } } else { scpFlags = append(scpFlags, "-q") } @@ -93,11 +111,15 @@ func copyAction(cmd *cobra.Command, args []string) error { if inst.Status == store.StatusStopped { return fmt.Errorf("instance %q is stopped, run `limactl start %s` to start the instance", instName, instName) } - if legacySSH { - scpFlags = append(scpFlags, "-P", fmt.Sprintf("%d", inst.SSHLocalPort)) + if useRsync { scpArgs = append(scpArgs, fmt.Sprintf("%s@127.0.0.1:%s", *inst.Config.User.Name, path[1])) } else { - scpArgs = append(scpArgs, fmt.Sprintf("scp://%s@127.0.0.1:%d/%s", *inst.Config.User.Name, inst.SSHLocalPort, path[1])) + if legacySSH { + scpFlags = append(scpFlags, "-P", fmt.Sprintf("%d", inst.SSHLocalPort)) + scpArgs = append(scpArgs, fmt.Sprintf("%s@127.0.0.1:%s", *inst.Config.User.Name, path[1])) + } else { + scpArgs = append(scpArgs, fmt.Sprintf("scp://%s@127.0.0.1:%d/%s", *inst.Config.User.Name, inst.SSHLocalPort, path[1])) + } } instances[instName] = inst default: @@ -107,7 +129,9 @@ func copyAction(cmd *cobra.Command, args []string) error { if legacySSH && len(instances) > 1 { return errors.New("more than one (instance) host is involved in this command, this is only supported for openSSH v8.0 or higher") } - scpFlags = append(scpFlags, "-3", "--") + if !useRsync { + scpFlags = append(scpFlags, "-3", "--") + } scpArgs = append(scpFlags, scpArgs...) var sshOpts []string @@ -128,13 +152,23 @@ func copyAction(cmd *cobra.Command, args []string) error { return err } } + + var cmdArgs []string sshArgs := sshutil.SSHArgsFromOpts(sshOpts) + if useRsync { + // for rsync, add the -e flag with SSH options + scpFlags = append(scpFlags, "-e", fmt.Sprintf("ssh %s", strings.Join(sshArgs, " "))) + cmdArgs = append(cmdArgs, append(scpFlags, scpArgs...)...) + } else { + // for scp, append SSH options and scpArgs + cmdArgs = append(cmdArgs, append(sshArgs, scpArgs...)...) + } - sshCmd := exec.Command(arg0, append(sshArgs, scpArgs...)...) + sshCmd := exec.Command(arg0, cmdArgs...) sshCmd.Stdin = cmd.InOrStdin() sshCmd.Stdout = cmd.OutOrStdout() sshCmd.Stderr = cmd.ErrOrStderr() - logrus.Debugf("executing scp (may take a long time): %+v", sshCmd.Args) + logrus.Debugf("executing %s (may take a long time): %+v", arg0, sshCmd.Args) // TODO: use syscall.Exec directly (results in losing tty?) return sshCmd.Run()