fix: commands on SSH hosts run under sh -c, so hosts whose login shell is csh or tcsh list, zip and send files too
This commit is contained in:
1 parent
458d826de6
commit
6d8f39f2a5
3 files changed
+29
-6
No files matched your search
+10
-2
@@ -562,6 +562,14 @@ func (u *unpacker) own(p string) {
|
|||||||
// shq quotes s for a POSIX shell.
|
// shq quotes s for a POSIX shell.
|
||||||
func shq(s string) string { return "'" + strings.ReplaceAll(s, "'", `'\''`) + "'" }
|
func shq(s string) string { return "'" + strings.ReplaceAll(s, "'", `'\''`) + "'" }
|
||||||
|
|
||||||
|
// posix wraps a command for the host's sh: the user's login shell gets one
|
||||||
|
// word to hand on, so fish or csh there does not matter.
|
||||||
|
func posix(cmd string) string {
|
||||||
|
// csh reads "!" as history even inside single quotes; '\!' is a plain
|
||||||
|
// "!" for it and for sh alike.
|
||||||
|
return "sh -c " + strings.ReplaceAll(shq(cmd), "!", `'\!'`)
|
||||||
|
}
|
||||||
|
|
||||||
// shipSSH writes the tar stream into tar on the SSH host, in Dir.
|
// shipSSH writes the tar stream into tar on the SSH host, in Dir.
|
||||||
func (a *Agent) shipSSH(ctx context.Context, args proto.FSShipArgs, src string, m *shipMeter) error {
|
func (a *Agent) shipSSH(ctx context.Context, args proto.FSShipArgs, src string, m *shipMeter) error {
|
||||||
client, _, err := a.sshConnect(ctx, *args.SSH, func(_, note string) { m.note.Store(note) })
|
client, _, err := a.sshConnect(ctx, *args.SSH, func(_, note string) { m.note.Store(note) })
|
||||||
@@ -615,7 +623,7 @@ func (a *Agent) shipSSH(ctx context.Context, args proto.FSShipArgs, src string,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cmd := "mkdir -p -- " + shq(dir) + " && cd -- " + shq(dir) + " && tar -xf -" + flags
|
cmd := "mkdir -p -- " + shq(dir) + " && cd -- " + shq(dir) + " && tar -xf -" + flags
|
||||||
if err := sess.Start(cmd); err != nil {
|
if err := sess.Start(posix(cmd)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m.note.Store("")
|
m.note.Store("")
|
||||||
@@ -646,7 +654,7 @@ func sshRun(c *ssh.Client, cmd string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
out, err := s.Output(cmd)
|
out, err := s.Output(posix(cmd))
|
||||||
var ee *ssh.ExitError
|
var ee *ssh.ExitError
|
||||||
if errors.As(err, &ee) {
|
if errors.As(err, &ee) {
|
||||||
err = nil // the checks may exit non-zero; what they print is the answer
|
err = nil // the checks may exit non-zero; what they print is the answer
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ func (a *Agent) sshfsRun(c *sshfsConn, cmd string, stdin io.Reader) ([]byte, err
|
|||||||
if stdin != nil {
|
if stdin != nil {
|
||||||
s.Stdin = stdin
|
s.Stdin = stdin
|
||||||
}
|
}
|
||||||
if err := s.Run(cmd); err != nil {
|
if err := s.Run(posix(cmd)); err != nil {
|
||||||
msg := strings.TrimSpace(errb.String())
|
msg := strings.TrimSpace(errb.String())
|
||||||
if msg == "" {
|
if msg == "" {
|
||||||
msg = err.Error()
|
msg = err.Error()
|
||||||
@@ -535,7 +535,7 @@ func (a *Agent) doSSHFSSend(ctx context.Context, env proto.TaskEnvelope) proto.T
|
|||||||
}
|
}
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
out, _ := s.StdoutPipe()
|
out, _ := s.StdoutPipe()
|
||||||
if err := s.Start("cat -- " + shq(args.Path)); err != nil {
|
if err := s.Start(posix("cat -- " + shq(args.Path))); err != nil {
|
||||||
return proto.TaskResult{Error: err.Error()}
|
return proto.TaskResult{Error: err.Error()}
|
||||||
}
|
}
|
||||||
// Read in order from cat, posted to the server a few at a time.
|
// Read in order from cat, posted to the server a few at a time.
|
||||||
@@ -612,7 +612,7 @@ func (a *Agent) doSSHFSPut(ctx context.Context, env proto.TaskEnvelope) proto.Ta
|
|||||||
in, _ := s.StdinPipe()
|
in, _ := s.StdinPipe()
|
||||||
var errb bytes.Buffer
|
var errb bytes.Buffer
|
||||||
s.Stderr = &limitBuf{b: &errb, max: 4096}
|
s.Stderr = &limitBuf{b: &errb, max: 4096}
|
||||||
if err := s.Start(writeCommand(args.Path)); err != nil {
|
if err := s.Start(posix(writeCommand(args.Path))); err != nil {
|
||||||
return proto.TaskResult{Error: err.Error()}
|
return proto.TaskResult{Error: err.Error()}
|
||||||
}
|
}
|
||||||
// Pieces come in a few ahead; stdin takes them in order. ack: how many
|
// Pieces come in a few ahead; stdin takes them in order. ack: how many
|
||||||
@@ -711,7 +711,7 @@ func (a *Agent) packRemote(ctx context.Context, w io.Writer, c *sshfsConn, srcs,
|
|||||||
rename[b] = names[i]
|
rename[b] = names[i]
|
||||||
cmd += " " + shq(b)
|
cmd += " " + shq(b)
|
||||||
}
|
}
|
||||||
if err := s.Start(cmd); err != nil {
|
if err := s.Start(posix(cmd)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := renameTar(w, &countReader{r: out, n: &m.done}, rename); err != nil {
|
if err := renameTar(w, &countReader{r: out, n: &m.done}, rename); err != nil {
|
||||||
|
|||||||
@@ -225,3 +225,18 @@ func TestListFolderLink(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Commands go to the host as one argument of sh -c, so the user's own login
|
||||||
|
// shell (fish, csh) cannot read them its way.
|
||||||
|
func TestPosixCommand(t *testing.T) {
|
||||||
|
if got := posix(`if [ -d '/x' ]; then echo ok; fi`); got != `sh -c 'if [ -d '\''/x'\'' ]; then echo ok; fi'` {
|
||||||
|
t.Fatalf("%s", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// csh expands "!" even inside single quotes: it goes out as '\!'.
|
||||||
|
func TestPosixCommandBang(t *testing.T) {
|
||||||
|
if got := posix(`ls .[!.]*`); got != `sh -c 'ls .['\!'.]*'` {
|
||||||
|
t.Fatalf("%s", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user