deruytja has asked for the wisdom of the Perl Monks concerning the following question:

Hello folks, at the moment I'm encountering a problem which I don't understand fully. I've written a web based ping script as a project, now i want to integrate a Graph (Via GD::Graph::bars) trough a second script (as I don't want a stored image on the server). The problem is now, I'll always get the error "multipinggraph.pl: Can't use an undefined value as a symbol reference at multiping.pl line 138." from the second script. I think it has something to do with the variable declaration but I've got no idea how to solve the problem.

The first script, which gets called from the html-page is this: multiping.pl
#! /usr/bin/perl use strict; use warnings; use FindBin qw ($Bin); use Net::Ping; use CGI; use CGI::Carp qw(warningsToBrowser fatalsToBrowser); use Time::HiRes; use Time::Piece; our $date = Time::Piece->new->strftime('%Y-%d-%m_%H-%M-%S'); #Aktue +lles Datum open (STDERR, '>/home/deruytja/webserver/rifucgi/logs/'.$date.'multipi +ng_error.log'); #Redirecting Standart Error to file my $cgi = new CGI; my $dateiname = $cgi->param('dateiname'); my ($data, $file, $out_file, $wfname, $wfile, $i, $dur,$age, $key, $typ, $val, $p1, $p2, $p3, $ip, $out, $anzahl, $start, $ende, @label, @dauer, %devices, @check); our (@graphdata); if (!length($dateiname) == 0) { $out_file = ("$dateiname\_$date.csv") or die 'Error processing + file: ',$!; } else { $out_file = ("ping_$date.csv") or die 'Error processing file: +',$!; } my $ping1 = Net::Ping->new("tcp", 1); my $ping2 = Net::Ping->new("tcp", 1); my $ping3 = Net::Ping->new("tcp", 1); $ping1->port_number("80"); $ping2->port_number("5432"); $ping3->port_number("301"); $ping1->hires(); $ping2->hires(); $ping3->hires(); my $outpath = '/home/deruytja/webserver/output/multiping/'; open ($out, '>', $outpath.$out_file) or die 'Error processing file: ', +$!; $out->autoflush(1); &UPLOAD; open (INPUT,"</home/deruytja/webserver/rifucgi/temp_ul/$wfname"); &READIN; close INPUT; $anzahl = keys %devices; $i=0; $start = Time::HiRes::time(); foreach (keys %devices) { ($p1, $dur, $ip) = $ping1->ping($devices{$_}); $typ='SIAE'; if (!$p1 == 1) { ($p2, $dur, $ip) = $ping2->ping($devices{$_}); $typ='Huawei'; if (!$p2 == 1) { ($p3, $dur, $ip) = $ping3->ping($devices{$_}); $typ='Siemens'; } if (!$p3 == 1){$typ='Unknown'} } if ($p1 == 1 or $p2 == 1 or $p3 == 1) { $dur = sprintf("%.2f", 1000 * $dur); push (@label,$_); #LinkID ins erste Array push (@dauer,$dur); #duration ins 2te Array $check[$i] = 'ACTIVE;'.$_.';'.$typ.';'.$devices{$_}.';'.$d +ur.'ms'."\n"; } else { push (@label,$_); #LinkID ins erste Array push (@dauer,'0'); #duration ins 2te Array $check[$i] = 'OFFLINE;'.$_.';'.$typ.';'.$devices{$_}."\n"; }; $ping1->close(); $ping2->close(); $ping3->close(); $i++; } $ende = Time::HiRes::time(); push @check, 'Ausführdauer: '.sprintf("%.2f",($ende-$start)).'s'; print $out 'Ping_result;Link_ID;Device_Type;IP;Ping_time'."\n"; foreach (@check) { print $out "$_"; }; close $out; @graphdata = ([@label],[@dauer]); ########################OUTPUT print $cgi->header, $cgi->start_html('Pingergebnis'), $cgi->h1('Ergebnis des Pings'), $cgi->br(); print "<tr>\n"; print $check[-1]; print "</tr>\n"; print $cgi->br(); print "<img src=/rifucgi/multipinggraph.pl>"; print $cgi->br(), $cgi->br(), $cgi->a({-href => "/output/multiping/$out_file"},'Auswertung') +, $cgi->br(), $cgi->a({-href => '../ping.html'},'Back to Paradise'), $cgi->end_html; ########################/OUTPUT sub READIN { while (<INPUT>) #Schleifenstart zum Einlesen der Werte { for ( "\r\n") {$/="\r\n"} chomp; #Abschneiden des INPUT_RECORD_SEPARATORS ($val, $key) = split /;/; #Teilen der Werte nach $val(ue) und $ +key (BGTR ID) if (exists $devices{$key}) {next;} else {$devices{$key} = $val;} } } sub UPLOAD { $wfile = $cgi->param("toping"); $wfname = 'toping'; open (DAT,">/home/deruytja/webserver/rifucgi/temp_ul/$wfname") or die +'Error processing file INPUT: ',$!; binmode $wfile; + binmode DAT; + while (read $wfile, $data, 1024) + { + print DAT $data + } #Upload Teil des Scripts close DAT; }
the second script, which gets called in the cgi print piece of the code, multipinggraph.pl:
#! /usr/bin/perl #use strict; use warnings; use GD::Graph::bars; require "multiping.pl"; our (@graphdata, $date); my ($mygraph,$graphimage); open (STDERR, '>/home/deruytja/webserver/rifucgi/logs/'.$date.'multipi +nggraph_error.log'); #Redirecting Standart Error to file $mygraph = GD::Graph::bars->new(500, 300); $mygraph->set( x_label => 'Link', y_label => 'Antwortzeit', title => 'Antwortzeiten der Links', ) or warn $mygraph->error; $graphimage = $mygraph->plot(\@graphdata); print "Content-type: image/png\n\n"; binmode STDOUT; print $graphimage->png;
I (think) I know the code isn't perfect, but works well (It's only for a small amount of users). Thanks for the help!

Replies are listed 'Best First'.
Re: Problems calling a second perl script from the first
by kennethk (Abbot) on Apr 13, 2015 at 15:02 UTC
    $wfile = $cgi->param("toping"); $wfname = 'toping'; open (DAT,">/home/deruytja/webserver/rifucgi/temp_ul/$wfname") or die +'Error processing file INPUT: ',$!; binmode $wfile;
    You are getting this error because you are treating an ordinary scalar $wfile like an indirect file handle. What are you trying to do with this? Why can't you just say
    $wfile = $cgi->param("toping"); open (my $dat,">", "/home/deruytja/webserver/rifucgi/temp_ul/$wfname") + or die 'Error processing file DAT: ',$!; print $dat $wfile;
    As a side note, there are a number of things you've done here which are poor practice from a modern web perspective, including 2 argument opens, bareword filehandles, and parroting unescaped text back to the browser. What resources are you using to learn Perl web scripting?

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

      Hello kennethk,

      frankly, I copied the routine for uploading a file (http://aktuell.de.selfhtml.org/artikel/cgiperl/file-upload/, a german HTML "learning" site) as I didn't had any clue how to do it in perl. I think it had something to do with uploading and converting the file.

      If I modifie the code like that:

      $wfile = $cgi->param("toping"); + #Upload Teil des Scripts $wfname = 'toping'; #Upload Teil des Sc +ripts open (my $DAT,">", "/home/deruytja/webserver/rifucgi/temp_ul/$wfname") + or die 'Error processing file INPUT: ',$!; #Upload Teil +des Scripts print $DAT $wfile;

      I'll only get the filename written to my temp file (the "toping" one)

      I've modified the bareword filehandles already (I know barewords are a bad practice and I can't remeber why I did it this way).

      I'm mostly using google and the "trial and error" method for learning. Maybe you could point me to my problems? Especially the open arguments and the unescaped text.

      I'm not a skilled programmer as I've learned more in the direction of hardware and planning

        So $cgi->param("toping") contains the target file name. So that means you need to open the target file and input it. Mimicking the behavior of the original source material, you might do something like:
        sub UPLOAD { $wfile = $cgi->param("toping"); open(my $wfin, '<', $wfile) or die "Error opening $wfile: $!"; $wfname = 'toping'; open (my $datin,">", "/home/deruytja/webserver/rifucgi/temp_ul/$wf +name") or die 'Error processing file INPUT: ',$!; binmode $wfin; binmode $datin; while (read $wfin, my($data), 1024) { print $datin $data } #Upload Teil des Scripts }
        With regard to reading material, there's a fair amount you could learn from Ovid's CGI Course - Resurrected and Updated! about good practice. He discusses taint and other security issues. It's worth noting that people generally don't use CGI directly any more. Specifically, the docs for CGI itself say
        The rationale for this decision is that CGI.pm is no longer considered good practice for developing web applications, including quick prototyping and small web scripts. There are far better, cleaner, quicker, easier, safer, more scalable, more extensible, more modern alternatives available at this point in time.
        Modules like Template::Toolkit (which is rolled into frameworks like Mojolicious, Dancer2 and Catalyst) allow for clean separation between your logic and layout, and can defaultly handle the character escaping.

        #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re: Problems calling a second perl script from the first
by flexvault (Monsignor) on Apr 13, 2015 at 15:01 UTC

    Welcome deruytja,

    I don't have a setup to test your code, but I call Perl from html all the time. I don't use the '<img ...' but use: ( example only )

    <a href="/perl_script?Sid=eddie&... " target="main"><font size="-1"><b +>Reset</b></font></a>

    From your description of your problem, you may need to pass the undefined variables to the 2nd script via the GET/POST method.

    Hopefully someone else has either a setup to test your code or has experience with your use.

    Good Luck...Ed

    "Well done is better than well said." - Benjamin Franklin

Re: Problems calling a second perl script from the first
by deruytja (Novice) on Apr 14, 2015 at 13:43 UTC
    So, I've taken the "short road" to the intended goal. I've just put the graph generating routine in the main script, it works now, via saving the image first on the server and displaying it. It's not a good solution but a working one. By time I will try to migrate these little "script pages" onto another system, like the "Template::Toolkit" or via Mojolicious/Dancer2/Catalyst. "Solution" for my problem:
    my $safe_filename_characters = "a-zA-Z0-9_.-"; sub PLOTTING { my ($GRAPH_FH, $mygraph, $graphimage); open ($GRAPH_FH, '>','/home/deruytja/webserver/output/multiping/'.$gra +phname.'.png'); #Redirecting Standart Error to file $mygraph = GD::Graph::linespoints->new(500, 300); $mygraph->set( x_label => 'Link', y_label => 'Antwortzeit in ms', title => 'Antwortzeiten der Links', dclrs => ['green'], y_max_value => $max, bar_spacing => 10, show_values => 1, ) or warn $mygraph->error; $graphimage = $mygraph->plot(\@graphdata); binmode $GRAPH_FH; print $GRAPH_FH $graphimage->png; close $GRAPH_FH; }

    Also I modified the upload script after encountering problems regarding filenames (If they've got ä/ö/ü in it) like this:

    sub UPLOAD { my $filename = $cgi->param('toping'); if ( !$filename ) { print $cgi->header ( ); print 'There was a problem uploading your file (try a smaller file +).'; exit; } my ( $name, $path, $extension ) = fileparse ( $filename, '..*' ); $filename = $name . $extension; $filename =~ tr/ /_/; $filename =~ s/[^$safe_filename_characters]//g; if ( $filename =~ /^([$safe_filename_characters]+)$/ ) { $filename = $1; } else { print $cgi->header(); print 'There was a problem with the filename (please rename).'; exit; } my $wfile = $cgi->upload('toping'); open(my $DAT,">", "/home/deruytja/webserver/rifucgi/temp_ul/toping.$da +te") or die 'Error processing file INPUT: ',$!; binmode $DAT; while (read $wfile, $data, 1024) { print $DAT $data } close $DAT; }

    The user could also give the output file a name, which would encounter the same problems with the characters, solved via this piece of code:

    if (!length($dateiname) == 0 && $dateiname =~ /^([$safe_filename_chara +cters]+)$/) { $out_file = ("$dateiname\_$date.csv") or die 'Error processing + file: ',$!; $graphname = "$dateiname\_$date"; } else { print $cgi->header(); print 'There was a problem with your file name, it was reverte +d to "ping_" (please change output name).'; $out_file = ("ping_$date.csv") or die 'Error processing file: +',$!; $graphname = "ping_$date"; }