vsh doesn't handle stdin from non-tty correctly |
||
Issue descriptionI'm trying to copy a file from host to container via a stdin pipe to vsh like: $ base64 /usr/local/bin/sysbench | vsh --vm_name=termina --target_container=penguin --owner_id=2c5859c2a92932288ce902f6c2a0746270229178 -- sh -c 'base64 --decode >/home/testuser/sysbench' Though it works correctly here, it doesn't work in headless mode like in a tast test like: args := []string{"--vm_name=" + c.VM.name, "--target_container=" + c.containerName, "--owner_id=" + c.VM.Concierge.ownerID, "--", "sh", "-c", "base64 --decode >"+containerPath} cmd := testexec.CommandContext(ctx, "vsh", args...) cmd.Stdin = bytes.NewBufferString(base64Data) testing.ContextLog(ctx, "Before running cmd.Run()") err = cmd.Run() if err != nil { cmd.DumpLog(ctx) return err } testing.ContextLog(ctx, "After running cmd.Run()") It would hang on "Before running cmd.Run()" and never returns. There're at least 3 problems: 1. I found that a trailing "\n" is needed for the psuedoterminal to terminate before sending an EOF. If using a pipe via command line, a "\n" is always appended so it's ok. In headless mode I'll need to append a "\n" manually like: cmd.Stdin = bytes.NewBufferString(base64Data+"\n") to void the hang. 2. vsh_client doesn't take care of input length > 4K. In tty mode, HandleStdinReadable() is called on byte, so it's ok to send one byte at a time. If using a pipe in headless mode, HandleStdinReadable() is called only one time, so only the first 4K is handled. 3. I managed to modify vsh_client so it loops over the whole stdin to send every bytes (A non-blocking read is needed so reach the end of stdin). Though I send the whole stdin to vshd, the receiving end still only get the first 4K bytes. I didn't get into details for this part. It looks some corresponding change in vclient_forwarder is needed to reflect the change. A patch to demo bug and facilitate testing. https://chromium-review.googlesource.com/c/chromiumos/platform/tast-tests/+/1423958 Before this is resolved, copying a large file to container remains a problem because of ARG_MAX limitation of a shell (The original code uses a vsh -- sh -c 'echo <payload> | base64 --decode >containerPath' solution so arg length matters). The best I can get for now is like const maxArgLen = 4000 dataLen := len(base64Data) start := 0 for start < dataLen { end := start + maxArgLen if end > dataLen { end = dataLen } cmd := c.Command(ctx, "sh", "-c", "echo '"+base64Data[start:end]+"' | base64 --decode >>"+containerPath) if err = cmd.Run(); err != nil { cmd.DumpLog(ctx) return err } start = end }
,
Jan 20
(2 days ago)
Side note: I'm not sure what the finished code looks like, but please generate the base64-encoded data in a subprocess or goroutine that writes the data directly to cmd.Stdin instead of reading all the data into memory first. This approach will likely be faster, too.
,
Jan 20
(2 days ago)
,
Jan 21
(2 days ago)
Is using stdin a requirement? Could you scp it to the host instead? we use sshfs already to share files with the files app.
,
Jan 21
(2 days ago)
#4 That's would be great. I started a ssh-agent and added /home/root/<crypto_home_id>/crosvm/sshkeys/host_key manually, then I can sftp into the container. The config in container seems to allow sftp only if I tried to use scp "This service allows sftp connections only." Any suggested ways? #2 no problem if it's required by the end.
,
Jan 21
(2 days ago)
I believe we use sshfs to share files with the container, so mounting that should work. You can do this from the secure shell app even on a verified mode system. Maybe smbarber knows some history about scp being disabled? |
||
►
Sign in to add a comment |
||
Comment 1 by cylee@chromium.org
, Jan 20 (2 days ago)