Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Practical Proc::Daemon example

by jellisii2 (Hermit)
on Feb 04, 2014 at 17:51 UTC ( #1073446=perlmeditation: print w/ replies, xml ) Need Help??

I had a lot of trouble getting started with Proc::Daemon because some things weren't completely obvious to me. Here's a practical example that I wrote for practice.

use strict; use warnings; use Getopt::Long; use Proc::Daemon; use Cwd; use File::Spec::Functions; my $pf = catfile(getcwd(), 'pidfile.pid'); my $daemon = Proc::Daemon->new( pid_file => $pf, work_dir => getcwd() ); # are you running? Returns 0 if not. my $pid = $daemon->Status($pf); my $daemonize = 1; GetOptions( 'daemon!' => \$daemonize, "start" => \&run, "status" => \&status, "stop" => \&stop ); sub stop { if ($pid) { print "Stopping pid $pid...\n"; if ($daemon->Kill_Daemon($pf)) { print "Successfully stopped.\n"; } else { print "Could not find $pid. Was it running?\n"; } } else { print "Not running, nothing to stop.\n"; } } sub status { if ($pid) { print "Running with pid $pid.\n"; } else { print "Not running.\n"; } } sub run { if (!$pid) { print "Starting...\n"; if ($daemonize) { # when Init happens, everything under it runs in the child + process. # this is important when dealing with file handles, due to + the fact # Proc::Daemon shuts down all open file handles when Init +happens. # Keep this in mind when laying out your program, particul +arly if # you use filehandles. $daemon->Init; } while (1) { open(my $FH, '>>', catfile(getcwd(), "log.txt")); # any code you want your daemon to run here. # this example writes to a filehandle every 5 +seconds. print $FH "Logging at " . time() . "\n"; close $FH; sleep 5; } } else { print "Already Running with pid $pid\n"; } }

start, stop, and status behave as one would expect. nodaemon runs the daemon in the foreground for troubleshooting. nodaemon must be given before start or it gets ignored.

A little work will need to be done to make it init friendly. This is left as an exercise for the reader.

I'm sure there's better ways to handle the filehandle.

Suggestions are welcome and will be incorporated.

Edit: added catch in stop to not try and stop a daemon that's not running.

Edit: added clarity for what code is actually run when the daemon is running.

Comment on Practical Proc::Daemon example
Select or Download Code
Re: Practical Proc::Daemon example
by Anonymous Monk on Feb 04, 2014 at 22:16 UTC
    Interesting ... could you please note, in the above posting, exactly what was not-obvious to you? It sounds like you ran into, and overcame, some obstacles along the way. Can you please elaborate (outside of the expandable block) just what those were and what you did about it? (Given that your audience hasn't yet run into those rocks, it isn't quite clear from your post just where they are.)

      There is a lack of examples for how to use the module, from my digging. While the documentation is complete, the lack of practical examples made it hard for me to envision how exactly to accomplish what I wished to do. I will freely admit to being spoiled by the great documentation on modules like XML::Twig :D.

      The Init method (still, less so than before, though) has the appearance of voodoo; Timing of its call seems to be very important, particularly if you want to limit the daemon to a single instance, which for my purposes is important as the task that I'm firing is exceptionally cpu intensive and long running. This tripped me up QUITE a few times until I realized I could call Status without having to do an Init. This blindingly bright lightbulb allowed me to get the important stuff (start, stop, status) to behave the way I desired.

      While the terse example on the documentation is valid, it was so far away from my use case (limit to single instance), I couldn't align it with what I wanted to do. The example above was my path to discovery of how to fix my ignorance.

Re: Practical Proc::Daemon example
by Arunbear (Parson) on Feb 05, 2014 at 10:54 UTC
    I like Daemon::Control a lot. If you were using it, none of the above code would be needed (i.e. it takes care of start/stop/status, pid files, init scripts etc). I've even used it to daemonise Java and Python programs.
Re: Practical Proc::Daemon example
by lee_crites (Beadle) on Dec 04, 2014 at 06:26 UTC

    SWEETNESS!!!!!

    Thanks for the code! I had been tinkering about with putting something together for a script which I needed to make a daemon out of. The script worked perfectly, so I did NOT want to pull it apart and split it up and such -- it simply needed to become a background process.

    I got it working; I got it doing the fork, etc. But the "complex" part of checking to see if it is running, starting it, stopping it, etc, etc, was all becoming daunting. Yes, I could keep hacking away at it, and I'd have it done. But why? I just KNEW there had to be a better way!!!

    Thanks to this one example, In less than an hour I replaced code I had spent days working on and testing, and am virtually finished with the project!

    The only problem I have is I can only ++ this one time!

    Lee Crites
    lee@critesclan.com
Re: Practical Proc::Daemon example
by lee_crites (Beadle) on Dec 04, 2014 at 18:22 UTC

    simple additions

    I added these to the example:

    GetOptions( 'daemon!' => \$daemonize, "help" => \&usage, "reload" => \&reload, "restart" => \&restart, "start" => \&run, "status" => \&status, "stop" => \&stop ) or &usage; exit(0); # ================================================== sub usage { my ($opt_name, $opt_value) = @_; print "your usage text goes here...\n"; exit(0); } # ================================================== sub reload { my ($opt_name, $opt_value) = @_; print "reload process not implemented.\n"; } # ================================================== sub restart { my ($opt_name, $opt_value) = @_; &stop; &run; } # ==================================================

    Yes, they are obvious, but since that is about all I did to the example to make it a "complete" test for my system, I figured that I'd toss it back a'cha.

    THANKS!!!

    Lee Crites
    lee@critesclan.com
      It seems as though Proc::Daemon makes you do a lot of extra work. What if you didn't have to re-invent so many wheels? Here's another way:
      #!/usr/bin/env perl # weather_watch.pl use strict; use Getopt::Long; use JSON::XS; use LWP::Simple; use Sys::Syslog; my $app = bless { city => 'London,uk', }; GetOptions( $app, 'city=s', ); $app->init; $app->run; sub init { my ($app) = @_; openlog($0, 'pid', 'user'); syslog("info", "Starting up"); $SIG{TERM} = sub { $app->{should_stop} = 1; }; } sub run { my ($app) = @_; my $url = "http://api.openweathermap.org/data/2.5/weather?q=$app-> +{city}"; until ( $app->{should_stop} ) { if ( my $json = get($url) ) { my $data = decode_json($json); syslog("info", "Temperature is $data->{main}{temp}"); syslog("info", "Wind speed is $data->{wind}{speed}"); } sleep 5; } syslog("info", "Shutting down"); closelog(); }
      The example program is not cluttered with daemonization logic and to run that in non daemon mode, just run it. To add daemonization, create a little script (all off this could be done in one script but for clarity I prefer to keep them separate):
      #!/usr/bin/perl # weathermon use warnings; use strict; use Daemon::Control; use Getopt::Long; GetOptions( \ my %OPT, 'city=s', ); exit Daemon::Control->new( name => "Weather watch daemon", path => '/home/arun/test/weathermon', program => '/home/arun/test/weather_watch.pl', program_args => [ '--city', $OPT{city} ], pid_file => '/tmp/weathermon.pid', )->run;
      Now we get all this for free:
      % ./weathermon status Weather watch daemon [Not Running +] % ./weathermon start Weather watch daemon [Started +] % ./weathermon status Weather watch daemon [Running +] % ./weathermon stop Weather watch daemon [Stopped +] % ./weathermon start -c Miami,us Weather watch daemon [Started +] % ./weathermon restart Weather watch daemon [Stopped +] Weather watch daemon [Started +] %

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://1073446]
Approved by moritz
Front-paged by moritz
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (8)
As of 2014-12-25 16:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (160 votes), past polls