socat blocking on stdin - perl

I have a bash script as follow on an AIX host, myscript.sh:
MODE="$1"
if [ "$MODE" == "start" ]; then
socat -T100 -lf $LOGF -d -d -d -x TCP4-LISTEN:$LISTENINGPORT,bind=$LISTENINGADDR,reuseaddr,fork EXEC:"$0 proxy" &
PID=$!
echo $PID > $PIDFILE
echo "$0 $MODE started (pid=$PID)"
elif [ "$MODE" == "proxy" ]; then
cat - > $TMPFILE
# process $TMPFILE before the SSL connection.
cat $TMPFILE | socat -T 100 -lf $LOGF -d - OPENSSL:$HOST
rm -f $TMPFILE
Everything is fine when I run:
$ cat somefile | myscript.sh proxy | xxd
The problem raise when I connect to the socat listener with a test script:
my $file = $ARGV[0];
my $fsize = -s $file;
my $socket = IO::Socket::INET->new("127.0.0.1:$port")
or die "Couldn't connect to remote host: $!";
$socket->autoflush(1);
binmode($socket);
open (FILE,$file);
binmode(FILE);
my $buffer ;
while(sysread(FILE, $buffer, $blocksize)) {
print $socket $buffer ;
}
print "sent\n" ;
close (FILE) ;
my $answer = <$socket>;
if (defined($answer)) {
print $answer; # never reached
print "...\n" ;
} else {
die "connection reset by peer\n";
}
In myscript.sh, it blocks on the line:
cat - > $TMPFILE
In the test script, it blocks on the line:
my $answer = <$socket>;
At this point, the data has been received by the socat listener (checked with tcpdump).
However, when I Ctrl+c the test script before the socat timeout, the data goes through the pipe (i.e., the SSL server is eventually contacted).
What am I doing wrong?
Update:
Thanks for the tips about cat and EOF. For the time being, I have worked around the problem like so:
timeout 0.2 cat -u - > $TMPFILE 2>>/dev/null
# process $TMPFILE before the SSL connection.
cat $TMPFILE | socat -T 100 -lf $LOGF -d - OPENSSL:$HOST
It's ugly, and a waste 0.2 seconds, I hope to find a better solution.
But it does the job for now. The 2>>/dev/null part is because AIX complains about an invalid counter (related to the timeout command).

My first thought is that there is no linefeed in the data you're trying to receive with cat - or <STDIN> . Both commands in their default behavior will return data once they have a linefeed or their buffers of the file-descriptor is full (4KB by default in Linux).

Related

How to suppress piped input sent to perl on command line?

At my comand prompt, I ran a grep and got the following result.
$ grep -r "javascript node"
restexample/NewsSearchService/V1/madonna_html.html:<!-- start empty javascript node for popup app fix -->
restexample/NewsSearchService/V1/madonna_html.html:<!-- end empty javascript node for popup app fix -->
Now, suppose I want to remove the "restexample" part. I can do that by using
print substr($_,13)
However, how when I pipe to perl, this is what I get -
grep -r "javascript node" | perl -pe ' print substr($_,11) '
/NewsSearchService/V1/madonna_html.html:<!-- start empty javascript node for popup app fix -->
restexample/NewsSearchService/V1/madonna_html.html:<!-- start empty javascript node for popup app fix -->
/NewsSearchService/V1/madonna_html.html:<!-- end empty javascript node for popup app fix -->
restexample/NewsSearchService/V1/madonna_html.html:<!-- end empty javascript node for popup app fix -->
As you can see, the piped input simply got echoed. How to prevent this?
Try
grep -r "javascript node" | perl -lpe '$_ = substr($_,11)'
or
grep -r "javascript node" | perl -lne 'print substr($_,11)'
Explanation: -p switch automatically prints current line ($_) while -n switch doesn't.
perl -MO=Deparse -lpe '$_ = substr($_,11)'
BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined($_ = <ARGV>)) {
chomp $_;
$_ = substr($_, 11);
}
continue {
die "-p destination: $!\n" unless print $_; # <<< automatic print
}

ssh login verification in perl

I am running following lines of code in perl script to find a df output of remote machines. this works fine and gather a info in #df_ret untill until ssh key is uptodate. if public is corrupted or changed, its not showing any sign of error in script. if i run manually then i will be asked for the password.
#df_ret = split /\n/, `ssh -q $server 'df -hP'`;
Is there any way i can verify that if ssh login is successful then this perl script line should be executed else not. been finding many searches on it but could not reach to the ONE. any help?
I have got one solution working as below;
#!/usr/local/bin/perl
my $line = `ssh $server -o 'BatchMode=yes' -o 'ConnectionAttempts=1' true`;
my $error = `echo $?`;
print "Error = $error";
if($error == 0 )
{
print "This is good";
#df_ret = split /\n/, `ssh -q $server 'df -hP'`;
}
else
{
print "This is bad";
}
Check the result of the ssh exec before splitting it:
$text = `ssh -q $server 'df -hP'` or die "unable to ssh\n";
#df_ret = split /\n/, $text;

How to get response from "ping -c 1 example.com"?

In BASH can I ping a server like so
for i in $MY_SERVER_LIST; do
if ping -c 1 $i > /dev/null 2>&1; then
# $i is alive
fi
done
and I would like to do the same in Perl, but how do I get the response from
my $response = `ping -c 1 google.com > /dev/null 2>&1`
Question
How do I do the same in Perl, but without using any packages like Net::Ping?
You are interested in the exitcode of ping not the output; forget about the $response and examine the exitcode in $?.
I'd use Net::Ping !
use Net::Ping;
$p = Net::Ping->new();
print "$host is alive.\n" if $p->ping($host);
$p->close();
$p = Net::Ping->new("icmp");
$p->bind($my_addr); # Specify source interface of pings
foreach $host (#host_array)
{
print "$host is ";
print "NOT " unless $p->ping($host, 2);
print "reachable.\n";
sleep(1);
}
$p->close();
http://perldoc.perl.org/Net/Ping.html

want to read file line by line and then want to cut the line on delimiter

cat $INPUT_FILE| while read LINE
do
abc=cut -d ',' -f 4 $LINE
Perl:
cat $INPUT_FILE | perl -ne '{my #fields = split /,/; print $fields[3];}'
The key is to use command substitution if you want the output of a command saved in a variable.
POSIX shell (sh):
while read -r LINE
do
abc=$(cut -d ',' -f 4 "$LINE")
done < "$INPUT_FILE"
If you're using a legacy Bourne shell, use backticks instead of the preferred $():
abc=`cut -d ',' -f 4 "$LINE"`
In some shells, you may not need to use an external utility.
Bash, ksh, zsh:
while read -r LINE
do
IFS=, read -r f1 f2 f3 abc remainder <<< "$LINE"
done < "$INPUT_FILE"
or
while read -r LINE
do
IFS=, read -r -a array <<< "$LINE"
abc=${array[3]}
done < "$INPUT_FILE"
or
saveIFS=$IFS
while read -r LINE
do
IFS=,
array=($LINE)
IFS=$saveIFS
abc=${array[3]}
done < "$INPUT_FILE"
Bash:
while read line ; do
cut -d, -f4 <<<"$line"
done < $INPUT_FILE
Straight Perl:
open (INPUT_FILE, "<$INPUT_FILE") or die ("Could not open $INPUT_FILE");
while (<INPUT_FILE>) {
#fields = split(/,/, $_);
$use_this_field_value = $fields[3];
# do something with field value here
}
close (INPUT_FILE);

How can I ping a host with a Perl one-liner with Net::Ping?

Trying to integrate the following Perl one-liner into a shell script. This code works within a Perl script but not as a one-liner executed from a shell script.
I've tried replacing $host with a real hostname with no luck.
#!/bin/ksh
hosts="host1 host2 host3"
PERL=/usr/bin/perl
# Check to see if hosts are accessible.
for host in $hosts
do
#echo $host
$PERL -e 'use Net::Ping; $timeout=5; $p=Net::Ping->new("icmp", $timeout) or die bye ; print "$host is alive \n" if $p->ping($host); $p->close;'
done
The single quotes in the shell stop the $host from being interpreted. So you can just stop and restart the single quotes as required:
perl -MNet::Ping -e 'if (Net::Ping->new("icmp", 5)->ping("'$host'")) {print "'$host' is alive\n"}'
Alternatively, you can pass the host in as a parameter - see the other answer.
Try to replace $host:
$PERL -e 'use Net::Ping; $timeout=5; $p=Net::Ping->new("icmp", $timeout) or die bye ; print "$host is alive \n" if $p->ping($host); $p->close;'
with $ARGV[0], the first command line argument:
$PERL -e 'use Net::Ping; $timeout=5; $p=Net::Ping->new("icmp", $timeout) or die bye ; print "$ARGV[0] is alive \n" if $p->ping($ARGV[0]); $p->close;' $host
If you want to use Perl, then use the Perl interpreter to run your script.
#!/usr/bin/env perl -w
use Net::Ping;
$timeout=5;
$p=Net::Ping->new("icmp", $timeout) or die bye ;
#hosts=qw/localhost 10.10.10.10/;
foreach my $host (#hosts) {
print "$host is alive \n" if $p->ping($host);
}
$p->close;
Otherwise, you might as well use the ping command directly from the shell
#!/bin/bash
for hosts in host1 host2 host3
do
if ping ...... "$hosts" >/dev/null ;then
.....
fi
done