combining multiple greps on to a a perl script - perl

sub logProcessing {
my $logpath = $File::Find::dir;
my $logfile = $_;
if ( $File::Find::name =~ m!$logDir/(.*?)/$pattern! and -d $File::Find::dir ) { $dirCount++ };
if ( $File::Find::name =~ m!$logDir/(.*?)/$pattern! and -f $File::Find::name ) { $fileCount++ };
if ( $File::Find::name =~ m!$logDir/(.*?)/$pattern! and -f $File::Find::name ) {
my $errorsSeen = `grep 'Errno 110' $File::Find::name|uniq`;
if ($errorsSeen ne "") {
$errorCount++;
my $hostname = $1;
my $lastModTime = localtime ( (stat $File::Find::name)[9] );
printf FILE "%3s %20s %50s %30s %50s\n", $hostname, 'Errno 110', $logpath, $logfile, $lastModTime;
}
else {
$nullCount++
}
}
}
The above is a code snippet from a Perl script which actually searches a bunch of log files and does a pattern search and prints a summary and sent it as a mail with attachment.
Wanted to refine the search for 3 patterns and don't want to increase the number of grep statement as the log files are 10k per day and that will increase the runtime of the script which now completes in approx 10 minutes.
As of now it searches 'Errno 110' but wants to add a combination of 'Errno 110', 'Errno 13' & 'Errno 43' and print the summary accordingly and as you see in the printf statement 'Errno 110' is hardcoded.
#!/usr/bin/perl
#
# program revision 1 - addded error filter
# and added error types
## Loading Modules
use strict;
use warnings;
use File::Find;
use Time::Piece;
use MIME::Lite;
use File::Slurp;
use Fcntl qw(:flock);
use Getopt::Long qw(:config no_ignore_case);
## Generic Variables
my $progversion = "1";
my $progrevision = "1";
my $prog_name = "error_alert.pl";
my $help;
my $version;
my $dryrun;
## Main Program Variables
my $dc = 'IN';
chomp($dc);
my $fileDate = localtime->ymd("-");
chomp($fileDate);
my $logDir = "/home/ajoy/alert-dc/testlogs";
my $Date = localtime->ymd("");
my $pattern = "applmgr\.log\.$Date";
chomp($Date);
chomp($pattern);
my $fileCount = 0;
my $dirCount = 0;
my $errorCount = 0;
my $nullCount = 0;
#my $to = 'ajoy.bharath#XXXXXX';
my $to = '123#xyz.com, 345#xyz.com, xyz#abc.com';
my $from = 'no-reply#xyz.com';
my $subject = "Log Alert ($dc)";
my $subDate = localtime->ymd("/");
chomp($subDate);
my $message = "<h2>Log Processing Status of $dc for Date:- $subDate </h2> <br> <h3>Please find the attached file for stats..!</h3>";
my $outFile = "$logDir/applmgr_status_$dc-$fileDate.txt";
## locking multiple instances of this script
open my $lockFile, ">", "$logDir/script.lock" or die $!;
flock $lockFile, LOCK_EX|LOCK_NB or die "Multiple instance not allowed: $!";
print $lockFile "$$";
## Options Sub Routines
sub print_usage() {
print "Usage: $prog_name or $prog_name with options [-v|--version] [-h|--help] [-d|--dryrun]\n";
exit(1);
}
sub print_version() {
print "$prog_name : $progversion.$progrevision\n";
exit(0);
}
sub print_help () {
print "$prog_name : $progversion.$progrevision";
print "\n";
print "Usage: $prog_name or $prog_name with options [-v|--version] [-h|--help] [-d|--dryrun]";
print "\n";
print "$prog_name = process logs and process summary and send mail\n";
print "-v|--version = Version.\n";
print "-h|--help = This screen.\n";
print "-d|--dryrun = process logs and process summary and output to stdout instead of sending mail\n\n";
print "\n";
exit(0);
}
sub dry_run {
open(FILE, ">$logDir/applmgr_status_$dc-$fileDate.txt") or die "Cannot open file";
printf FILE "%3s %20s %30s %40s %50s\n", "Host Name", "Errors", "Log Path", "Log FIle", "Modified Time";
printf FILE "%3s\n", "-" x 150;
find( \&logProcessing, $logDir);
print FILE "\n\nSUMMARY\n";
printf FILE "%3s\n", "-" x 20;
print FILE "Number of Hosts investigated for error: $dirCount\n";
print FILE "Number of LogFiles investigated for error: $fileCount\n";
print FILE "Number of Hosts processed with errors: $errorCount\n";
print FILE "Number of Hosts processed wihout errors: $nullCount\n";
close(FILE);
my $data = read_file($outFile);
print $data;
exit(0);
}
# Main Program starts here
print_usage() if ( ! GetOptions('v|version' => \$version, 'h|help' => \$help, 'd|dryrun' => \$dryrun));
print_help() if ($help);
print_version() if ($version);
dry_run() if ($dryrun);
open(FILE, ">$logDir/applmgr_status_$dc-$fileDate.txt") or die "Cannot open file";
printf FILE "%3s %20s %30s %40s %50s\n", "Host Name", "Errors", "Log Path", "Log FIle", "Modified Time";
printf FILE "%3s\n", "-" x 150;
find( \&logProcessing, $logDir);
print FILE "\n\nSUMMARY\n";
printf FILE "%3s\n", "-" x 20;
print FILE "Number of Podhosts investigated for error: $dirCount\n";
print FILE "Number of LogFiles investigated for error: $fileCount\n";
print FILE "Number of Podhosts harvested with errors: $errorCount\n";
print FILE "Number of Podhosts harvested wihout errors: $nullCount\n";
&alertMessage;
## Main Program Sub Routines
sub logProcessing {
my $logpath = $File::Find::dir;
my $logfile = $_;
if ( $File::Find::name =~m!$logDir/(.*?)/$pattern! and -d $File::Find::dir ) { $dirCount++ };
if ( $File::Find::name =~m!$logDir/(.*?)/$pattern! and -f $File::Find::name ) { $fileCount++ };
if($File::Find::name =~m!$logDir/(.*?)/$pattern! and -f $File::Find::name) {
my $errorsSeen = `grep 'Errno 110' $File::Find::name|uniq`;
if ($errorsSeen ne "") {
$errorCount++;
my $hostname = $1;
my $lastModTime = localtime ( (stat $File::Find::name)[9] );
printf FILE "%3s %20s %50s %30s %50s\n", $hostname, 'Errno 110', $logpath, $logfile, $lastModTime;
} else { $nullCount++ }
}
}
close(FILE);
sub alertMessage {
my $msg = MIME::Lite->new(
From => $from,
To => $to,
Subject => $subject,
Type => 'multipart/mixed'
);
$msg->attach(
Type => 'text/html',
Data => $message
);
$msg->attach(
Type => 'text/html',
Path => $outFile,
Disposition => 'attachment'
);
$msg->send;
}
==========================================================
sample log file - applmgr.log.20170303
2017-03-03 08:35:13 UTC 965 [14385] ERROR upload process failed with: error(110, 'Connection timed out')
error: [Errno 110] Connection timed out
2017-03-03 08:43:43 UTC 913 [20057] ERROR upload process failed with: error(110, 'Connection timed out')
error: [Errno 110] Connection timed out
2017-05-26 08:10:14 UTC 278 [7665] WARNING Failed to check upload result with: Exception('Received error response 400 Bad Request from HTTP server',)
2017-05-26 08:10:14 UTC 288 [7665] ERROR upload process failed with: error(32, 'Broken pipe') error: [Errno 32] Broken pipe
2017-05-26 08:10:14 UTC 278 [7665] WARNING Failed to check upload result with: Exception('Received error response 400 Bad Request from HTTP server',)
2017-05-26 08:10:14 UTC 288 [7665] ERROR upload process failed with: error(32, 'Broken pipe') error: [Errno 32] Broken pipe
2017-05-26 08:10:14 UTC 278 [7665] WARNING Failed to check upload result with: Exception('Received error response 400 Bad Request from HTTP server',)
2017-05-26 08:10:14 UTC 288 [7665] ERROR upload process failed with: error(32, 'Broken pipe') error: [Errno 32] Broken pipe
2017-03-03 08:29:24 UTC 010 [9417] ERROR upload process failed with: error(110, 'Connection timed out')
error: [Errno 110] Connection timed out
Any help in streamlining this for better performance or better logic is much more appreciable. I'm a novice in perl and I appled my logic as it is. My refences were Automating System Administration with perl and questions I've asked and others asked in stack* sites

Related

test for available data in filehandle

For some reason I am implementing some specific network protocol similar to STOMP in plain pure Perl.
The connection can be either a direct network socket, or an SSL tunnel provided by openssl s_client created by a call to open3 (no IO::Socket::SSL available on the host).
Depending on the dialog a request to the server may or may not have a response, or may have multiple responses. How can I test the file descriptors for the existence of data? Currently when no data is available, it waits until the defined timeout.
EDIT: I have probably a vocabulary issue between file handle vs. file descriptor to perform my research. I just found that eof() may help but cannot use it correctly yet.
While it is a bit complicated to provide an SCCCE, here is the interesting parts of the code:
# creation of a direct socket connection
sub connect_direct_socket {
my ($host, $port) = #_;
my $sock = new IO::Socket::INET(PeerAddr => $host,
PeerPort => $port,
Proto => 'tcp') or die "Can't connect to $host:$port\n";
$sock->autoflush(1);
say STDERR "* connected to $host port $port" if $args{verbose} || $args{debug};
return $sock, $sock, undef;
}
# for HTTPS, we are "cheating" by creating a tunnel with OpenSSL in s_client mode
my $tunnel_pid;
sub connect_ssl_tunnel {
my ($dest) = #_;
my ($host, $port);
$host = $dest->{host};
$port = $dest->{port};
my $cmd = "openssl s_client -connect ${host}:${port} -servername ${host} -quiet";# -quiet -verify_quiet -partial_chain';
$tunnel_pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $cmd);
say STDERR "* connected via OpenSSL to $host:$port" if $args{verbose} || $args{debug};
say STDERR "* command = $cmd" if $args{debug};
$SIG{CHLD} = sub {
print STDERR "* REAPER: status $? on ${tunnel_pid}\n" if waitpid($tunnel_pid, 0) > 0 && $args{debug};
};
return *CMD_IN, *CMD_OUT, *CMD_ERR;
}
# later
($OUT, $IN, $ERR) = connect_direct_socket($url->{host}, $url->{port});
# or
($OUT, $IN, $ERR) = connect_ssl_tunnel($url);
# then I am sending with a
print $OUT $request;
# and read the response with
my $selector = IO::Select->new();
$selector->add($IN);
FRAME:
while (my #ready = $selector->can_read($args{'max-wait'} || $def_max_wait)) {
last unless #ready;
foreach my $fh (#ready) {
if (fileno($fh) == fileno($IN)) {
my $buf_size = 1024 * 1024;
my $block = $fh->sysread(my $buf, $buf_size);
if($block){
if ($buf =~ s/^\n*([^\n].*?)\n\n//s){
# process data here
}
if ($buf =~ s/^(.*?)\000\n*//s ){
goto EOR;
# next FRAME;
} }
$selector->remove($fh) if eof($fh);
}
}
}
EOR:
EDIT 2 and epilogue
As a summary, depending in the protocol dialog
a request can have an expected response (for instance a CONNECT must return a CONNECTED)
a request to get the pending messages can return a single response, multiple responses at once (without intermediate request), or no response (and in this case the can_read() with no parameter of Ikegami is blocking, what I want to avoid).
Thanks to Ikegami I have changed my code as the following:
the timeout argument to can_read() is passed as an argument to the sub that is processing the responses
for initial connections I am passing a timeout of several seconds
when I expect instant responses I am passing a timeout of 1 second
in the process loop, after any correct response I replace the initial timeout by a 0.1 to not block if no more data is waiting in the filehandle
Here is my updated code:
sub process_stomp_response {
my $IN = shift;
my $timeout = shift;
my $resp = [];
my $buf; # allocate the buffer once and not in loop - thanks Ikegami!
my $buf_size = 1024 * 1024;
my $selector = IO::Select->new();
$selector->add($IN);
FRAME:
while (1){
my #ready = $selector->can_read($timeout);
last FRAME unless #ready; # empty array = timed-out
foreach my $fh (#ready) {
if (fileno($fh) == fileno($IN)) {
my $bytes = $fh->sysread($buf, $buf_size);
# if bytes undef -> error, if 0 -> eof, else number of read bytes
my %frame;
if (defined $bytes){
if($bytes){
if ($buf =~ s/^\n*([^\n].*?)\n\n//s){
# process frame headers here
# [...]
}
if ($buf =~ s/^(.*?)\000\n*//s ){
# process frame body here
# [...]
push #$resp, \%frame;
$timeout = 0.1; # for next read short timeout
next FRAME;
}
} else {
# EOF
$selector->remove($fh);
last FRAME;
}
} else {
# something is wrong
say STDERR "Error reading STOMP response: $!";
}
} else {
# what? not the given fh
}
}
}
return $resp;
}
Do not use eof in conjunction with select (which can_read wraps). It performs a buffered read, which breaks select.
select will mark a handle as ready for reading when it reaches EOF, and sysread returns zero on EOF. So all you need to do to detect EOF is to check for sysread returning zero.
Note that using a new buffer for every pass was a mistake sysread can easily return only part of a message. The following fixes this, and shows how to handle errors and EOF from sysread.
Globals:
my %clients_by_fd;
When you get a new connection:
$selector->add( $fh );
$clients_by_fd{ fileno( $fh ) } = {
buf => "",
# Any other info you want here.
};
Event loop:
while ( 1 ) {
my #ready = $selector->can_read();
for my $fh ( #ready ) {
my $client = $clients_by_fd{ fileno( $fh ) };
my $buf_ref = \$client->{ buf };
my $rv = sysread( $fh, $$buf_ref, 1024*1024, length( $$buf_ref ) );
if ( !$rv ) {
if ( defined( $rv ) ) {
# EOF
if ( length( $$buf_ref ) ) {
warn( "Error reading: Incomplete message\n" );
}
} else {
# Error
warn( "Error reading: $!\n" );
}
delete $clients_by_fd{ fileno( $fh ) };
$select->remove( $fh );
}
while ( $$buf_ref =~ s/^.*?\n\n//s ) {
process_message( $client, $& );
}
}
}

Device with Telnet/SSH not responding, show error

Strange issue I am running into. I have a few devices with Telnet/SSH issues. When I run my script the results are saying the script was successful. When debug is on I get the follow results..
[ 0.012464] pr finding prompt
[ 0.016593] tr creating Net::Telnet wrapper for telnet
[ 0.017859] tr connecting with: telnet Host 10.xx.xx.xx Port 23
How could I add something to show a error if a promopt is not present or the connection times out?
Thanks
#!/usr/bin/perl
use Net::Appliance::Session;
$file = '1list';
open (FH, "< $file") or die "Can't open $file for read: $!";
my #ios_list = <FH>;
close FH or die "Cannot close $file: $!";
chomp(#ios_list);
my $ios_username = 'xxxx';
my $ios_password = 'xxxx';
DEVICE:
for my $ios_device_ip ( #ios_list ) {
my #version_info;
my $proto = shift;
if (($proto == 43)||($proto == 44)){
$tran = "SSH";
$app="/usr/local/bin/ssh";
}else{
$tran = "Telnet";
$app="/bin/telnet";
}
my $session_obj = Net::Appliance::Session->new(
host => $ios_device_ip,
transport => $tran,
personality => 'ios',
);
#interace
eval {
# try to login to the ios device, ignoring host check
$session_obj->connect(
username => $ios_username,
password => $ios_password,
#SHKC => 0
);
# get our running config
$session_obj->begin_privileged;
$session_obj->cmd('conf t');
$session_obj->cmd('aaa authorization config-commands');
$session_obj->cmd('exit');
$session_obj->end_privileged;
$session_obj->cmd('write memory');
# close down our session
$session_obj->close;
};
#error check
if ($#) {
if ( UNIVERSAL::isa($#, 'Net::Appliance::Session::Exception') ) {
# fault description from Net::Appliance::Session
print "We had an error during our Telnet/SSH session to device : $ios_devi
ce_ip \n";
print $#->message . " \n";
# message from Net::Telnet
print "Net::Telnet message : " . $#->errmsg . "\n";
# last line of output from your appliance
print "Last line of output from device : " . $#->lastline . "\n\n";
}
elsif (UNIVERSAL::isa($#, 'Net::Appliance::Session::Error') ) {
# fault description from Net::Appliance::Session
print "We had an issue during program execution to device : $ios_device_ip
\n";
# print $#->message . " \n";
}
else {
# we had some other error that wasn't a deliberately created exception
print "We had an issue when accessing the device : $ios_device_ip \n";
print "$ios_device_ip The reported error was : $# \n";
}
next DEVICE;
}
print #version_info;
print "$ios_device_ip ok \n";
#end
}
If you're having trouble connecting, it may help to check $# after the eval as there may be an error you're ignoring.
#interace
eval {
# try to login to the ios device, ignoring host check
$session_obj->connect(
username => $ios_username,
password => $ios_password,
#SHKC => 0
);
It's also worth noting that this doesn't do anything:
my $proto = shift;
if (($proto == 43)||($proto == 44)){
$tran = "SSH";
$app="/usr/local/bin/ssh";
}else{
shift is operating on #_ which isn't populated, so $proto will always be undef.

Output Lines are missing

I have written small program for getting output from router. but the starting content of the output is missing in output file.
#!C:\strawberry\perl\bin\perl -w
open ( OUTPUT,"> D:\\Routerbkp\\router\\abc.txt" );
use Control::CLI;
# Create the object instance for SSH
$cli = new Control::CLI('SSH');
# Connect to host - Note that with SSH,
# authentication is part of the connection process
$cli->connect( Host => '10.0.0.1',
Username => 'abc',
Password => 'abc',
PrivateKey => 'C:\Users\Administrator\.ssh\key_10.0.0.1_22.pub',
);
# Send a command and read the resulting output
$output1 = $cli->cmd("terminal length 0");
sleep(1);
$output2 = $cli->cmd("show running-config");
sleep(5);
$output8 = $cli->cmd("show alarm current");
sleep(2);
$cli->disconnect;
print OUTPUT $output1;
print OUTPUT $output2;
print OUTPUT $output8;
If you're having a problem with your code, your first port of call is ALWAYS use strict; and use warnings;.
Then - fix that open statement. Try in the style of:
open ( my $output_fh, ">", "D:\\Routerbkp\\router\\abc.txt" ) or die $!;
You probably also want to trap any errors from $cli -> connect() because there's no guarantee that's worked.
my $result = $cli -> connect ( ...
if ( not $result ) { print "Connect failed: ", $cli -> errormode(), ":", $cli -> errormsg(), "\n"; };

Perl sftp downloads with Net::SFTP::Foreign

Im a beginner. I have written a perl script which does the following
-Create a directory under “/x01/abcd/abc_logs/abcd_Logs” by the current date, in the format of “YYYYMMDD” if it has not already been created.
i.e: if the script is run on “01st of jan 2013”, the directory “20130101” will be created under the said path. So whenever there is a need to inspect the logs always look for a directory by the current date.
-Check if the log file(s) have already been downloaded earlier within the same day, and if not log(s) will be downloaded to the TODAY’s directory.
Im having a hard time, coming up with a solution to print a message when there are no files in the share. This is of course when the user specify 2 or more files that are not there in the share. I know that this happens because there is a "die" statement in the "sub get_LOGS". I just cannot seem to understand how to return a message when all the files I specify do not happen to be in the share.
usage of this script is as follows
./abc_logs ....<file(n)>
following is the script.
my $LOGS_LOCAL_PATH = "/x02/abc/abcba2/";
chomp $LOGS_LOCAL_PATH;
my $LOGS_REM_PATH = "/x01/INT/abc/vabc2/";
chomp $LOGS_REM_PATH;
my $TODAY = `date +%Y%m%d`;
chomp $TODAY;
my #GETLOOP = #ARGV;
unless ($#ARGV >= 0) {
print "\nUsage: gtp_logs.pl <file1> <file2> <file3>.....<file(n)>\n\n";
exit;
}
system("clear");
unless ( -d "$LOGS_LOCAL_PATH"."$TODAY") {
print "Directory \"$TODAY\" doesn't exist. So creating the directory..!\n";
print "OK..Done.....!\n\n";
system("mkdir $LOGS_LOCAL_PATH/$TODAY");
}
else {
print "Directory already exists. Logs will be downloaded to ==> \"$LOGS_LOCAL_PATH$TODAY\".....!\n\n";
}
# if_DOWNLOADED($LOGS_LOCAL_PATH,$TODAY,#GETLOOP);
chdir("$LOGS_LOCAL_PATH"."$TODAY") || die "cannot cd to ($!)";
foreach my $GETL (#GETLOOP) {
my $is_downloaded = if_DOWNLOADED($LOGS_LOCAL_PATH,$TODAY,$GETL);
if(!$is_downloaded)
{
get_LOGS("172.25.70.221","abc","abc2","/x01/INT/abc",$GETL);
print "File \"$GETL\" downloaded to ==> \"$LOGS_LOCAL_PATH$TODAY\"\n\n";
}
else
{
print "File \"$GETL\" has already been Downloaded to ==> \"$LOGS_LOCAL_PATH$TODAY\"\n\n";
}
}
sub get_LOGS {
my $LOG_HOST = shift;
my $REM_USER = shift;
my $REM_PASSW = shift;
my $REM_PATH = shift;
my $REM_FILE = shift;
print "Connecting to the sftp share! Please wait....!\n";
my $sftp = Net::SFTP::Foreign->new($LOG_HOST, user => $REM_USER, password => $REM_PASSW);
$sftp->setcwd($REM_PATH) or die "unable to change cwd: " . $sftp->error;
print "OK. On the share! Downloading the file \"$REM_FILE\"...................!\n\n\n\n";
$sftp->error and die "Problem connecting to the share...!!!! " . $sftp->error;
$sftp->get($REM_FILE) or die "File does not seem to be present on the remote share. Please re-request..!!!" . $sftp->error;
return $REM_FILE;
}
sub if_DOWNLOADED {
my $DWD_FILE_PATH = shift;
my $DWD_DIR = shift;
my $DWD_FILE = shift;
if (-e "$DWD_FILE_PATH/$DWD_DIR/$DWD_FILE")
{
return 1;
}
else
{
return 0;
}
}
Please can someone help me finding a solution to this matter? Please try to use the same script and modify.
/V
Some comments to your code:
Use strict and warnings in order to catch lots of errors early.
Read some book on style (i.e. Damian Conway's Perl Best Practices). But in any case try to be consistent when naming variables, subroutines, and everything and also with their case.
When you have to use some calculated value in several places, try to calculate it once and save it in a variable.
Don't use subroutines for trivial things.
You don't need to call chomp on variables you have defined and that don't have a "\n" character at the end.
Opening a new SFTP connection for every file transfer is very inefficient. You can open just one at the beginning and use it for all the transfers.
And now, a simplified version of your script:
#!/usr/bin/perl
use strict;
use warnings;
my $host = "172.25.70.221";
my $user = "abc";
my $password = "abc1234321";
my $LOGS_LOCAL_PATH = "/x02/ABC/abc2";
my $LOGS_REM_PATH = "/x01/INT/abc/vim";
my $TODAY = `date +%Y%m%d`;
chomp $TODAY;
my $TODAY_LOCAL_PATH = "$LOGS_LOCAL_PATH/$TODAY";
my #files = #ARGV;
#files or die "\nUsage: gtp_logs.pl <file1> <file2> <file3>.....<file(n)>\n\n";
system("clear");
if ( -d $TODAY_LOCAL_PATH) {
print "Directory already exists. Logs will be downloaded to ==> \"$TODAY_LOCAL_PATH\".....!\n\n";
}
else {
print "Directory \"$TODAY\" doesn't exist. So creating the directory..!\n";
mkdir "$TODAY_LOCAL_PATH" or die "unable to create directory: $!\n";
print "OK..Done.....!\n\n";
}
chdir $TODAY_LOCAL_PATH or die "cannot cd to ($!)\n";
my $sftp = Net::SFTP::Foreign->new($host, user => $user, password => $password);
$sftp->error
and die "Problem connecting to the share...!!!! " . $sftp->error;
my $ok = 0;
my $failed = 0;
foreach my $file (#files) {
if (-e "$TODAY_LOCAL_PATH/$file") {
print "File \"$file\" has already been Downloaded to ==> \"$TODAY_LOCAL_PATH\"\n";
}
else {
if ($sftp->get("$LOGS_REM_PATH/$file")) {
print "File \"$file\" downloaded to ==> \"$TODAY_LOCAL_PATH\"\n";
$ok++;
}
else {
print "Unable to download file \"$file\" : " . $sftp->error . "\n";
$failed++;
}
}
}
print "$ok files have been downloaded, $failed files failed!\n\n";

Can't read from socket in perl - possible deadlock?

My OS is Archlinux with perl 5.14.2. I am just trying to write a little program to accomplish a remote comlile. The program just passes a C source file to the server. The server will call gcc to compile the C code and pass the compiler's message. The client can't receive the compiler's message. I have the message in the server.
There is the code:
#!/usr/bin/perl -w
# oj.pl --- alpha
use warnings;
use strict;
use IO::File;
use IO::Socket;
use constant MY_TRAN_PORT => 138000;
$| = 1;
my $tmpFileToBeCompiled = IO::File->new ("> tmpFile09090989.c") or die "Can't creat this file";
#if (defined $tmpFileToBeCompiled) {
# print $tmpFileToBeCompiled "argh"; # just for test!
#}
# $fihi->close;
my $port = shift || MY_TRAN_PORT;
my $sock_server = IO::Socket::INET->new (Listen => 20,
LocalPort => $port,
Timeout => 60,
Reuse => 1)
or die "Can't create listening socket: $!\n";
my $tmp = 1;
while ($tmp) {
next unless my $session = $sock_server->accept;
my $peer = gethostbyaddr ($session->peeraddr, AF_INET)
|| $session->peerhost;
warn "Connection from [$peer, $port]\n";
while (<$session>) {
print $tmpFileToBeCompiled $_; # if it works, the filehandle should be changed into tmpFile. just fixed.
print $session "test!";
}
my #lines = `gcc tmpFile09090989.c 2>&1`;
foreach ( #lines) {
print $session $_ . "test!!!\n";
# $session->print;
}
print "OK!";
$tmpFileToBeCompiled->close;
warn "Connecting finished!\n";
$session->close;
$tmp --;
}
$sock_server->close;
----------------------------------------end--------------------------------------------------------
-------------------------------------client.pl--------------------------------------------------------
use warnings;
use strict;
use IO::Socket qw(:DEFAULT);
use File::Copy;
use constant MY_TRAN_PORT => 138000;
use IO::File;
my $host = shift || '127.0.0.1';
my $port = shift || MY_TRAN_PORT;
my $socket = IO::Socket::INET->new("$host:$port") or die $#;
my $fh = IO::File->new("a.c", "r");
my $child = fork();
die "Can't fork: $!\n" unless defined $child;
# if (!$child) {
# $SIG{CHLD} = sub { exit 0 };
# userToHost();
# print "Run userToHost done!\n";
# $socket->shutdown(1);
# sleep;
# } else {
# hostToUser();
# print "Run hostToUser done! \n";
# warn "Connection closed by foreign host\n";
# }
userToHost();
unless ($child) {
hostToUser();
print "Run hostToUser done! \n";
warn "Connection closed by foreign host\n";
$socket->close;
}
sub userToHost {
while (<$fh>) {
# print $_; # for debug
print $socket $_;
}
}
sub hostToUser {
while (<$socket >) {
print $_;
}
}
# copy ("a.c", $socket) or die "Copy failed: $!";
print "Done!";
You don't need to fork in client. At all. Just like themel said
You have error in client code: <$socket > should be <$socket>
You need to notify server that you have written all data and server can start compilation. Otherwise server will stuck at while (<$session>) forever.
To achieve this you could call shutdown($socket, 1) which means you finished writing. See perldoc -f shutdown
Final prototype (very rough) could look like this: https://gist.github.com/19b589b8fc8072e3cfff
yko nailed it, but let me just suggest that your task will be solved in a much easier and more maintainable way by a shell script running from inetd.