I have an Angular2 application that has Perl running the back end scripts.
The Perl is sending back an error, and I finally nailed it down to the fact that a "read_ini()" function is failing, so it sends back a simple "Failed to read ini" and it's done. The read_ini() function, as you can probably deduce, locates and gets certain information from a .ini file. Thing is, it's a long function. I'm trying to nail down where in the function it's dying. It would be very useful to be able to print out to the browser JS console to help with this. Is this possible?
In your perl script:
use diagnostics;
use Carp qw(longmess);
eval {
read_ini();
};
if ($#) {
my $long_message = longmess( "error: '$#'");# stack trace about perl error
# send this $long_message as the http response to angular call.
}
in angular controller code:
$http.get("perlscript.pl")
.then(function(response) {
console.log(response.data); # stack trace about perl error should be shown in console log now.
},
function(response) {
#handle error
});
Please try this. Just thrown you some code snippet as I don't know your code.
I ended up finding the answer elsewhere.
At the top of the file I have
open my $savedSTDERR, ">&STDOUT";
then, every print statement just logs to the JS console.
Thanks all for your help.
Related
In this oversimplified script I'm doing a GET request with Net::Async::HTTP using IO::Async::Loop::EV:
use Modern::Perl '2017';
use IO::Async::Loop::EV;
use Net::Async::HTTP;
use Future;
my $loop = IO::Async::Loop::EV->new;
my $http = Net::Async::HTTP->new(max_redirects => 0);
$loop->add($http);
my $f = $http->GET('https://www.perl.org')
->then(sub {
my $response = shift;
printf STDERR "got resp code %d\n", $response->code;
return Future->done;
});
$http->adopt_future($f->else_done);
$loop->run;
I get this warning a couple of times:
EV: error in callback (ignoring): Can't call method "sysread" on an undefined value at .../IO/Async/Stream.pm line 974
I get this warning when using IO::Async::Loop::Event too (again in IO::Async::Stream, at line 974).
For non-secure (http) links, however, all looks good. So something's probably wrong with IO::Async::SSL. (I tried this on different machines, with different OS - still getting those warnings)
Why am I getting this warning multiple times? Does it occur on your machines too?
It seems this warning is specific to the IO::Async::Loop::EV implementation. If you just
use IO::Async::Loop;
my $loop = IO::Async::Loop->new;
then it appears to work just fine. Unless you're picking that for a specific purpose it's best to avoid it and just let the IO::Async::Loop->new magic constructor find a good one.
Additionally, rather than ending the script with
$loop->run
You could instead use
$f->get
so it performs a blocking wait until that Future is complete but then exits cleanly afterwards, so as not to have to <Ctrl-C> it to abort.
I've raised this as a bug against IO::Async::Loop::EV at https://rt.cpan.org/Ticket/Display.html?id=124030
Below is part of the code
use Net::Telnet;
my $session = new Net::Telnet (Timeout => 15,Prompt => '/#$/');
foreach $node (#nodes) {
$session->open("$node") or die ("\n\n\n NOT ACCESSIBLE ");
$session->login('admin', 'admin');
$session->cmd('term len 0');
my #output1=$session->cmd("sh isis neighbor");
print #output1;
}
Puspose of this script: login to list of nodes and print output
however i see one of the node is not reachable from server and this script stops printing output with below output.
"eof read waiting for login prompt: at telnet-test-rtc1.pl line 11 "
My requirement is even if one of the node is not reachable the script should continue excluding that node.
Is it possible ? Please let me know if more clarity required
regards
In the documentation for Net::Telnet, this can be found:
Errors such as timing-out are handled according to the error mode
action. The default action is to print an error message to standard
error and have the program die. See the errmode() method for more
information.
By setting the errormode appropriately, you can prevent the script from dying.
Telnet is rather aged, technology-wise, though. It might be a good idea to look into SSH instead.
Check the perldoc:
Errors such as timing-out are handled according to the error mode action. The default action is to print an error message to standard error and have the program die. See the errmode() method for more information.
Search "errmode" on that page and you will get what you need.
This is my code:
if ($DAEMON) {
my $pid = fork();
if (not defined $pid) {
print "Unable to start daemon.\n";
exit(1);
}
elsif ($pid == 0) {
open STDOUT, '>', '/dev/null';
open STDERR, '>', '/dev/null';
_create_sessions($self, $settings);
$poe_kernel->run;
}
else { print "Script forked to background with PID $pid\n"; }
}
else {
_create_sessions($self, $settings);
$poe_kernel->run;
}
When $DAEMON = 1, it complains that POE::Kernel's run() method was never called, but as you can see in the above code, I did that already. The script works perfectly fine when in daemon mode, but I can't get rid of that warning or understand why it says that. I also tried calling $poe_kernel->has_forked() and that didn't make a difference either.
I'm out of ideas. Any suggestions?
Updated to add: Maybe I wasn't clear enough. The code below creates the session and runs the kernel.
_create_sessions($self, $settings);
$poe_kernel->run;
It works perfectly fine. It's only when the same code is run inside a fork'd child so I can send the script to the background, that it says POE::Kernel's run method wasn't called. The script does go into the background and works like it should which means the kernel is indeed running. I'm only looking to get rid of that annoying warning.
ysth is right. The warning happens because POE::Session instances are created in the parent process but they haven't been given an opportunity to run.
% perl -wle 'use POE; POE::Session->create(inline_states=>{_start => sub {}})'
40023: Sessions were started, but POE::Kernel's run() method was never
40023: called to execute them. This usually happens because an error
40023: occurred before POE::Kernel->run() could be called. Please fix
40023: any errors above this notice, and be sure that POE::Kernel->run()
40023: is called. See documentation for POE::Kernel's run() method for
40023: another way to disable this warning.
In the above example, 40023 is the process ID where the problem was detected.
It's similar to Perl's warning about exiting with active threads:
% perl -wle 'use threads; threads->create(sub { sleep 3600 }); '
Perl exited with active threads:
1 running and unjoined
0 finished and unjoined
0 running and detached
While your code snippet shows sessions are created and run in the child process, I suspect sessions are created before or afterward. The parent process doesn't exit in your snippet, so there's no telling where execution goes afterward?
You should also call POE::Kernel->has_forked() in the child process. I can't tell whether that's happening in your code snippet.
The correct solution is to move all session instantiation into the child process when daemonizing. A passable workaround is to call POE::Kernel->run() just after using POE::Kernel and before any sessions are actually created. run() will return immediately because no sessions exist, but the call satisfies the condition you're being warned about. It's a way of saying "yes, yes, but I know what I'm doing".
From the doc, POE::Kernel's run is normally called as a class method; what is $poe_kernel?
Somewhere, you seem to be starting a session but don't end up calling POE::Kernel->run();
Update: since the message you see is output with warn, and you are throwing away STDERR in the child, I'm guessing it is the parent giving the warning. Something you are doing (in the code you don't show that loads POE and sets $poe_kernel?) is in fact creating a session, apparently unintentionally.
Try to reduce your code to a short, yet runnable example and you will either find the problem yourself or enable others to help you find it.
How do you redirect the output of printf to, for example, a stream or something? I have a gui app that links with a console library. The library makes repeated calls to printf. I need a way to intercept those and have them processed by a function. Also, creating a console is not an option. Im using Windows, btw.
Edit - Also I was hoping not to redirect to a file.
freopen(filename, mode, stdout);
If you want to avoid using a file you can use a named pipe, redirect stdout to it and read from it in a different thread or process.
Some pseudocode with omitted error checking:
HANDLE hPipe = CreateNamedPipe("\\.\pipe\SomePipeName", ...);
int pipeFileDescriptor = _open_osfhandle(hPipe, someFlags);
_dup2(pipeFileDescriptor, _fileno(stdout));
Now what printf writes to stdout should go to the pipe.
In another thread or process you can read from the pipe into a buffer:
HANDLE hPipeClient = CreateFile("\\.\pipe\SomePipeName", ...);
ReadFile(hPipeClient, ...);
I think it will work but I haven't tested it yet.
Some quote to pick from practical mod_perl
"Usually, a single process serves many requests before it exits, so END blocks cannot be used if they are expected to do something at the end of each request's processing."
So, in my a.cgi script :
my $flag = 1;
END {
# Value for $flag is undefined, if this script is run under mod_perl.
# END block code only executed when process to handle a.cgi exit.
# I wish to execute some code, just before process to handle a.cgi exit.
if ($flag) {
# clean up code.
}
}
The book recommences $r->register_cleanup(sub { #cleanup } );
However,
How I can obtain $r in a.cgi script?
Can the subroutine access the my scope flag variable?
Is this $r->register_cleanup shall be placed at a.cgi script? I only want the cleanup code to be executed for a.cgi script. Not the rest.
my $r = Apache->request;
Yes, but see http://modperlbook.org/html/6-2-Exposing-Apache-Registry-Secrets.html and the next couple of pages, regarding scoping of local variables and functions.
Yes, only register the function if you want it to run.
If I understand this correctly, you have a script you want to run both under mod_perl and as a plain CGI and it sounds like you are using Apache::Registry to do this.
You have cleanup code that you want run only when you are running as CGI script.
You need to detect whether or not you are running under mod_perl. That's fairly easy. The simplest way is to check your environment:
unless ($ENV{MOD_PERL})
{
#... cleanup code here.
}
You only to register a cleanup handler if you want something to run when your script terminates under Apache::Registry.
If you do want that, you should place your cleanup code into a sub and call that sub from your check in the CGI:
unless ($ENV{MOD_PERL})
{
cleanup_sub();
}
and from your cleanup handler:
my $r = Apache->request;
$r->register_cleanup(sub { cleanup_sub() } );