New issue
Advanced search Search tips

Issue 923721 link

Starred by 3 users

Issue metadata

Status: Untriaged
Owner:
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: Chrome
Pri: 2
Type: Bug

Blocking:
issue 922178



Sign in to add a comment

vsh doesn't handle stdin from non-tty correctly

Project Member Reported by cylee@chromium.org, Jan 20 (2 days ago)

Issue description

I'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
                }
 

Comment 1 by cylee@chromium.org, Jan 20 (2 days ago)

Blocking: 922178

Comment 2 by derat@chromium.org, 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.

Comment 3 by derat@chromium.org, Jan 20 (2 days ago)

Labels: OS-Chrome

Comment 4 by dgreid@google.com, 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.

Comment 5 Deleted

Comment 6 by cylee@chromium.org, 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.

Comment 7 by dgreid@google.com, 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