2

TL;DR How can I fix this command line template, which fails when cat (on the client side) contends with ssh prompting for a password?

{ echo some stuff; cat; } | ssh SERVER cat
{ echo some stuff; cat; } | ssh SERVER ls

I'm not asking about creating public/private keys. I don't want to consume stdin in advance, since the remote command might be something which ignores its stdin (in which case, forcing the user to type ^D is a pain). I don't want the user to have to specify an option to the template to indicate whether or not the remote command will read its stdin. I want the template to work regardless of whether or not the remote command reads its stdin.


Details: I want to write client- and server-side wrappers for a command being run remotely via ssh. The command in question can be any command of the user's choosing. The purpose of the wrappers is to set up the environment of the command. The client-side wrapper needs to send a small amount of data to the server-side wrapper, but I don't want to send that data on the command line (I want to send it on stdin).

Here's an example of how this might be called:

$ wrapper1 HOST wrapper2 sh -c "cat >file"
This data is sent to the remote side
^D

Wrapper1 computes some data, passes it to wrapper2, which sets up the environment before execing sh. Cat reads from stdin and writes into file on the remote side.

To accomplish this, I want the client-side wrapper to inject some data at the beginning of the stdin stream that is sent to the server-side wrapper. That extra data will be consumed from stdin by the server-side wrapper before the real command is run.

After the injected data has been sent and consumed, I want the stdin pipeline to the remote command to be as it would have been without this data injection. The remote command may or may not bother to read anything from its stdin -- that depends on the particular command being run.

Since the command being run is not known in advance, and it's not known in advance if it needs to read anything from stdin, I don't want the client-side to consume its entire stdin before calling the remote command.

Here are even more details of what I'm trying to do:

I want to write wrapper1 around "ssh HOST wrapper2 CMD", where CMD is not known in advance (assume it does not require a tty, but it may or may not read stdin). Wrapper1 will send some extra data to the remote side to be read by wrapper2 before wrapper2 execs CMD. I want wrapper1 to be more or less transparent; it should work just like ssh works, except for sending the extra data and running wrapper2 as the remote command to read the extra data before execing CMD. If the remote CMD reads stdin, I want it to get the contents of wrapper1's stdin, just as if "ssh HOST CMD" were run.

I have coded this by having wrapper1 create a child process which writes the extra data to stdout followed by execing cat to copy stdin to stdout. Wrapper1 pipes the output of the child process into the stdin of the ssh command. However, this approach fails when ssh prompts for a password, since both cat and ssh are contending for input from the keyboard (when CMD is running interactively). If I insert a sleep before the child's cat, it will work, but obviously that is not optimal.

Because I don't know if CMD reads stdin at all, I cannot consume all of stdin before execing ssh in order to avoid the conflict. In any case, it is suboptimal to read all of stdin under many scenarios (such as when it is very large, or it needs to be consumed incrementally).

Is there any way of doing what I want? Right now all that I can think of is an option to wrapper1 which gives the choice of either:

  1. Consume your entire stdin and send it to the remote side, or
  2. Redirect stdin from /dev/null.

But, it would be better to avoid making the user choose one of the two options and allow the data from stdin to be read incrementally as it normally would be without the wrappers.

0

So you might try setting up CMD so that it will always redirect a file as STDIN like so

cmd < file.txt

or perhaps read the file first, sanitize then send it in as a string

cmd <<< "string"

Then when where you call the CMD script you could insert the arguments as it were in to the file before you call ./wrapper2. When there is no need for the STDIN then make sure you won't send in something bad by automatically deleting the temp file or truncating it after you execute.

  • I want the remote CMD to be able to read from stdin normally. – jrw32982 Mar 26 '14 at 20:47
  • Ah, well i believe that if the CMD is blocking on input, you might try cmd < /dev/stdin and try sending it some input via keyboard. I tried to do this with non-blocking commands like echo to no-avail – datUser Mar 26 '14 at 21:02
0

Not sure if I understood the question correctly:

wrapper1 () {
    host="$1"
    shift
    {
         echo "nonsensical statement involving wrapper2"
         cat
    } | ssh $host wrapper2 "$@"
}

Maybe watch out for buffering.

  • That's more or less what I'm doing now. But when ssh prompts for a password, it contends with cat. Have you gotten it to work? This fails for me from both cygwin and linux: { echo some stuff; cat; } | ssh SERVER cat – jrw32982 Mar 26 '14 at 21:16
  • @jrw32982: Works for me on Linux. ssh uses the current terminal to ask the password. – BatchyX Mar 27 '14 at 11:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.