#!/usr/bin/perl -w #/**************************************************************************/ #/* */ #/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ #/* */ #/* NXSCRIPTS, NX protocol compression and NX extensions to this software */ #/* are copyright of the aforementioned persons and companies. */ #/* */ #/* Redistribution and use of the present software is allowed according */ #/* to terms specified in the file LICENSE.nxcomp which comes in the */ #/* source distribution. */ #/* */ #/* All rights reserved. */ #/* */ #/* NOTE: This software has received contributions from various other */ #/* contributors, only the core maintainers and supporters are listed as */ #/* copyright holders. Please contact us, if you feel you should be listed */ #/* as copyright holder, as well. */ #/* */ #/**************************************************************************/ # This is a test script for the slave channel feature. While there are no # particular requirements for the slave channel command, what makes the most # sense is to use the feature to run some sort of multiplexer. # # This script contains functionality intended to test the channel's bandwidth, # latency and integrity. # # Usage: # Set NX_SLAVE_CMD to this script and enable the slave channel # # Run: # nxproxy [...] slave=12000 # nxagent -display nx/nx,options=nxagent.conf # slave=22000 in nxagent.conf # # Where 12000 and 22000 are example TCP ports the program will listen on. # # For ease of debugging and running both sides on a single machine, the script # reacts to its own name and changes the prompt to "Proxy" if $0 contains # "proxy", or "Agent" if it contains "agent". This has no other effect. # # use strict; use POSIX ":sys_wait_h"; my $me = "?"; my $EXIT; $| = 1; if ( $0 =~ /proxy/i ) { $me = "Proxy"; } elsif ( $0 =~ /agent/i ) { $me = "Agent"; } else { $me = $0; } print "$me slave. "; if ( scalar @ARGV ) { print "Called with arguments: " . join(' ', @ARGV) . "\n\n"; } else { print "Called without arguments.\n\n"; } my %commands; register('quit' , \&cmd_quit , "Exit."); register('fork' , \&cmd_fork , "Test forking a child process."); register('exec' , \&cmd_exec , "Test calling another process via exec."); register('system' , \&cmd_system , "Test calling another process via system."); register('echo' , \&cmd_echo , "Echo text after the command. Tests channel latency."); register('blkecho' , \&cmd_blkecho, "Echo data by 1k blocks. Tests channel integrity."); register('reexec' , \&cmd_reexec , "Reexecute slave handler."); register('chargen' , \&cmd_chargen, "Output characters forever. Tests channel throughput."); register('randgen' , \&cmd_randgen, "Output random characters forever. Tests channel throughput."); register('discard' , \&cmd_discard, "Accept characters forever. Tests channel throughput."); register('fastgen' , \&cmd_fastgen, "Output a single character forever. Tests channel throughput."); register('env' , \&cmd_env , "Dump the environment."); register('help' , \&cmd_help , "Shows this help."); register('pwd' , \&cmd_pwd , "Print working directory."); cmd_help(); #print "$me> "; my $line; while(!$EXIT) { print "$me> "; # Buffered IO screws things up my $c=""; $line = ""; while($c ne "\n") { my $ret = sysread(STDIN, $c, 1); if (!defined $ret) { die "Read failed: $!"; } if (!$ret) { last; } $line .= $c; } chomp $line; $line =~ s/\r+$//; $line =~ s/\n+$//; next unless ( $line ); my ($cmd, @args) = split(/\s+/, $line); if ( exists $commands{$cmd} ) { $commands{$cmd}->{handler}->( @args ); } else { print "Unknown command: '$cmd'\n"; } } print "$me slave terminated.\n"; sub register { my ($name, $handler, $desc) = @_; $commands{$name} = { handler => $handler, desc => $desc }; } sub cmd_quit { $EXIT = 1; } sub cmd_echo { my (@args) = @_; print "You said: '" . join(' ', @args) . "'\n\n"; } sub cmd_blkecho { my $size = shift // 1024; unless ($size =~ /^\d+/ ) { print "The argument must be a number\n\n"; return; } my $buf; while(1) { my ($tmp, $ret); $buf = ""; while(length($buf) < $size) { $ret = sysread(STDIN, $tmp, $size - length($buf)); if ( !defined $ret ) { die "Error reading from socket: $!"; } last if ( $ret == 0 ); $buf .= $tmp; } my $written =0; while($written < $size) { my $ret = syswrite(STDOUT, $buf, $size, $written); if (!defined $ret) { die "Error writing to socket: $!"; } last if ( $ret == 0); $written += $ret; } } } sub cmd_reexec { print "Will now re-execute: $0 " . join(' ', @ARGV) . "\n"; exec($0, @ARGV); } sub cmd_exec { print "Will now exec: uname -a\n"; exec("uname", "-a"); } sub cmd_system { print "Will now call: uptime\n"; system("uptime"); print "\n"; } # Forks off a short lived process sub cmd_fork { my $pid = fork(); if ( $pid == 0 ) { print "I am the child, with pid $$. Waiting 10 seconds.\n"; sleep(10); print "Child exiting with code 123\n"; exit(123); } else { print "I am the parent, my child is $pid\n"; my $dead = waitpid($pid, 0); print "Reaped child $pid, return $dead\n"; } print "\n"; } sub cmd_help { print "Commands:\n"; for my $cmd ( sort keys %commands ) { print "\t$cmd" . ( " " x (10 - length($cmd))) . ": " . $commands{$cmd}->{desc} . "\n"; } print "\n"; } # Output a single character really fast. # Used to test bandwidth and integrity sub cmd_fastgen { my $char = chr(shift // ord("x")); my $buf = ($char x 72) . "\n"; while( syswrite(STDOUT, $buf) ) { 1; } } # Output RFC 864 chargen. # Used to test bandwidth sub cmd_chargen { my $text = ""; for(my $i=33;$i<33+95;$i++) { $text .= chr($i); } my $pos = 0; my $strlen = 72; while( 1 ) { my $out = substr($text, $pos, $strlen); if ( $pos + $strlen > length($text) ) { $out .= substr($text, 0, $pos + $strlen - length($text) + 1); } $out .= "\n"; syswrite(STDOUT, $out) or return; if ( ++$pos >= length($text) ) { $pos=0; } } } # Output random data # Used to test channel integrity and bandwidth with incompressible content. # Typically slower than chargen/fastgen. sub cmd_randgen { if ( open(my $fh, '<', '/dev/urandom') ) { my $buf; while(1) { sysread($fh, $buf, 1024); syswrite(STDOUT, $buf) or return; } } else { print "Failed to open /dev/urandom: $!. May not be available on this architecture.\n\n"; } } # Accept arbitrary data # Used to test bandwidth sub cmd_discard { my $buf = ""; while( sysread(STDIN, $buf, 1024) ) { 1; } } # Dump the environment sub cmd_env { my $longest = 0; foreach my $var (keys %ENV) { if ( $longest < length($var) ) { $longest = length($var); } } foreach my $var (sort keys %ENV) { print "$var" . (" " x ($longest - length($var))) . ": $ENV{$var}\n"; } print "\n"; } # Show the current directory sub cmd_pwd { require Cwd; import Cwd; print "Current directory: " . getcwd() . "\n\n"; }