I'm writing a quick script to test the failures and interpreted traffic of a load balancer. I want it to keep trying to make connections after it can't connect to one host or another. My current script doesn't look like it's executing the eval block in the mkcnct sub, and I can't figure out why. Can anyone spot what I'm doing wrong?
#!/usr/bin/perl
use strict;
use Net::HTTP;
use Getopt::Std;
my %opts;
getopts('ht:',\%opts);
my #hostlist ("www.foo.com","www1.foo.com","www2.foo.com");
my $timeout;
if ($opts{t} =~ /\d+/) {
$timeout = $opts{t} + time();
} else {
$timeout = 3600 + time();
}
while ($timeout < time()) {
foreach my $host (#hostlist) {
my $cnct = mkcnct($host);
if ($cnct) { mkreq($cnct) };
}
}
sub mkreq {
my $cnct = shift;
my $time = gettime();
my $out;
$cnct->write_request(GET => "/index.html");
($out->{code},$out->{message},%{$out->{headers}}) = $cnct->read_response_headers;
printf "%s\t%s - Size %d\tLast modified %s\n", $time, $out->{message}, $out->{headers}{'Content-Length'}, $out->{headers}{'Last-Modified'};
$out = "";
$cnct->write_request(GET => "/pki/ca.crl");
($out->{code},$out->{message},%{$out->{headers}}) = $cnct->read_response_headers;
printf "%s\t%s - Size %d\tLast modified %s\n", $time, $out->{message}, $out->{headers}{'Content-Length'}, $out->{headers}{'Last-Modified'};
}
sub mkcnct {
my $host = shift;
my $time = gettime();
my $cnct;
eval{
local $SIG{ALRM} = sub { print "$time\tCannot connect to $host\n"};
alarm(2);
$cnct = Net::HTTP->new(Host => $host);
alarm(0);
};
alarm(0);
return($cnct);
}
sub gettime {
my #time = localtime(time);
my $out;
$out = sprintf "%d\/%d\/%d %d:%d", ($time[4] + 1), $time[3], ($time[5] % 100 ), $time[2], $time[1];
return($out);
}
Try replacing return($cnct); with return $cnct; in mkcnct. You might want to revue the docs on returning scalars and lists
Related
I was using this in the perl script, where it is login to a couple of servers and trying to change the password for the servers through a remote host. But the problem is that the password is not getting changed on both the servers as well as i am not finding a way to check if the new passwords are passed to the servers using expect. i am posting that part of the code where it is checking for the prompt and trying to change the password.
#!/usr/bin/perl
package Session;
use strict;
use warnings;
use Expect;
use IO::Pty;
use Data::Dumper;
use Time::HiRes qw(usleep);
use Switch;
use YAML;
use feature 'say';
my $host1 = $ARGV[0];
my $host2 = $ARGV[1];
my $host1_adapter_name = $ARGV[2];
my $host2_adapter_name = $ARGV[3];
my $exp = Expect->new;
my ($selfObj) = #_;
my $str = "{$host1}\{root} # ";
my $cmdStr; my $result; my $dev_id;
my $timeout = 10;
my $min = 192;
my $range = 32;
my $host1_dev_id = _adapter($host1_adapter_name);
my $host2_dev_id = _adapter($host2_adapter_name);
my #hosts = ("$host1", "$host2");
print ("host2 name is=$host2------");
foreach my $n (#hosts)
{
print ("value of n is $n\n");
if ( $n eq $host1 )
{
_login($n,$host1_dev_id);
}
if ( $n eq $host2)
{
print ("inside 2nd if-----\n");
_login($n,$host2_dev_id);
}
}
sub _login
{
my ($host,$host_dev_id) = #_;
my $exit = 1;
$exp->raw_pty(1);
$exp = Expect->spawn("telnet $host") or die "unable to connect , Please check the connection & retry again: $!\n";
if (!defined($exp))
{
print "Please check the connection & retry again\n";
return -1;
}
`sleep 2 `;
$exp->expect($timeout,
[
qr'[lL]ogin:[\s]?[a]?[s]?[\s]?$',
sub
{
$exp->send("root\r");
`sleep 3 `;
exp_continue;
}
],
[
qr'[pP]assword:[\s]?$',
sub
{
$exp->send("That11NeverWork!\r");
exp_continue;
}
],
[
qr '[#>:][\s]?$',
sub {
$cmdStr = "passwd\r";
$result =_run_cmd($cmdStr);
qr'root\'s New password:\s*';
$exp->send("raym0nd24");
qr'Enter the new password again:\s*';
$exp->send("raym0nd24");
# $exp->send('passwd:\s*',5);
$exit = 0;
exp_continue;
}
],
[
eof =>
sub
{
print("FileName : Session.pm , ERROR: premature EOF in command execution.\n");
}
],
'-re', qr'[#$>:]\s?$', # wait for shell prompt, then exit expect
);
}
#############################################################
#############################################################################
sub _adapter
{
my ($adapter_name) = #_;
print "Adapter name: $adapter_name\n";
chomp($adapter_name);
switch($adapter_name){
case "AUSTIN" {$dev_id="e414571614102004"}
case "CX5" {$dev_id="b315191014103506"}
case "CX4" {$dev_id="b31513101410f704"}
case "CX4_EG10" {$dev_id="b315151014101f06"}
case "CX4_EG25" {$dev_id="b315151014101e06"}
case "CX3" {$dev_id="RoCE"}
case "CX2" {$dev_id="b315506714106104"}
case "CX3_PRO" {$dev_id="RoCE"}
case "CX3_PRO1" {$dev_id="b31507101410e704"}
case "HOUSTON_LR" {$dev_id="df1020e214104004"}
case "HOUSTON_SR" {$dev_id="df1020e214100f04"}
case "HOUSTON_Cu" {$dev_id="df1020e214103d04"}
case "SHINER_S" {$dev_id="e4148a1614109304"}
case "SHINER_T" {$dev_id="e4148e1614109204"}
case "SLATE_SR" {$dev_id="df1020e21410e304"}
case "SLATE_CU" {$dev_id="df1020e21410e404"}
case "EVERGLADES" {$dev_id="b315151014101e06"}
else { print "Adapter not in list\n"}
}
return $dev_id;
}
#######################################################################################
##########################################################
sub _run_cmd
{
my $output; my $output1;
my ($cmdStr) = #_;
$exp->send($cmdStr ."\r");
$exp->expect(21, '-re', $str);
$output = $exp->exp_before();
$exp->clear_accum();
my #PdAt_val = split("\r?\n", $output);
foreach my $line1 (#PdAt_val)
{
chomp($line1);
if ( $line1 =~ /(\(\d+\))(\s*root\s*\#\s*)/)
{
if ( $1 =~ /\((\d+)\)/)
{
if ($1 != 0)
{
print("*************** Command $cmdStr didn't ran sucessfully ***************\n");
exit;
}
}
}
}
return $output;
}
######################################################################
There are individual solutions for different systems. So some systems got from their god the must have highlevel restrictions. So the regular says, that you cant login as root directly. To step up the long way to the stage you can use sudo or su. I didnt see that mind in your lines.
# The simpliest way is to use what you have!
sub passwd
{
my $user = #_[0];
my $password = #_[1];
#
# as root
my $execline = qq~passwd $user:$password~;
#
# as root with second password
my $execline = qq~passwd $user:$password\n$password~;
#
# for microsoft certified ubuntu noobs, kidding mint's
my $execline = qq~sudo $password && passwd $user:$password~;
#
# for apple greyed, debian nerds, solaris freaks
my $execline = qq~su $password && passwd $user:$password~;
#
my $return = system("$execline");
}
print &passwd("root","the magical word");
#
# elseif read this.url([to get the higher experience][1]);
[1]: https://stackoverflow.com/questions/714915/using-the-passwd-command-from-within-a-shell-script
I am trying to emulate synchronous control flow in an asynchronous environment.
The purpose is to support DB requests without callbacks or blocking on request.
I am trying to use the Coro module, but I think I don't understand it in full.
Here are the code snippets:
sub execute {
my ($sth, #vars) = #_;
my $res = $sth->SUPER::execute(#vars);
my $dbh = $sth->{Database};
my $async = new Coro::State;
my $new;
$new = new Coro::State sub {
my $w;
while (!$dbh->pg_ready) {
$w = AnyEvent->io(
fh => $dbh->{pg_socket},
poll => 'r',
cb => sub {
if($dbh->pg_ready) {
$w = undef;
$new->transfer($async);
}
}
) if not $w;
print "run once before statement: $sth->{Statement}\n";
EV::run EV::RUN_ONCE;
}
};
$async->transfer($new);
$res = $dbh->pg_result;
$res;
}
Here is the testing code:
my $cv = AE::cv;
ok(my $dbh = db_connect(), 'connected');
ok(my $sth = $dbh->prepare('select pg_sleep(2)'), 'prepared');
my $start_time = time;
ok($sth->execute(), 'executed');
my $duration = time - $start_time;
ok(($duration > 1 && $duration < 3), 'slept');
is(ref($dbh), 'DBIx::PgCoroAnyEvent::db', 'dbh class');
is(ref($sth), 'DBIx::PgCoroAnyEvent::st', 'sth class');
my $status = 0;
my $finished = 0;
for my $t (1 .. 10) {
$finished += 1 << $t;
}
for my $t (1 .. 10) {
my $timer;
$timer = AE::timer 0.01 + $t/100, 0, sub {
ok(my $dbh = db_connect(), "connected $t");
ok(my $sth = $dbh->prepare('select pg_sleep(' . $t . ')'), "prepared $t");
my $start_time = time;
ok($sth->execute(), "executed $t");
my $duration = time - $start_time;
ok(($duration > $t - 1 && $duration < $t + 1), "slept $t");
print "duration: $t: $duration\n";
$status += 1 << $t;
if ($status == $finished) {
$cv->send;
}
undef $timer;
};
}
$cv->recv;
Full module and test scripts are here DBIx::PgCoroAnyEvent and here 01_sleeps.t
Can someone have a look and explain me what is wrong there?
eval+die is the typical method in Perl.
eval { some_function( #args ); };
if( $# ){
# caught longjmp
}
...
sub some_function {
...
if( some_condition ){
die "throw longjmp"
}
...
}
I can't see any information in the Azure Blob Service Error Code list https://msdn.microsoft.com/en-us/library/dd179439.aspx that relates to error 596.
I am trying to upload some blocks to the Azure service and am getting a response back from the API with code 596 and description 'Broken pipe'.
Has anyone encountered this before ?
(N.B. Yes, I know the code below is not complete yet in that the code as-is does not upload the final chunk)
#!/usr/bin/perl
use 5.014;
use strict;
use warnings;
use autodie;
use Data::Dumper;
use Digest::MD5 qw(md5_base64);
use Crypt::PRNG::Fortuna qw(random_bytes_b64u random_bytes);
use Digest::SHA qw(hmac_sha256_base64);
use Getopt::Long;
use Sys::Syslog qw( :DEFAULT setlogsock);
use File::stat;
use AnyEvent;
use AnyEvent::HTTP;
use Time::Piece;
use Encode qw(decode encode);
use MIME::Base64 qw(encode_base64 decode_base64 encode_base64url);
use FileHandle;
use Fcntl ':flock', 'SEEK_SET';
delete #ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
use sigtrap 'handler' => \&term_handler, 'normal-signals';
####### PARAMS
my $script="upload.pl";
my $maxSingleUpload=1048576; # Maximum size of a single attempt upload (in bytes);
my $multiChunkSize=4194304; # Maximum size of a single block (in bytes)
my $multiLimit=6; # Maximum number of parallel HTTP requests
####### AZURE
my $azureKey="<REMOVED>";
my $azureKeyBin=decode_base64($azureKey);
####### ARGS
my ($vault,$container,$localfile,$remotefile);
my $debug=0;
GetOptions(
"vault|v=s" => \$vault,
"container|c=s" => \$container,
"localfile|l=s" => \$localfile,
"remotefile|r=s" => \$remotefile,
"debug|d+" => \$debug
);
if (!defined $vault || !defined $container || !defined $localfile || !defined $remotefile) {
say "USAGE: -v <vault> -c <container> -l <localfile> -r <remotefile> [-d (debug)]";
exit 1;
}
if (!-e $localfile) {
say "Local file does not exist !";
exit 1;
}
####### Vars
my ($wholeChunks,$chunkRemainder,$runID,$condvar,#offsets,#blocklist);
my $activeCount=0;
my $putBlockURL="https://${vault}.blob.core.windows.net/${container}/${remotefile}?comp=block&blockid=";
####### FUNCTIONS
# Quotient remainder calculator
sub qrem {
use integer;
my( $dividend, $divisor ) = #_;
my $quotient = $dividend / $divisor;
my $remainder = $dividend % $divisor;
my #result = ( $quotient, $remainder );
return #result;
}
# Do pad
sub doPad {
my ($raw) = #_;
while (length($raw) % 4) {
$raw .= '=';
}
return $raw;
}
# Random
sub getRandom {
my ($len) = #_;
#return doPad(random_bytes_b64u($len));
return doPad(encode_base64(random_bytes($len)));
}
# Term handler
sub term_handler {
doLog("err","term_handler: Program terminated early due to user input");
exit 2;
}
# Log sub
sub doLog {
my ($priority,$msg) = #_;
return 0 unless ($priority =~ /info|err|debug/);
setlogsock('unix');
openlog($script, 'pid,cons', 'user');
syslog($priority, $msg);
closelog();
return 1;
}
# Get file size
sub fileSz {
my($file) = #_;
my $stat = stat($file);
return $stat->size;
}
# Get data
sub readData {
my ($file,$length,$offset)=#_;
my $fh = FileHandle->new;
my ($data);
if ($debug) { say "Reading ${file} offset ${offset} for length ${length}";}
#open ($fh,"<",$file);
$fh->open("< $file");
binmode($fh);
seek($fh,$offset,SEEK_SET);
read($fh,$data,$length);
if ($debug) { say "readData read ".byteSize($data);}
#close($fh);
$fh->close;
return $data;
}
# Calc MD5
sub calcMD5 {
my ($data)=#_;
my $hash = md5_base64($data);
return doPad($hash);
}
# Populate offsets
sub populateOffsets {
my ($count,$offsetSize)=#_;
if (!defined $count || !defined $offsetSize) {exit 1;}
my $offset=0;
my #offsets;
for my $i (1..$count) {
push #offsets,$offset;
$offset = $offset + $offsetSize;
}
return #offsets;
}
# Calc auth string
sub azureAuth {
my($t,$signstring)=#_;
if (!defined $signstring) { exit 1;}
if ($debug) {say "String to sign:${signstring}";}
my $auth;
$auth=doPad(hmac_sha256_base64($signstring,$azureKeyBin));
if ($debug) { say "Sig:${auth}";}
return $auth;
}
# Byte size
sub byteSize {
use bytes;
my ($inval)=#_;
return length($inval);
}
# Process
sub doProcess {
return if $activeCount >= $multiLimit;
my $offset = shift #offsets;
return if !defined $offset;
$activeCount++;
if ($debug) { say "Active:${activeCount}, Offset:${offset}";}
$condvar->begin;
my $t = localtime;
my $tNow = $t->strftime();
my $blockid = getRandom(8);
my $subRunID=getRandom(5);
my $contentLength=$multiChunkSize-1;
my $content = readData($localfile,$contentLength,$offset);
my $hash = calcMD5($content);
if ($debug) { say "Block ID:${blockid}, Hash: ${hash}";}
my $url = $putBlockURL.$blockid;
my $canocResource="/${vault}/${container}/${remotefile}\nblockid:${blockid}\ncomp:block";
my $hdrs="x-ms-client-request-id:${runID}\nx-ms-date:${tNow}\nx-ms-version:2009-09-19";
my $byteLength=byteSize(${content});
my $canocHeaders=encode('UTF-8',"PUT\n\n\n${byteLength}\n${hash}\n\n\n\n\n\n\n\n${hdrs}\n${canocResource}",Encode::FB_CROAK);
my $authData=azureAuth($t,$canocHeaders);
if ($debug) {say "Length:${byteLength}";say "Sig: ${authData}"; say "URL:${url}";}
my $azureArr = {
"Authorization"=>"SharedKey ${vault}:${authData}",
"Content-Length"=>${byteLength},
"Content-MD5"=>${hash},
"x-ms-version"=>"2009-09-19",
"x-ms-date"=>${tNow},
"x-ms-client-request-id"=>"${runID}"
};
####### ERROR OCCURS HERE ....
http_request "PUT" => $url,
persistent=>0,
headers=>$azureArr,
body=>$content,
sub {
my ($body, $hdr) = #_;
say Dumper($hdr);
#say "received, Size: ", length $body;
#say $body;
$activeCount--;
$condvar->end;
doProcess();
};
return 1;
}
####### MAIN
$runID=getRandom(5);
doLog("info","${runID} Starting upload for ${localfile} (${remotefile})");
if (fileSz($localfile)<$maxSingleUpload) {
if ($debug) {say "Using single upload method";}
} else {
if ($debug) {say "Using multi-upload method";}
# Calculate chunk quantity
my #chunks = qrem(fileSz($localfile),$multiChunkSize);
$wholeChunks=$chunks[0];
$chunkRemainder=$chunks[1];
if ($debug) {say "Whole chunks (${multiChunkSize}):${wholeChunks}, Remainder:${chunkRemainder}";}
# Init
#offsets=populateOffsets(${wholeChunks},${multiChunkSize});
say Dumper(#offsets);
$condvar = AnyEvent->condvar;
# DO IT
for (1..$multiLimit) {
doProcess();
}
$condvar->recv;
}
doLog("info","${runID} Upload complete");
exit 0;
Error 596 is a client-side error returned by AnyEvent::HTTP. You need to investigate locally to see why you are hitting this error.
See this page for more info:
https://metacpan.org/pod/AnyEvent::HTTP
596 - errors during TLS negotiation, request sending and header processing.
I am working on a very large, very old "historically grown" codebase. In the past, there were often people thinking "Oh, I may need this and that module, so I just include it...", and later, people often "cached" Data inside of modules ("use ThisAndThat" needing a few seconds to load some hundred MB from DB to RAM, yeah, its really a stupid Idea, we are working on that too) and so, often, we have a small module use'ing like 20 or 30 modules, from who 90% are totally unused in the source itself, and, because of "caching" in several use'd submodules, modules tend to take up one minute to load or even more, which is, of course, not acceptable.
So, Im trying to get that done better. Right now, my way is looking through all the modules, understanding them as much as possible and I look at all the modules including them and see whether they are needed or not.
Is there any easier way? I mean: There are functions returning all subs a module has like
...
return grep { defined &{"$module\::$_"} } keys %{"$module\::"}
, so, aint there any simple way to see which ones are exported by default and which ones come from where and are used in the other modules?
A simple example is Data::Dumper, which is included in nearly every file, even, when all debug-warns and prints and so on arent in the script anymore. But still the module has to load Data::Dumper.
Is there any simple way to check that?
Thanks!
The following code could be part of your solution - it will show you which symbols are imported for each instance of use:
package traceuse;
use strict;
use warnings;
use Devel::Symdump;
sub import {
my $class = shift;
my $module = shift;
my $caller = caller();
my $before = Devel::Symdump->new($caller);
my $args = \#_;
# more robust way of emulating use?
eval "package $caller; require $module; $module\->import(\#\$args)";
my $after = Devel::Symdump->new($caller);
my #added;
my #after_subs = $after->functions;
my %before_subs = map { ($_,1) } $before->functions;
for my $k (#after_subs) {
push(#added, $k) unless $before_subs{$k};
}
if (#added) {
warn "using module $module added: ".join(' ', #added)."\n";
} else {
warn "no new symbols from using module $module\n";
}
}
1;
Then just replace "use module ..." with "use traceuse module ...", and you'll get a list of the functions that were imported.
Usage example:
package main;
sub foo { print "debug: foo called with: ".Dumper(\#_)."\n"; }
use traceuse Data::Dumper;
This will output:
using module Data::Dumper added: main::Dumper
i.e. you can tell which functions were imported in robust way. And you can easily extend this to report on imported scalar, array and hash variables - check the docs on Devel::Symdump.
Determine which functions are actually used is the other half of the equation. For that you might be able to get away with a simple grep of your source code - i.e. does Dumper appear in the module's source code that's not on a use line. It depends on what you know about your source code.
Notes:
there may be a module which does what traceuse does - I haven't checked
there might be a better way to emulate "use" from another package
I kind of got of got it to work with PPI. It looks like this:
#!/usr/local/bin/perl
use strict;
use warnings;
use Data::Dumper;
use Term::ANSIColor;
use PPI;
use PPI::Dumper;
my %doneAlready = ();
$" = ", ";
our $maxDepth = 2;
my $showStuffOtherThanUsedOrNot = 0;
parse("/modules/Test.pm", undef, undef, 0);
sub parse {
my $file = shift;
my $indent = shift || 0;
my $caller = shift || $file;
my $depth = shift || 0;
if($depth && $depth >= $maxDepth) {
return;
}
return unless -e $file;
if(exists($doneAlready{$file}) == 1) {
return;
}
$doneAlready{$file} = 1;
my $skript = PPI::Document->new($file);
my #included = ();
eval {
foreach my $x (#{$skript->find("PPI::Statement::Include")}) {
foreach my $y (#{$x->{children}}) {
push #included, $y->{content} if (ref $y eq "PPI::Token::Word" && $y->{content} !~ /^(use|vars|constant|strict|warnings|base|Carp|no)$/);
}
}
};
my %double = ();
print "===== $file".($file ne $caller ? " (Aufgerufen von $caller)" : "")."\n" if $showStuffOtherThanUsedOrNot;
if($showStuffOtherThanUsedOrNot) {
foreach my $modul (#included) {
next unless -e createFileName($modul);
my $is_crap = ((exists($double{$modul})) ? 1 : 0);
print "\t" x $indent;
print color("blink red") if($is_crap);
print $modul;
print color("reset") if($is_crap);
print "\n";
$double{$modul} = 1;
}
}
foreach my $modul (#included) {
next unless -e createFileName($modul);
my $anyUsed = 0;
my $modulDoc = parse(createFileName($modul), $indent + 1, $file, $depth + 1);
if($modulDoc) {
my #exported = getExported($modulDoc);
print "Exported: \n" if(scalar #exported && $showStuffOtherThanUsedOrNot);
foreach (#exported) {
print(("\t" x $indent)."\t");
if(callerUsesIt($_, $file)) {
$anyUsed = 1;
print color("green"), "$_, ", color("reset") if $showStuffOtherThanUsedOrNot;
} else {
print color("red"), "$_, ", color("reset") if $showStuffOtherThanUsedOrNot;
}
print "\n" if $showStuffOtherThanUsedOrNot;
}
print(("\t" x $indent)."\t") if $showStuffOtherThanUsedOrNot;
print "Subs: " if $showStuffOtherThanUsedOrNot;
foreach my $s (findAllSubs($modulDoc)) {
my $isExported = grep($s eq $_, #exported) ? 1 : 0;
my $rot = callerUsesIt($s, $caller, $modul, $isExported) ? 0 : 1;
$anyUsed = 1 unless $rot;
if($showStuffOtherThanUsedOrNot) {
print color("red") if $rot;
print color("green") if !$rot;
print "$s, ";
print color("reset");
}
}
print "\n" if $showStuffOtherThanUsedOrNot;
print color("red"), "=========== $modul wahrscheinlich nicht in Benutzung!!!\n", color("reset") unless $anyUsed;
print color("green"), "=========== $modul in Benutzung!!!\n", color("reset") if $anyUsed;
}
}
return $skript;
}
sub createFileName {
my $file = shift;
$file =~ s#::#/#g;
$file .= ".pm";
$file = "/modules/$file";
return $file;
}
sub getExported {
my $doc = shift;
my #exported = ();
eval {
foreach my $x (#{$doc->find("PPI::Statement")}) {
my $worthATry = 0;
my $isMatch = 0;
foreach my $y (#{$x->{children}}) {
$worthATry = 1 if(ref $y eq "PPI::Token::Symbol");
if($y eq '#EXPORT') {
$isMatch = 1;
} elsif($isMatch && ref($y) ne "PPI::Token::Whitespace" && ref($y) ne "PPI::Token::Operator" && $y->{content} ne ";") {
push #exported, $y->{content};
}
}
}
};
my #realExported = ();
foreach (#exported) {
eval "\#realExported = $_";
}
return #realExported;
}
sub callerUsesIt {
my $subname = shift;
my $caller = shift;
my $namespace = shift || undef;
my $isExported = shift || 0;
$caller = `cat $caller`;
unless($namespace) {
return 1 if($caller =~ /\b$subname\b/);
} else {
$namespace = createPackageName($namespace);
my $regex = qr#$namespace(?:::|->)$subname#;
if($caller =~ $regex) {
return 1;
}
}
return 0;
}
sub findAllSubs {
my $doc = shift;
my #subs = ();
eval {
foreach my $x (#{$doc->find("PPI::Statement::Sub")}) {
my $foundName = 0;
foreach my $y (#{$x->{children}}) {
no warnings;
if($y->{content} ne "sub" && ref($y) eq "PPI::Token::Word") {
push #subs, $y;
}
use warnings;
}
}
};
return #subs;
}
sub createPackageName {
my $name = shift;
$name =~ s#/modules/##g;
$name =~ s/\.pm$//g;
$name =~ s/\//::/g;
return $name;
}
Its really ugly and maybe not 100% working, but it seems, with the tests that Ive done now, that its good for a beginning.
I'm working on an adhoc GUI so that I can easily view a bunch of data from the VMWare Perl SDK without having to setup a bunch of scripts under IIS. The basic idea is to start up the script, and have it fork two processes. One is the HTTP::Daemon web server, and then two seconds later its the Win32::IEAutomation run browser. It's not pretty, I admit, but I'm slightly comfortable with the VMPerlSDK than the VMCOMSDK. Plus I'm kind of curious to see if I can get this to work.
As far as I can tell, the program starts okay. The fork works. The little URI parser works. The only problem is whenever I try to call /quit to shutdown the server, the script explodes.
Any suggestions (aside from how this should be done with IIS and AutoIT, I know, I know) would be appreciated. Thanks!
#!/usr/bin/perl -w
use Data::Dumper;
use HTTP::Daemon;
use HTTP::Status;
use HTTP::Response;
use strict;
use warnings;
use Win32::IEAutomation;
sub MainPage {
return<<eol
<html>
<head><title>Test</title></head>
<body>
<h3>Home</h3>
<p>Quit</p>
</body>
</html>
eol
}
# Parses out web variables
sub WebParse {
my ($wstring) = #_;
my %webs = ();
# gets key/value data
my #pairs = split(/&/, $wstring);
# puts the key name into an array
foreach my $pair (#pairs) {
my ($kname, $kval) = split (/=/, $pair);
$kval =~ tr/+/ /;
$kval =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$webs{$kname} = $kval;
}
return(%webs);
}
sub StartServer {
my $PORT = shift;
my $ALLOWED = shift;
my $d = HTTP::Daemon->new(ReuseAddr => 1, LocalAddr => $ALLOWED, LocalPort => $PORT) || die;
# Prints a startup message
print "Please contact me at: <URL:", $d->url, ">\n";
my $xt = 0;
BLOOP: while (my $c = $d->accept) {
while (my $r = $c->get_request) {
# Handles requests with the GET or POST methods
if ($r->method =~ m/GET/i || $r->method =~ m/POST/i) {
my $uri = $r->uri; my %ENV = ();
$ENV{REQUEST_METHOD} = $r->method;
$ENV{CONTENT_TYPE} = join('; ', $r->content_type);
$ENV{CONTENT_LENGTH} = $r->content_length || '';
$ENV{SCRIPT_NAME} = $uri->path || 1;
$ENV{REMOTE_ADDR} = $c->peerhost();
if ($r->method =~ m/GET/i) {
$ENV{QUERY_STRING} = $uri->query || '';
}
elsif ($r->method =~ m/POST/i) {
$ENV{QUERY_STRING} = $r->{"_content"} || '';
}
my %q = &WebParse($ENV{QUERY_STRING});
my $res = HTTP::Response->new("200");
if ($uri =~ m/quit/i) {
$res->content("Goodbye");
$xt=1;
}
else {
$res->content(MainPage());
}
$c->send_response($res);
}
# Otherwise
else {
$c->send_error("This server only accepts GET or POST methods");
}
if ($xt == 1) {
sleep(2);
$c->force_last_request();
last BLOOP;
}
$c->close;
}
undef($c);
}
$d->close;
undef($d);
exit;
}
sub StartInterface {
my $PORT = shift;
my $ALLOWED = shift;
my $ie = Win32::IEAutomation->new(visible => 1, maximize => 1);
$ie->gotoURL("http://".$ALLOWED.":".$PORT."/");
exit;
}
# Return Status
my $STATUS = 1;
# Server port number
my $PORT = 9005;
# The server that's allowed to talk to this one
my $ALLOWED = "127.0.0.1";
my $pid = fork();
if ($pid == 0) {
StartServer($PORT, $ALLOWED);
} else {
sleep(2);
StartInterface($PORT, $ALLOWED);
}
exit;
before you close your daemon $d, shutdown the socket and tell the parent pid to quit:
$d->shutdown(2);
$d->close;
undef $d;
kill(2,getppid());
exit;