Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Practical Proc::Daemon example

by jellisii2 (Hermit)
on Feb 04, 2014 at 17:51 UTC ( [id://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.

Replies are listed 'Best First'.
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 (Prior) 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.

      Most of the OP code is not necessary with Proc::Daemon either. I see very little difference between the utility, options, and syntactic verbosity of the two packages; only the init stuff seems an exception.

Re: Practical Proc::Daemon example
by lee_crites (Scribe) 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 +] %
Re: Practical Proc::Daemon example
by stevieb (Canon) on Apr 02, 2017 at 23:30 UTC

    This is a bit late, but the parent came up due to other comments, so I thought I'd chime in.

    I tried Proc::Daemon, but it didn't work cross-OS, so I went with Proc::Background instead, which did what I wanted. I must admit that I spent a great deal of time using other distributions to turn things into a proper Windows service while on Windows, and changing up to Proc::Daemon when on Unix, but it was too much work.

    This is a real-life, in use, frequently used example of Proc::Background that works on both Windows and Unix. Here is where I actually fork out.

    At the time I wrote that code, I was testing forking-type code heavily, and I am still looking for the be-all, end-all, but to suit both OS types simultaneously isn't easy, by any stretch.

Re: Practical Proc::Daemon example
by lee_crites (Scribe) 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
      I joined this forum solely to say "Thank You".

      I am not a Perl Monk, I am a complete novice even after too many years. Time and time again I keep coming back here to hear pearls of wisdom.

      This description of how to use Daemon::Control was great and worked first time. It's saved me so much hassle.

      Thank you to everybody for putting so much time and effort into so many clear answers. We are often quick to criticise and carp, less quick to praise and say thanks. This is one small way of correcting that.

      Best wishes,

      Rob.

Re: Practical Proc::Daemon example
by Anonymous Monk on Apr 02, 2017 at 08:20 UTC
    How do I use proc::daemon if Database connection is involved in script. Lets say I want to write a daemon which polls DB inserts and if a new insert happens #dosomething. Should I make a DB connection in while(1) in above example. I believe it will make DB connection in each iteration. Please bless.

      Have you looked at DBI's connect_cached() ? Used with care it can help in a situation like yours.


      The way forward always starts with a minimal test.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
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?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (6)
As of 2024-04-18 09:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found