busybox ash via paramiko not emitting prompt on stdout

893 views Asked by At

I created Python GUI that takes a list of commands as input and executes the list through a Telnet or SSH session. While opening a SSH session (using Paramiko in Python) I run commands on various devices using this code in a for loop:

                    stdin.write(command+"\n")
                    then = time.time()
                    timeout=10.0
                    while not stdout.channel.exit_status_ready():

                        if timeout is not None:
                            timeTest =time.time() - then
                            if timeout <= timeTest:
                                break 

                    # Only print data if there is data to read in the channel
                        if stdout.channel.recv_ready():

                            # Write data from stdout
                            temp=str(stdout.channel.recv(1024))
                            print temp
                            if (temp.endswith(delim)) or (temp.endswith("# ")) :
                                print "successful exit"
                                break

The code is designed to be used on modems that have BusyBox installed. Thus it's common for a user to enter a command to open busybox and run a sequence of commands in the BusyBox shell. As you can see in this line of code "if (temp.endswith(delim)) or (temp.endswith("# "))" the while loop is suppose to break when the command prompt of busybox is detected which is a "# " (this means the command has finished outputting).

The problem I'm having is that BusyBox isn't printing the command prompt to stdout or in the debug line "print temp". Why is this? The command outputs (an example is ls -l) are successfully printed to stdout but not the command prompt or busybox intro message: when a user enters busybox on these modems a introduction message is printed "BusyBox v1.17.2 (2014-10-02 10:50:35 PDT) built-in shell (ash) Enter 'help' for a list of built-in commands." which is also not printed to STDOUT. This is forcing the code to utilize the timeout for each command executed in busybox which is undesirable, i.e. it's slow and there could be command outputs that take longer than the desired timeout so it's best to look for the command prompt.

Is this issue due to the implementation of ash in BusyBox? Is there a way to receive the command prompt text?

2

There are 2 answers

0
Charles Duffy On BEST ANSWER

First: If you're trying to recognize shell prompts when invoking a shell programmatically, you're Doing It Wrong.

If you use exec_command() on a new channel (over the same transport) for each command you want to run, you'll get a separate stdout, stderr, etc. for each command; have the exit status for that command individually reported, and never need to guess whether a piece of output is from the command or from the outer shell.

Second: If you really want to do it that way (and, again, you shouldn't!), you should be using invoke_shell() after a get_pty() call, not exec_command('sh').

10
Charles Duffy On

See the entry on PS1 (the variable specifying the prompt to emit) in IEEE standard 1003.1 (emphasis added):

PS1

Each time an interactive shell is ready to read a command, the value of this variable shall be subjected to parameter expansion and written to standard error. The default value shall be "$ ". For users who have specific additional implementation-defined privileges, the default may be another, implementation-defined value. The shell shall replace each instance of the character '!' in PS1 with the history file number of the next command to be typed. Escaping the '!' with another '!' (that is, "!!" ) shall place the literal character '!' in the prompt. This volume of POSIX.1-2008 specifies the effects of the variable only for systems supporting the User Portability Utilities option.

Thus, any POSIX-compliant shell -- not just busybox ash, but ksh, dash, bash, and all the rest -- must write its prompt to stderr, not stdout.


Your simple fix, then, is to either read from stderr, or to use redirection to combine the streams (running exec 2>&1, for instance).