Daemonize like fetchmail

P

Payton Swick

Has anyone used the Daemons library (http://daemons.rubyforge.org/)
extensively? I'm trying to do something that doesn't appear covered by
the examples, and I'm wondering if I need to reinvent the wheel.

What I'd like to do is to easily add functionality to my script similar
to the way that fetchmail works, ie:

When starting the script with one command-line option (eg: --daemon), I
daemonize it and start it running (saving the PID in a file).

When starting the script with another command-line option (eg: --stop),
it finds the PID file and kills the instance.

In a sense, I'd like to combine the wrapper method given in the examples
and the script it runs, but the wrapper example seems to absorb
command-line options, and I want to call those functions from within
Ruby instead.

Suggestions?

-Payton
 
A

ara.t.howard

Has anyone used the Daemons library (http://daemons.rubyforge.org/)
extensively? I'm trying to do something that doesn't appear covered by the
examples, and I'm wondering if I need to reinvent the wheel.

What I'd like to do is to easily add functionality to my script similar to
the way that fetchmail works, ie:

When starting the script with one command-line option (eg: --daemon), I
daemonize it and start it running (saving the PID in a file).

When starting the script with another command-line option (eg: --stop), it
finds the PID file and kills the instance.

In a sense, I'd like to combine the wrapper method given in the examples and
the script it runs, but the wrapper example seems to absorb command-line
options, and I want to call those functions from within Ruby instead.

Suggestions?

-Payton

ruby queue and dirwatch both do this.

http://codeforpeople.com/lib/ruby/dirwatch/
http://codeforpeople.com/lib/ruby/rq/

atomic creation of files is trickier than one might imagine. is this on *nix?
is so checkout my lockfile class and posixlock. it's very important to note
that there is ONE atomic creation operator on *nix and it is NOT open or
mkdir - symlink is the one. 'man lockfile' might be illuminating.

cheers.

-a
--
===============================================================================
| ara [dot] t [dot] howard [at] noaa [dot] gov
| all happiness comes from the desire for others to be happy. all misery
| comes from the desire for oneself to be happy.
| -- bodhicaryavatara
===============================================================================
 
A

ara.t.howard

Well, I did it myself anyway. Hope it's useful:
http://rubyforge.org/projects/pidify/

-Payton

it does look useful. few comments/suggestions though

- since it's *nix specify anyhow you might want to use O_EXCL when opening
the file since otherwise there is a race condition to create the file

- on the same page using File#flock with File::LOCK_EX when writing and
File::LOCK_SH for writing and reading (with File::LOCK_NB of course) will
avoid the race conditions and possibibity of reading a half-baked file

- escalation of signals with a pause between is useful for killing processes
since many trap signit and most do not die immediately. something like

def maim(pid, signals=%w(SIGTERM SIGQUIT SIGKILL), suspend=4)
pid = Integer("#{ pid }")
signals = [ signals ].flatten.map{|sig| "#{ sig }"}
suspend = Integer("#{ suspend }")

existed = false
sigs.each do |sig|
begin
Process::kill(sig, pid)
existed = true
rescue Errno::ESRCH
return(existed ? true : nil)
end
return true unless alive?(pid)
sleep suspend
return true unless alive?(pid)
end
return(not alive?(pid))
end

which makes sure the process is actually dead prevents removing the
pidfile in cases where the Process::kill didn't work

- the alive function above can be something like this

def alive pid
pid = Integer("#{ pid }")
begin
Process::kill 0, pid
true
rescue Errno::ESRCH
false
end
end

cheers.

-a
--
===============================================================================
| ara [dot] t [dot] howard [at] noaa [dot] gov
| strong and healthy,
| who thinks of sickness until it strikes like lightning?
| preoccupied with the world,
| who thinks of death, until it arrives like thunder?
| -- milarepa
===============================================================================
 
P

Payton Swick

Would this be correct usage of the file, then?

def pid
dpid = nil
File.open(pid_file, File::RDONLY) { |file| dpid = file.gets.chomp
if file.flock(File::LOCK_SH|File::LOCK_NB); file.flock(File::LOCK_UN) }
if pid_exists?
dpid.to_i
end


def save_pid
File.open(pid_file, File::CREAT|File::EXCL|File::WRONLY) { |file|
file.puts $$ if file.flock(File::LOCK_EX); file.flock(File::LOCK_UN) }
end

-Payton

Well, I did it myself anyway. Hope it's useful:
http://rubyforge.org/projects/pidify/

-Payton


it does look useful. few comments/suggestions though

- since it's *nix specify anyhow you might want to use O_EXCL when
opening
the file since otherwise there is a race condition to create the file

- on the same page using File#flock with File::LOCK_EX when writing and
File::LOCK_SH for writing and reading (with File::LOCK_NB of course)
will
avoid the race conditions and possibibity of reading a half-baked file

- escalation of signals with a pause between is useful for killing
processes
since many trap signit and most do not die immediately. something like

def maim(pid, signals=%w(SIGTERM SIGQUIT SIGKILL), suspend=4)
pid = Integer("#{ pid }")
signals = [ signals ].flatten.map{|sig| "#{ sig }"}
suspend = Integer("#{ suspend }")

existed = false
sigs.each do |sig|
begin
Process::kill(sig, pid)
existed = true
rescue Errno::ESRCH
return(existed ? true : nil)
end
return true unless alive?(pid)
sleep suspend
return true unless alive?(pid)
end
return(not alive?(pid))
end

which makes sure the process is actually dead prevents removing the
pidfile in cases where the Process::kill didn't work

- the alive function above can be something like this

def alive pid
pid = Integer("#{ pid }")
begin
Process::kill 0, pid
true
rescue Errno::ESRCH
false
end
end

cheers.

-a
 
A

ara.t.howard

Would this be correct usage of the file, then?

def pid
dpid = nil
File.open(pid_file, File::RDONLY) { |file| dpid = file.gets.chomp if
file.flock(File::LOCK_SH|File::LOCK_NB); file.flock(File::LOCK_UN) } if
pid_exists?
dpid.to_i
end


def save_pid
File.open(pid_file, File::CREAT|File::EXCL|File::WRONLY) { |file|
file.puts $$ if file.flock(File::LOCK_EX); file.flock(File::LOCK_UN) }
end

more or less. it can still get slippery though. consider:

def pid
dpid = nil
File.open(pid_file, File::RDONLY) { |file| dpid = file.gets.chomp if file.flock(File::LOCK_SH|File::LOCK_NB); file.flock(File::LOCK_UN) } if pid_exists?
dpid.to_i
end


- if the file does not exists this returns nil.to_i, which is 0

- races still exist:

0) process a: reader opens existing pidfile

1) process b: removes pidfile

2) process a: read lock obtained, pid read

this works because:

harp:~ > irb
irb(main):001:0> open('pid','w'){|f| f.write Process::pid}
=> 4
irb(main):002:0> f = open 'pid'
=> #<File:pid>
irb(main):003:0> File::unlink 'pid'
=> 1
irb(main):004:0> File::exists? 'pid'
=> false
irb(main):005:0> f.flock File::LOCK_SH|File::LOCK_NB
=> 0
irb(main):006:0> f.read
=> "4000"

i think this can be solved by only removing the file while holding a
write lock.


def save_pid
File.open(pid_file, File::CREAT|File::EXCL|File::WRONLY) do |file|
file.puts $$ if file.flock(File::LOCK_EX); file.flock(File::LOCK_UN)
end
end

- on some filesystems File::EXCL does not work. on those systems two
process can succeed in opening the file for writing, waiting for the
lock, and then writing the pid with the second clobbering the first.
the open man page details this behaviour. the only solution is to
use an operation which atomically creates a file. the only one i
know of is 'link'. i don't think this is worth dealing with though
since /var/run is probably on a fs for which File::EXCL works...

cheers.

-a
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,201
Messages
2,571,052
Members
47,656
Latest member
rickwatson

Latest Threads

Top