Terminal Asks for Email and Password, how do I Programmatically fill it out (in Ruby)? - ruby

I am running a command to push files to Google App Engine, and it might ask me for my email and password:
$ appcfg.py update .
Application: my-cdn; version: 3. # printed out
Server: appengine.google.com. # printed out
Scanning files on local disk. # printed out
Initiating update. # printed out
Email: email#gmail.com # now it asks me...
Password:
I am running that in Ruby right now using this: %x[appcfg.py update .]. How can I fill out the email and password? I have seen something like this with capistrano:
%x[appcfg.py update .] do |channel, stream, data|
channel.send_data "#{yaml['production']['email']}\n" if data =~ /^Email:/
end
...but haven't figured out how to set that up without it.
What's the best way to fill out things the command line asks for programmatically?
Another issue is that if I run the command through ruby, I can see output as the command runs, but it never shows me the "Email: " line, it stops here:
Application: my-cdn; version: 3.
Server: appengine.google.com.
Scanning files on local disk.
Initiating update.
# ... can't see "Email: "
Thanks for the tips.

Generally, you can use Open3.popen3(command) do |input, output, error| ... end to invoke a command, write to its input stream and read from its output and error streams (you need to require "open3" first).
However that usually does not work with programs that ask for a password (because they access the terminal directly instead of simply reading from stdin). In that case, you need to use PTY (require "pty") instead of open3. PTY.getpty will spawn a shell and return an array containing the shell's output stream, input stream and pid. You can use those to invoke commands and read their input (be aware that the output you read will also include the shell prompt and the command invocation).

Related

Using expect to emulate user input

I am attempting to answer y in a [y/N] prompt during a scripted install on the command line (-y is not an option).
So far I have the following:
#!/bin/bash
expect << EOF
dasht-docsets-install expressjs
expect "[y/N]"
send "y"
EOF
But that gives the following output:
invalid command name "dasht-docsets-install"
while executing
"dasht-docsets-install expressjs"
I have mutiple docsets to install and only one can be installed at a time, i.e I can not go dasht-docsets-install expressjs vuejs etc...so ideally I would like to create a bash function that can be handed a docset like so:
doc_install expressjs
This would take care of handling the input.
Any help would be appreciated.
Use this:
dasht-docsets-install -f expressjs
# `-f`, `--force`
# Forces the operation by overriding the interactive confirmation prompt.
Source: github repo.

Laravel Envoy and bash prompt

I'm using Envoy to provision a remote server. Provisioning is done by pulling the bash script from a private repo and then execute it.
The bash script ask some confirmation like yes/no (using bash "read -p"): it works as expected when i'm connected to the remote server... the script wait for user input.
Instead Envoy seems to ignore any prompt. Is it an expected behavior?
Any workaround?
Yes, this is expected. There's nothing for read to read from so it doesn't.
You have a few options.
Rewrite your script to use a config file when there's no terminal to prompt from.
Use something like [ -t 0 ] to test if the standard input is a terminal and load a configuration file with defaults. The simplest way to do that is just have a file that contains appropriate variable assignments and just source it . defaults.sh or whatever. You don't even need the -t test if you source the defaults first since then anything the user inputs will over-ride the default value.
Rewrite your script to have sane defaults.
Rewrite whatever runs the script to provide your script input via pipeline/file via redirection (e.g. printf 'answer 1\nanswer 2\n' | ./script.sh or ./script.sh <answerfile).

Net::SSH::Shell::Process $DONTEVERUSETHIS

When using Net::SSH to run commands on a remote connection, it adds the following script to the end of each and every command:
DONTEVERUSETHIS=$?; echo #{manager.separator} $DONTEVERUSETHIS; echo \"exit $DONTEVERUSETHIS\"|sh
the output produced looks like:
DONTEVERUSETHIS=$?; echo 10e75e2821012645fa3a3cc08ec5de527a392af68db4c3cac63dac22d4de2a8708fcc176190817fe $DONTEVERUSETHIS; echo "exit $DONTEVERUSETHIS"|sh
Here's a link to the source code Net::SSH::Shell::Process and look at the 'run' method
Can anyone explain why this is always added?
It doesn't appear in the console output but plays hell with parsing ~/.bash_history
A quick look into the source repository reveals this commit:
keep the exitcode 1 available for the next command
In effect, this allows you to inspect the value of $? (i.e. the exitcode of the previous command) in the next command.
TL;DR: It's the machine readable equivalent of a colored shell prompt. It's there to tell the library when the issued command has finished, and whether it was successful.
When running a command with Net::SSH (not ::Shell), here's what happens:
Connection is established
Command is sent
Output is received
The command exits, sshd returns the exit code and ends the connection.
This means that it's easy to:
Get the output: just read until sshd closes the connection.
Get the exit code. sshd returns it.
However, it means that each command is run in a separate session, so cd /tmp followed by pwd will return /home/youruser because these are two different sessions, so the former doesn't affect the latter.
The purpose of Net::SSH::Shell is instead to run multiple, individual commands in the same shell session:
Connection is established.
Commands are sent as a single, infinite, concatenated stream
Output is received as a single, infinite, concatenated stream
This leaves two open questions:
How do you know whether the command has finished or whether it's still processing?
How do you get the exit code now that sshd doesn't return it?
The way Net::SSH::Shell solves this is by modifying the command in the way you saw, to make it print a unique ID and exit code when done:
To get the command's output, read until a line with the unique ID is printed.
To get the exit code, read it from the same line.

How to enter a password into another process prompt from Ruby

I am writing an application that needs to run command on a remote Raspberry PI using a revssh script. revssh is a custom script that implements to some level the Revssh protocol concepts. it uses ssh reverse tunneling to send commands from the server to the clients.
I am using Ruby 2.1, I tried to do this using IO.popen but it does not work, so I tried the following:
# revssh (short for reverse ssh ) enables the execution of remote commands
# from the server on connected clients, like the 'psu_pi_analytics' here. but it requires
# to enter a root password each time you want to run a command using 'revssh -c'
IO.popen('revssh -c psu_pi_analytics uname -a', 'w+') do|io|
io.puts 'password' # enter the password when prompted
puts io.gets
end
this code work if the command to execute run on the local machine, but not in my case.
So any thoughts, or suggestions.
What important here is how to deal with the new connection created by the revssh script using ssh, which is managed in the terminal if the script is run directly from the terminal.
Edit:
By not work I mean it still prompts for the password, even if I puts the password to the io.
You can use an Expect-like library (e.g. RExpect, Expect4r) for interacting with other processes.
Another question related to this: Is there an Expect equivalent gem for Ruby?

Running a shell command from Ruby: capturing the output while displaying the output?

I have a problem.
I want to run a ruby script from another ruby script and capture it's output information while letting it output to the screen too.
runner
#!/usr/bin/env ruby
print "Enter your password: "
password = gets.chomp
puts "Here is your password: #{password}"
The script file that I run:
start.rb
output = `runner`
puts output.match(/Here is your (password: .*)/).captures[0].to_s
As you see here there is a problem.
In the first line of start.rb the screen is empty.
I cannot see the "Enter your password: " in runner.
Is there a way to display the output of the runner script before it's finished, and still let me capture it to a string so I can process the information, eg. using match like in this example?
runner.rb
STDOUT.print "Enter your password: "
password = gets.chomp
puts "Here is your password: #{password}"
Note STDOUT.print
start.rb
require "stringio"
buffer = StringIO.new
$stdout = buffer
require "runner"
$stdout = STDOUT
buffer.rewind
puts buffer.read.match(/Here is your (password: .*)/).captures[0].to_s
output
Enter your password: hello
password: hello
Read more...
I recently did a write-up on this here: Output Buffering with Ruby
Try this:
rd, wr = IO::pipe
pid = Process.fork do
$stdout.reopen(wr)
rd.close
exec("command")
end
wr.close
rd.each do |line|
puts "line from command: #{line}"
end
Process.wait(pid)
Similar if you want to capture stderr. If you need to capture both it would a bit more difficult (Kernel.select?)
Edit: Some explanation. This is an ancient Unix procedure: pipe + fork + calls to dup2 (reopen) depending on what you want. In a nutshell: you create a pipe as a means of communication between child and parent. After the fork, each peer close the pipe's endpoint it does not use, the child remaps (reopen) the channel you need to the write endpoint of the pipe and finally the parent reads on the read channel of the pipe.
For script independent output logging you might want to enable it from the terminal emulator (shell container).
screen -L
OR
xterm -l
This will capture all output produced by any shell or program running inside the emulator, including output generated by your ruby scripts.
You could use tee to write the contents to a file or a pipe, and read the file afterwards.
Have a look at POpen4.
It claims to be platform independent (but I do not think it works in jruby where you can use IO#popen instead).
Have your script do its prompt output to stderr.
echo "Enter something" >&2
read answer
echo "output that will be captured"
This will be done for you if you use read -p to issue the prompt:
read -p "Enter something" answer
echo "output that will be captured"
io = IO.popen(<your command here>)
log = io.readlines
io.close
Now in log variable you have the output of executed command. Parse it, convert it, or do whatever you want.

Resources