Perl Net::SSH2 scp_put puts file and then hangs - windows

I'm using Net::SSH2's scp_put method to place one file in my home directory on a Unix server from a Windows box. I am using Strawberry Perl 5.12 (portable version). I installed the libssh2 1.2.5 binaries and then Net::SSH2 from cpan.
Here's my code snippet:
sub uploadToHost{
my $file=#_[0];
my $host=#_[1];
my $user=#_[2];
my $pass=#_[3];
my $remotelocation=#_[4];
#makes a new SSH2 object
my $ssh=Net::SSH2->new() or die "couldn't make SSH object\n";
#prints proper error messages
$ssh->debug(1);
#nothing works unless I explicitly set blocking on
$ssh->blocking(1);
print "made SSH object\n";
#connect to host; this always works
$ssh->connect($host) or die "couldn't connect to host\n";
print "connected to host\n";
#authenticates with password
$ssh->auth_password($user, $pass) or die "couldn't authenticate $user\n";
print "authenticated $user\n";
#this is the tricky bit that hangs
$ssh->scp_put($file, $remotelocation") or die "couldn't put file in $remotelocation\n";
print "uploaded $file successfully\n";
$ssh->disconnect or die "couldn't disconnect\n";
} #ends sub
Output (edited for anonymity):
made SSH object\n
connected to host\n
authenticated \n
libssh2_scp_send_ex(ss->session, path, mode, size, mtime, atime) -> 0x377e61c\n
Net::SSH2::Channel::read(size = 1, ext = 0)\n
It then hangs forever (>40 minutes in one test) and needs to be killed.
What's strange is that it actually does scp the file to the remote server! It only hangs after it should have completed. I couldn't find references to this curious problem elsewhere on StackOverflow or elsewhere.
Can anyone point me in the right direction to either 1) stop it from hanging, or 2) implement (as a workaround) a timer that kills this one command after a few seconds, which is enough time to scp the file?
Thanks, everyone!

You can try using alarm() to prod your process into behaving, if you save this example as 'alarm.pl' you can see how it works:
use strict;
use warnings;
use 5.10.0;
# pretend to be a slow process if run as 'alarm.pl s'
if (#ARGV && $ARGV[0] eq 's') {
sleep(30);
exit();
}
# Otherwise set an alarm, then run myself with 's'
eval {
local $SIG{ALRM} = sub {die "alarmed\n"};
alarm(5);
system("perl alarm.pl s");
};
if ($#) {
die $# unless $# eq "alarmed\n";
say "Timed out slow process";
}
else {
say "Slow process finished";
}

Use Net::SFTP::Foreign with the Net::SSH2 backend, Net::SFTP::Foreign::Backend::Net_SSH2:
use Net::SFTP::Foreign;
my $sftp = Net::SFTP::Foreign->new($host, user => $user, password => $password, backend => Net_SSH2);
$sftp->die_on_error("Unable to connect to remote host");
$sftp->put($file, $remotelocation);
$sftp->die_on_error("Unable to copy file");
If that doesn't work either, you can try using plink (from the PuTTY project) instead of the Net::SSH2 backend.

I don't think it is hanging it is just REALLY SLOW. 10x slower than what it should be. The reason the file would appear to be there is that it allocates the file before it has finished transferring. This isn't really too unexpected, Perl finds new ways to disappoint and frustrate programmers on a daily basis. Sometimes I think I spend more time working around Perl's idiosyncrasies and learning 10 slightly different ways to do the same thing than doing real work.

Related

How to deal with shell commands that never stops

Here is the case;
There is this app called "termux" on android which allows me to use a terminal on android, and one of the addons are androids API's like sensors, tts engines, etc.
I wanted to make a script in ruby using this app, specifically this api, but there is a catch:
The script:
require('json')
JSON.parse(%x'termux-sensor -s "BMI160 Gyro" -n 1')
-s = Name or partially the name of the sensor
-n = Count of times the command will run
returns me:
{
"BMI160 Gyroscope" => {
"values" => [
-0.03...,
0.00...,
1.54...
]
}
}
I didn't copied and pasted the values, but that's not the point, the point is that this command takes almost a full second the load, but there is a way to "make it faster"
If I use the argument "-d" and not use "-n", I can specify the time in milliseconds to delay between data being sent in STDOUT, it also takes a full second to load, but when it loads, the delay works like charm
And since I didn't specify a 'n' number of times, it never stops, and there is the problem
How can I retrieve the data continuously in ruby??
I thought about using another thread so it won't stop my program, but how can I tell ruby to return the last X lines of the STDOUT from a command that hasn't and will not ever stop since "%x'command'" in ruby waits for a return?
If I understood you need to connect to stdout from a long running process.
see if this works for your scenario using IO.popen:
# by running this program
# and open another terminal
# and start writing some data into data.txt
# you will see it appearing in this program output
# $ date >> data.txt
io_obj = IO.popen('tail -f ./data.txt')
while !io_obj.eof?
puts io_obj.readline
end
I found out a built in module that saved me called PTY and the spawn#method plus thread management helped me to keep a variable updated with the command values each time the command outputted new bytes

Expect->Telnet->Store and read a variable / Check if directory exists

Summary:
I am writing a script to check if a directory exists on a remote machine. I need a solution which can allow me to check for that directory and return the result in a usable way. This whole process is automated within a much larger script so I need a functional way to tell the parent script the directory exists or not.
Restrictions:
The tools that I have are limited. $REMOTE_2 can only be accessed through $REMOTE_1. Also, $REMOTE_2 can only be connected via telnet (no ssh available).
Current goal:
I am trying to set a local variable to be then read back to chose a return code. I am open to other options, but this is the closest I've come to a working solution so far.
I realize that $found will take from the parent process, but this is not my desired result and am not sure what syntax I need to return true or false when trying to echo the $found variable.
/usr/bin/expect<<EOF
spawn ssh $USER#$REMOTE_1
expect "*$USER*"
send -- "telnet $REMOTE_2\r"
expect "*login:*"
send -- "root\r"
expect "*$*"
# Everything prior to this can't be changed. Everything after it can be.
send "if \[ -d $DIRECTORY_LOCATION \] ; then found=true; else found=false ; fi\r"
send -- "echo **$found**\r"
expect {
"*true*" {
exit 0
}
"*false*" {
exit 1
}
}
EOF
I believe this type of solution can work, but I am not sure how to use the remote variable that I store within the if statement, later on to allow me to choose which return code to use.

Windows process called by perl doesn't write to file properly

I'm trying to call a perl script from another perl script, read from serial port, and write to a file. I've distilled my code to isolate the problem, so it probably won't make sense what the point of the code is.
Caller:
use Win32::Process;
my $perl_path = $^X;
my $SerialLogProcess;
my $SerialLogObj;
my $serial_log_script = "callee.pl";
Win32::Process::Create($SerialLogObj, "$perl_path", "perl $serial_log_script " ,0,NORMAL_PRIORITY_CLASS,".");
$SerialLogProcess = $SerialLogObj->GetProcessID();
print "waiting for 3 secs";
sleep(3);
print "done";
`taskkill /F /T /PID $SerialLogProcess`;
Callee:
use Win32::SerialPort;
my $portObj = new Win32::SerialPort("\\\\.\\COM70") || die;
my $serialReading;
$portObj->baudrate(115200);
$portObj->parity("none");
$portObj->databits(8);
$portObj->stopbits(1);
system("rm -r \"log.txt\"");
open (LOGFILE, ">>log.txt") or die;
while (1){
$serialReading = $portObj->read(10);
print LOGFILE $serialReading;
#print LOGFILE " ";
}
So the caller creates a process for the callee script, and then kills it after 3 seconds. In those 3 seconds, I do something that gives guaranteed messages for the serial port to read. The log file is created, but nothing is written to it.
Heres what's weird: I can make the messages show up two ways. I run the callee script straight from the command line, or I can uncomment that last print. Unfortunately, these aren't solutions for me. I'm pretty stumped why I can't get my code to work properly, and it makes me think there is some kind of undefined behavior.
The output is buffered by default. The data will eventually be written once there's enough to write. Or you can use the following which will cause a flush for every print.
use IO::Handle qw( ); # Needed before Perl 5.14
open (LOGFILE, ">>log.txt") or die;
LOGFILE->autoflush(1);
But ug, why use a global variable???
open (my $LOGFILE, ">>log.txt") or die;
$LOGFILE->autoflush(1);
print $LOGFILE $serialReading;

Perl Windows - watch a file and read last line

I'm trying to watch a file in Windows Perl. I'm using Win32::ChangeNotify
Here is my code:
use strict;
use warnings;
require Win32::ChangeNotify;
use Data::Dumper;
my $Path="C:\\Eamorr\\";
my $WatchSubTree=0;
my $Events="FILE_NAME";
my $notify=Win32::ChangeNotify->new($Path,$WatchSubTree,$Events);
while(1){
$notify->reset;
$notify->wait;
print "File changed\n";
}
But "File changed" never gets printed! I realise this is quite basic stuff, but I'm really struggling on this Windows platform.
I have a file in "C:\Eamorr\Eamorr.out" which I want to monitor for changes (a new line of data is appended to this file every ten minutes by another program).
When Eamorr.out is updated, I want to be able to run some Perl and populate a MySQL table.
Please help me watching the file Eamorr.out and printing the last line to the console.
p.s. I'm on Windows Server 2003
Many thanks in advance,
This works on my Windows 7, Activestate Perl 5.16.
use feature ":5.16";
use warnings FATAL => qw(all);
use strict;
use Data::Dump qw(dump);
use Win32::ChangeNotify;
my $Path='C:\Phil\z\Perl\changeNotify\\';
my $WatchSubTree = 0;
my $Events = "SIZE";
say STDERR "Exists=", -e $Path;
my $notify=Win32::ChangeNotify->new($Path,$WatchSubTree,$Events) or say("Error=", Win32::GetLastError());
while(1)
{$notify->reset;
$notify->wait;
say STDERR "File changed";
}

No Gui Option?

Ok, well I'm really pushing the bounds as to what shoes is for here, so I don't expect any miracles: is there a way to optionally run a shoes app without a gui?
The reason I'd like to do this is I'm creating a tool for use by "non computer people", as well as "computer people", who would rather just run the program as a command line tool, maybe even on systems without X/gtk installed. (I work as a multidisciplinary researcher, and shoes is great for focusing on tools and not fiddling with gui design all day.)
Here's some example code:
if(ARGV[1] == "nogui")
puts "running computation on #{ARGV[2]}";
exit();
end
Shoes.app(:width => 200, :height => 100) do
#button = button("Quit").click() {
exit();
}
end
which works except I get a
Gtk-CRITICAL **: gtk_main_quit: assertion `main_loops != NULL' failed
error.
I haven't tried it, but I don't know that Shoes will even happily start on a system without X. You're probably better off creating a shell script that chooses which version to start. Something like this:
#!/usr/bin/sh
NOGUI=0
if [ $# -gt 0 ]; then
NOGUI=$1
fi
if [ $NOGUI = nogui ]; then
shift
echo "Running in command-line mode..."
ruby command-line.rb "$#"
else
echo "Starting Shoes..."
shoes shoes.rb "$#"
fi
If the first argument is nogui, the remaining arguments are sent to the Ruby version, otherwise all arguments (including the first) are sent to Shoes.
Now you just need to separate the actual performance logic out so that it can be imported into both versions.

Resources