Index


1. General information
2. PERL one-liner example - autologin via CYGWIN ftp client on SHELL-TERMINAL connect
3. PERL interactive PLUGIN (I-PLUGIN) example


1. General information

User can write program in any language to automate terminal interaction with remote host.

Such program is executed locally as child process of the terminal and it is referred to as PLUGIN.

To use PLUGIN one has to create and use PLUGIN Launcher on LauncherTree:

PLUGIN launcher contains command line to start local child process in terminal.

This child process (PLUGIN) takes control of the terminal and iteracts with remote host on behalf of the user.

When PLUGIN process is terminated, it returns control over terminal to user.

PLUGIN process can terminate when it finished execution or it can be forcefully terminated by user (by pressing "Kill" button in terminal where PLUGIN is executing ) to take over control over terminal. "Kill" button appear in the terminal only while PLUGIN process is running. PLUGIN process is also forcefully killed when its terminal is closed or disconnected or when proxy32 application is exited.

Fig.1 below shows how PLUGIN process controls the terminal. This example is assuming TELNET-TERMINAL, but it will eqally apply to SHELL-TERMINAL or COM-PORT TERMINAL (Pls, see TERMINAL launchers ). It does not matter where terminal is connected to.

Fig.1 Interface between Terminal and PLUGIN process

Fig.1 Interface between Terminal and PLUGIN process

When any Windows (or UNIX/CYGWIN) process is created, it has three input/output byte streams (STDIN/STDOUT/STDERR) already open for the programmer to use. If console type (non-GUI) Windows process is started directly by the user via mouse/keyboard actions, Windows console window is automatically created to control such process. Then STDOUT and STDERR streams of the process are automatically connected to the screen of console window, and STDIN stream of the process is connected to the keyboard of console window. If GUI type Windows process is started directly by the user via mouse/keyboard actions, started GUI Windows process will programmatically create main window to interact with the customer. Regular PLUGIN process is not started by the user, it started as child process of active terminal. PLUGIN process should not depend on presence of console window as it will not have one. Instead, its STDIN/STDOUT/STDERR will be connected via internal byte pipes to the terminal instance in which it is running. PLUGIN process can have GUI main window to interact with the user, but simple PLUGIN does not have to interact with the user, only with remote host. If PLUGIN need also to interact with the user, it requires fourth channel in addition to existing three channels (STDIN/STDOUT/STDERR). There are two options to interact with the user:

PLUGIN process is created differently comparing to the process that is started directly by user. PLUGIN is started as child process from inside of the active (red frame) Proxy32 terminal when user double-clicks PLUGIN launcher in LauncherTree window. Terminal does following:

Output of the remote host is buffered by terminal before sending it to the STDIN stream of PLUGIN process. It is done to ensure no remote host's data is lost while PLUGIN process is initializing itself and preparing to run. Also, if PLUGIN process is buggy or very slow and, therefore, unable to receive and process the output from remote host in the real time, then terminal's buffer that holds output of remote host will start filling up. Terminal will detect filling up of the buffer and automatically kill PLUGIN process to prevent loss of the output from remote host.

PLUGIN process can spawn it's own child processes if needed. Proxy32 terminal owns the whole PLUGIN process subtree and able to terminate the whole PLUGIN process tree at any time.

In the following scenarios PLUGIN process tree is killed:

Here are the rules for the programmer who writes the program intended to be executed as terminal PLUGIN process:

2. PERL one-liner example - autologin via CYGWIN ftp client on SHELL-TERMINAL connect

Example of PLUGIN command line:

Fig.2 Example of PLUGIN command line

Fig.2 Example of PLUGIN command line


perl -e '
$a="";while(){sysread STDIN,$_,10000;syswrite STDERR,$_;$a.=$_;last if $a=~/Name/;}
syswrite STDOUT,\'belous\';syswrite STDOUT,"\r\n";
$a="";while(){sysread STDIN,$_,10000;syswrite STDERR,$_;$a.=$_;last if $a=~/assword:/;}
syswrite STDOUT,\'<PROXYPASSWORD=belous password>\';syswrite STDOUT,"\r\n";
$a="";while(){sysread STDIN,$_,10000;syswrite STDERR,$_;$a.=$_;last if $a=~/ftp>/;}
'

There are seven lines of code here (1-7):

  1. starts PERL interpreter and tells it that the PERL script to be executed is located in the string that will be in the command line of the interpreter (following -e command line switch). Single quote at the end of the line starts the string that contains in-line PERL script.
  2. Infinite loop: Reads bytes from remote host (STDIN), relays them to terminal screen (STDERR) and scans them for the presence of the prompt/phrase Name. Loop is exited when prompt is found.
  3. sends user name belous to remote host (STDOUT) followed by the CR/NL.
  4. Infinite loop: Reads bytes from remote host (STDIN), relays them to terminal screen (STDERR) and scans them for the presence of the prompt/phrase assword:. Loop is exited when prompt is found.
  5. sends user password <PROXYPASSWORD=belous password> to remote host (STDOUT) followed by the CR/NL.
  6. Infinite loop: Reads bytes from remote host (STDIN), relays them to terminal screen (STDERR) and scans them for the presence of the prompt/phrase ftp>. Loop is exited when prompt is found. This is the last line of the PERL script, PLUGIN is exiting here and connection between remote host and user's screen and keyboard is automatically restored.
  7. single quote on this line signifies the end of the string that contains in-line PERL script

Note that in Proxy32 by design PLUGIN launcher can be automatically executed. When user double-clicked on any TERMINAL launcher, if it executed successfully, terminal will become connected (for example, to remote telnet server or to local ssh client that is in turn connected to remote ssh server). If connect is successful, any TERMINAL launcher will automatically (without user intervention) execute any PLUGIN (or I-SCRIPT) launcher that is located immediately under this TERMINAL launcher on the LauncherTree. This feature allows to perform login automatically (without manually executing PLUGIN launcher) on connection of the terminal. That is very convenient as double-click on TERMINAL launcher not only creates and connects terminal window but also automatically launches login script when connection with remote host is established. Also, if user wants to disable automatic login script, he/she needs to simply move script launcher (by mouse drag-n-drop) to another location on the LancherTree (location that is no longer immediately under TERMINAL launcher).

This particular PLUGIN is designed for the automatic start scenario when after CYGWIN ftp client is started in SHELL-TERMINAL terminal (for example, SHELL-TERMINAL with following command line: proxycygterm.exe -s '/usr/bin/ftp 192.168.1.9 21' ) and connected to remote host, it would print prompt Name then password: to the terminal. When PLUGIN is started automatically on connect (launch of ftp client in SHELL-TERMINAL), prompt Name from ftp client will be received by the PLUGIN and not by the user on the screen of the terminal. If this PLUGIN would be started manually, it would be too late to try to read the prompt Name from remote host (aka connected ftp client). The prompt Name will be already received and printed on the screen of terminal by the time when PLUGIN process would start reading from its STDIN pipe.

If PLUGIN uses Proxy32 parameters substitution to substitute parameters into PLUGIN code right before it is executed, then the start of the PLUGIN may be delayed if protected parameter <PROXYPASSWORD=belous password> is not defined (empty value) in Proxy32 parameter memory or this parameter is not labelled as do not ask again. If parameter that is present in the command line of the PLUGIN is not defined (empty value) or not labelled as do not ask again then proxy32 pops up the parameter input dialog before attempting to replace protected parameter <PROXYPASSWORD=belous password> by its value. Parameter input dialog will hold the start of the PLUGIN. The prompt Name will be already received and printed on the screen of the terminal by the time when PLUGIN process would start reading from its STDIN pipe. PLUGIN will never find the prompt but will keep looking for it indefinitely. It will have to be killed manually, then parameter needs to be properly defined not to break PLUGIN execution next time terminal connects. To avoid this problem either define the value of <PROXYPASSWORD=belous password> in advance and label it as do not ask again or simply do not use parameter substitution in the PLUGIN. Not using parameter substitution in PLUGIN poses some security risk as the paswword have to literally typed into the code of the PLUGIN and to be stored as part of this code. If you do not want to use parameter substitution in this PLUGIN, just replace <PROXYPASSWORD=belous password> in the code of the PLUGIN by its actual value (in my case it is test123). Here is how PLUGIN code would look without using parameter substitution:


perl -e '
$a="";while(){sysread STDIN,$_,10000;syswrite STDERR,$_;$a.=$_;last if $a=~/Name/;}
syswrite STDOUT,\'belous\';syswrite STDOUT,"\r\n";
$a="";while(){sysread STDIN,$_,10000;syswrite STDERR,$_;$a.=$_;last if $a=~/assword:/;}
syswrite STDOUT,\'test123\';syswrite STDOUT,"\r\n";
$a="";while(){sysread STDIN,$_,10000;syswrite STDERR,$_;$a.=$_;last if $a=~/ftp>/;}
'

Again, please note, it is a very bad security practice to keep passwords as part of the PLUGIN code.

Starting from version of July 15th 2012 the need to pre-define values of the parameters used in autologin PLUGINS was eliminated. When Proxy32 parses substitution parameters of terminal launcher it also tries to locate associated autologin PLUGIN and parse its substitution parameters also. Then all parameters (both parameters to be substituted into terminal launcher and parameters to be substituted into autologin PLUGIN) are combined into one parameter input dialog that pops up before the start of execution of the terminal launcher. At this stage user finalizes parameter's values and attributes and no parameter input dialog will pop up before the start of the PLUGIN itself.

Other things to note for this particular PLUGIN:

This particular PLUGIN is built-in into proxy32 "New" dialog that helps user to add pre-defined launchers to the LauncherTree. It is used as login auto-completion script for SHELL-TERMINAL that uses CYGWIN ftp client.

Above PLUGIN is very compact one-liner but it still has to manage STDIN/STDOUT/STDERR byte pipes directly and this may become a bit cumbersome for everyday's PLUGIN writing. Controlling terminal via I/O byte pipes allows to write PLUGIN in any language (not just in PERL). On the other hand, if one uses PERL PLUGINS most often, then all byte-pipe control stuff can be encapsulated into some PERL function and simply called when needed from multiple PERL PLUGINs. To simplify user's life I have wrapped proper management of STDIN/STDOUT/STDERR byte pipes into handy PERL function and placed it into separate include file (gco.pl), so it can be re-used very easy by adding require "gco.pl"; statement to the beginning of any PERL PLUGIN.

Function is named "gco" (which stands for get command output) is defined in the include file and it is used to send command to remote host and wait for certain prompt string that signify the end of the command printout and readiness of the remote host to accept new command. Command printout is captured to PERL variable and can be analyzed/parsed later by the next statements in the PERL PLUGIN. User can control whether the printout of the command will appear on the terminal screen or it will not be shown.

Instead of using gco function, one can always replace it with the following two lines (replace sent command ls and expected prompt ftp> in below code by what you need):


syswrite STDOUT,\'ls\';syswrite STDOUT,"\r\n";
$a="";while(){sysread STDIN,$_,10000;syswrite STDERR,$_;$a.=$_;last if $a=~/ftp>/;}

On the other hand, using gco function can improve readability of your PLUGIN code and reduce its size.

One-liner PERL PLUGINs can be very long as Windows can take command line up to 32767 characters long!!!

One-liner PERL PLUGINs are very convenient as the whole PLUGIN code is stored inside of its command line and we do not need to use separate file(s) to store PLUGIN code. The whole PLUGIN resides completely inside of its own launcher!!!

3. PERL interactive PLUGIN (I-PLUGIN) example

This example shows that code of PLUGIN can be placed into .pl file and then this file can be passed to perl interpreter for the execution.

Path to perl interpreter (from CYGWIN installation) is already in the PATH environment variable.

Current directory (.\) for PLUGIN process is pointing to startup directory of proxy32.

I-PLUGIN (child process started by I-PLUGIN launcher) is executed in the same fashion as PLUGIN, the difference is that proxy32 appends name of temporary file (that passes keystrokes from user's keyboard in terminal) to the command line of I-PLUGIN.

I-PLUGIN can read user's keystrokes from the file name of which is passed as last command line argument.

If this functionality is not needed PLUGIN launcher should be used.


perl .\plug\proxy_plugin_interactive.pl



#!/usr/bin/perl                                              
$help_text = <<EOD;
This tool can only be executed as PROXY PLUGIN!!!\r
It is changing prompt to \"REMOTE_SHELL_PROMPT>\" at remote UNIX/LINUX!!!\r
EOD

syswrite STDERR, "$help_text \r\n";

#print command line of I-PLUGIN for debug purposes
#last command line argument (appended by I-PLUGIN execution startup code in terminal) 
#should be name of temporary file that passes keystrokes from user's keyboard in terminal.
#arguments before last can be used to pass parameters to I-PLUGIN during its startup                                                           
syswrite STDERR,"\r\nStart of PLUGIN!\r\n";                      
syswrite STDERR,"Command line used: ";                     
syswrite STDERR,"$0 ";                                     
for($i=0;$i<$#ARGV;$i++){syswrite STDERR,"$ARGV[$i] ";}                               
syswrite STDERR,"\r\n";
                           
#check if we are in interactive mode
#in interactive mode at least one command line parameter 
#(name of temporary file that passes keystrokes from user's keyboard in terminal)
#should be present
if($#ARGV<0)
    {
    syswrite STDERR,"This PLUGIN can only be executed in interactive mode";
    exit(0);
    }
                                                             
#open TEMP file to read from the user's keyboard in terminal window 
#(TEMP file is created by I-PLUGIN launcher and its name is passed as last command parameter to I-PLUGIN)  
sysopen(TMPFD,$ARGV[$#ARGV],O_RDONLY)                             
or die "sysopen $ARGV[$#ARGV]: $!";                             

#setup SIGTERM handler for CYGWIN 
#in 2000-2001 proxy was written in C++ and compiled under CYGWIN
#it was terminating PLUGIN/I-PLUGIN by sending SIGTERM to it.
#this is no longer the case for modern version of proxy32
#modern version of proxy32 terminates PLUGIN/I-PLUGIN by closing JOB object in Windows                                                           
$SIG{TERM} = \&getout;

#change shell prompt to '#' 
syswrite STDERR,"\r\nChanging prompt to \"REMOTE_SHELL_PROMPT>\":\r\n";                      
syswrite STDOUT, "export PS1='REMOTE_SHELL_PROMPT>'\r\n";                                                             
while(1)                         
    {                        
    sysread STDIN, $greeting,10000;          
    syswrite STDERR, $greeting;          
    if($greeting =~ /REMOTE_SHELL_PROMPT>/){last;}           
    }                        

#menu loop
while(1)                                                     
    { 
    #print menu
    syswrite STDERR,"\r\n\r\n\r\n\r\n\t\tMENU:"; 
    syswrite STDERR,"\r\n\t\t1:ls -altrR";                    
    syswrite STDERR,"\r\n\t\t2:df";                    
    syswrite STDERR,"\r\n\t\t3:pwd";                   
    syswrite STDERR,"\r\n\t\t4:ps -ef";                
    syswrite STDERR,"\r\n\t\tq:quit plugin";           
    syswrite STDERR,"\r\n\t\tEnter your choice:";
    #wait for user's choice 
    #or for "Ctrl-C" from keyboard of local terminal
    while(1)                                                 
            {                                                
            sysread TMPFD, $greeting,10000;                    
            syswrite STDERR, $greeting;                      
            if($greeting =~ /[1234q]/){last;}                  
            if($greeting eq "\003"){\&getout();}
            sleep(1);                  
            }                                                
    syswrite STDERR,"\r\n";
    #execute user's choice - 1 - send command to remote UNIX shell
    if($greeting=~/1/){syswrite STDOUT, "ls -altrR\r\n";};      
    if($greeting=~/2/){syswrite STDOUT, "df\r\n";};      
    if($greeting=~/3/){syswrite STDOUT, "pwd\r\n";};     
    if($greeting=~/4/){syswrite STDOUT, "ps -ef\r\n";};  
    if($greeting=~/q/)                                       
            {                                                
            syswrite STDERR, "Exiting....\r\n";
            sleep(1);
            exit(0);                                         
            }                                                
    #execute user's choice - 2 - process command output from remote UNIX shell
    #while checking for "Ctrl-C" from keyboard of local terminal
    while(1)                                                 
            {                                                 
            sysread STDIN, $greeting,10000;                     
            syswrite STDERR, $greeting;                       
            last if $greeting =~ /REMOTE_SHELL_PROMPT>/;#detect end of command printout by finding command prompt
            checkctrlc();#check is user wants to intrrupt printout of command output coming from remote shell and exit from PLUGIN                        
            }                                                 
    }#end of dead while loop: print_menu/select/execute                                     
                                                             
############################################################################################
sub getout
    {
    syswrite STDERR, "\r\n\r\nSIGTERM or Ctrl-C has been received!!!!";
    syswrite STDERR, "\r\nCleaning up. Exiting...\r\n\r\n";
    exit();
    }
############################################################################################
sub checkctrlc
    {
    #to exit on Ctrl-C received from user keyboard                        
    sysread TMPFD, $greeting,10000;                    
    if($greeting eq "\003"){\&getout();}                  
    }



belous@belous-PC-vista ~
$ 
============================
I-PLUGIN has started!!!
============================
This tool can only be executed as PROXY PLUGIN!!!
It is changing prompt to "REMOTE_SHELL_PROMPT>" at remote UNIX/LINUX!!!
 

Start of PLUGIN!
Command line used: .\plug\proxy_plugin_interactive.pl 

Changing prompt to "REMOTE_SHELL_PROMPT>":
export PS1='REMOTE_SHELL_PROMPT>'
REMOTE_SHELL_PROMPT>



                MENU:
                1:ls -altrR
                2:df
                3:pwd
                4:ps -ef
                q:quit plugin
                Enter your choice:1
ls -altrR
.:
total 8
drwxrwx---+ 1 belous None    0 Jul 22 22:13 ..
drwxrwxr-x+ 1 belous None    0 Jul 23 16:26 mytestdir
-rw-rw-r--+ 1 belous None 1476 Aug 21 15:03 test.txt
drwxrwxr-x+ 1 belous None    0 Aug 21 15:03 .

./mytestdir:
total 4
-rw-rw-r--+ 1 belous None 0 Jul 23 16:26 mytestfile.txt
drwxrwxr-x+ 1 belous None 0 Jul 23 16:26 .
drwxrwxr-x+ 1 belous None 0 Aug 21 15:03 ..
REMOTE_SHELL_PROMPT>



                MENU:
                1:ls -altrR
                2:df
                3:pwd
                4:ps -ef
                q:quit plugin
                Enter your choice:2
df
Filesystem                         1K-blocks       Used Available Use% Mounted on
C:/cygwin/home/belous/proxy32/cyg  468856828  155766948 313089880  34% /
E:                                1465136000 1445925448  19210552  99% /cygdrive/e
F:                                1953513556 1930287848  23225708  99% /cygdrive/f
REMOTE_SHELL_PROMPT>



                MENU:
                1:ls -altrR
                2:df
                3:pwd
                4:ps -ef
                q:quit plugin
                Enter your choice:3
pwd
/home/belous
REMOTE_SHELL_PROMPT>



                MENU:
                1:ls -altrR
                2:df
                3:pwd
                4:ps -ef
                q:quit plugin
                Enter your choice:4
ps -ef
     UID     PID    PPID  TTY        STIME COMMAND
  belous    3712    3888 pty0     18:39:29 /usr/bin/ps
  belous    3888    1884 pty0     18:39:19 /usr/bin/bash
  belous     936       1 ?        18:39:21 /usr/bin/perl
  belous    1884       1 ?        18:39:19 /cygdrive/c/cygwin/home/belous/proxy32/proxycygterm
REMOTE_SHELL_PROMPT>



                MENU:
                1:ls -altrR
                2:df
                3:pwd
                4:ps -ef
                q:quit plugin
                Enter your choice:q
Exiting....

============================
PLUGIN has finished!!!
============================


1. General information
2. PERL one-liner example - autologin via CYGWIN ftp client on SHELL-TERMINAL connect
3. PERL interactive PLUGIN (I-PLUGIN) example


Index