daemon_controller 0.2.6 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE.txt +1 -1
- data/README.markdown +37 -9
- data/daemon_controller.gemspec +3 -3
- data/lib/daemon_controller/spawn.rb +1 -1
- data/lib/daemon_controller/version.rb +4 -4
- data/lib/daemon_controller.rb +57 -8
- data/spec/daemon_controller_spec.rb +24 -0
- metadata +16 -6
data/LICENSE.txt
CHANGED
data/README.markdown
CHANGED
@@ -31,10 +31,9 @@ It provides the following functionality:
|
|
31
31
|
|
32
32
|
## Resources
|
33
33
|
|
34
|
-
* [Website](
|
34
|
+
* [Website](https://github.com/FooBarWidget/daemon_controller)
|
35
35
|
* [RDoc](http://rdoc.info/projects/FooBarWidget/daemon_controller)
|
36
36
|
* [Git repository](git://github.com/FooBarWidget/daemon_controller.git)
|
37
|
-
* [RubyForge project](http://rubyforge.org/projects/daemoncontrol/)
|
38
37
|
|
39
38
|
|
40
39
|
What is it for?
|
@@ -317,7 +316,7 @@ That can be done with the following code:
|
|
317
316
|
controller = DaemonController.new(
|
318
317
|
:identifier => 'Apache web server',
|
319
318
|
:start_command => 'apachectl -f apache.conf -k start',
|
320
|
-
:ping_command =>
|
319
|
+
:ping_command => [:tcp, 'localhost', 1234],
|
321
320
|
:pid_file => 'apache.pid',
|
322
321
|
:log_file => 'apache.log',
|
323
322
|
:start_timeout => 25
|
@@ -401,7 +400,7 @@ This can be achieved with the following code:
|
|
401
400
|
:identifier => 'Sphinx search server',
|
402
401
|
:start_command => "searchd -c config/sphinx.conf",
|
403
402
|
:before_start => method(:before_start),
|
404
|
-
:ping_command =>
|
403
|
+
:ping_command => [:tcp, 'localhost', SEARCH_SERVER_PORT],
|
405
404
|
:pid_file => 'tmp/pids/sphinx.pid',
|
406
405
|
:log_file => 'log/sphinx.log')
|
407
406
|
end
|
@@ -458,11 +457,40 @@ when the daemon is designed from the beginning with such abilities in mind, but
|
|
458
457
|
it's compatible with virtually all daemons, and is easy to use.
|
459
458
|
|
460
459
|
|
461
|
-
Concurrency notes
|
462
|
-
|
463
|
-
DaemonController
|
464
|
-
|
465
|
-
|
460
|
+
Concurrency and compatibility notes
|
461
|
+
===================================
|
462
|
+
DaemonController uses a lock file and the Ruby `File#flock` API to guarantee
|
463
|
+
synchronization. This has a few implications:
|
464
|
+
|
465
|
+
* On most Ruby implementations, including MRI, `File#flock` is implemented
|
466
|
+
with the POSIX `flock()` system call or the Windows file locking APIs.
|
467
|
+
This kind of file locking works pretty much the way we expect it would.
|
468
|
+
Multiple threads can safely use daemon_controller concurrently. Multiple
|
469
|
+
processes can safely use daemon_controller concurrently. There will be no
|
470
|
+
race conditions.
|
471
|
+
|
472
|
+
However `flock()` is not implemented on Solaris. daemon_controller, if
|
473
|
+
used in MRI does not currently work on Solaris. You need to use JRuby
|
474
|
+
which does not use `flock()` to implement `File#flock`.
|
475
|
+
|
476
|
+
* On JRuby `File#flock` is implemented through the Java file locking API,
|
477
|
+
which on Unix is implemented with the `fcntl()` system calls. This is a
|
478
|
+
different kind of lock with very strange semantics.
|
479
|
+
|
480
|
+
* If *any* process/thread closes the lock file, then the lock on that file
|
481
|
+
will be removed even if that process/thread never requested a lock.
|
482
|
+
* Fcntl locks are usually implemented indepedently from `flock()` locks so
|
483
|
+
if a file is already locked with `flock()` then `fcntl()` will not block
|
484
|
+
when.
|
485
|
+
* The JVM's file locking API only allows inter-process synchronization. It
|
486
|
+
cannot be used to synchronize threads. If a thread has obtained a file
|
487
|
+
lock, then another thread within the same JVM process will not block upon
|
488
|
+
trying to lock the same file.
|
489
|
+
|
490
|
+
In other words, if you're on JRuby then don't concurrently access
|
491
|
+
daemon_controller from multiple threads without manual locking. Also be
|
492
|
+
careful with mixing MRI processes that use daemon_controller with JRuby
|
493
|
+
processes that use daemon_controller.
|
466
494
|
|
467
495
|
|
468
496
|
API documentation
|
data/daemon_controller.gemspec
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "daemon_controller"
|
3
3
|
# Don't forget to update version.rb too.
|
4
|
-
s.version = "0.
|
5
|
-
s.date = "
|
4
|
+
s.version = "1.0.0"
|
5
|
+
s.date = "2012-02-04"
|
6
6
|
s.summary = "A library for implementing daemon management capabilities"
|
7
7
|
s.email = "hongli@phusion.nl"
|
8
|
-
s.homepage = "
|
8
|
+
s.homepage = "https://github.com/FooBarWidget/daemon_controller"
|
9
9
|
s.description = "A library for robust daemon management."
|
10
10
|
s.has_rdoc = true
|
11
11
|
s.authors = ["Hongli Lai"]
|
@@ -19,6 +19,6 @@
|
|
19
19
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
20
|
# THE SOFTWARE.
|
21
21
|
|
22
|
-
# Used on Ruby 1.9 because forking
|
22
|
+
# Used on Ruby 1.9 because forking may not be safe/supported on all platforms.
|
23
23
|
Process.setsid
|
24
24
|
Process.spawn(ARGV[0])
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# daemon_controller, library for robust daemon management
|
2
|
-
# Copyright (c) 2010 Phusion
|
2
|
+
# Copyright (c) 2010, 2011, 2012 Phusion
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
@@ -20,8 +20,8 @@
|
|
20
20
|
# THE SOFTWARE.
|
21
21
|
|
22
22
|
class DaemonController
|
23
|
-
MAJOR =
|
24
|
-
MINOR =
|
25
|
-
TINY =
|
23
|
+
MAJOR = 1
|
24
|
+
MINOR = 0
|
25
|
+
TINY = 0
|
26
26
|
VERSION_STRING = "#{MAJOR}.#{MINOR}.#{TINY}"
|
27
27
|
end # class DaemonController
|
data/lib/daemon_controller.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# daemon_controller, library for robust daemon management
|
2
|
-
# Copyright (c) 2010 Phusion
|
2
|
+
# Copyright (c) 2010, 2011, 2012 Phusion
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
@@ -85,14 +85,19 @@ class DaemonController
|
|
85
85
|
# The value may be a command string. This command must exit with an exit code of
|
86
86
|
# 0 if the daemon can be successfully connected to, or exit with a non-0 exit
|
87
87
|
# code on failure.
|
88
|
+
#
|
89
|
+
# The value may also be an Array which specifies the socket address of the daemon.
|
90
|
+
# It must be in one of the following forms:
|
91
|
+
# - [:tcp, host_name, port]
|
92
|
+
# - [:unix, filename]
|
88
93
|
#
|
89
94
|
# The value may also be a Proc, which returns an expression that evaluates to
|
90
95
|
# true (indicating that the daemon can be connected to) or false (failure).
|
91
96
|
# If the Proc raises Errno::ECONNREFUSED, Errno::ENETUNREACH, Errno::ETIMEDOUT
|
92
|
-
#
|
97
|
+
# Errno::ECONNRESET, Errno::EINVAL or Errno::EADDRNOTAVAIL then that also
|
93
98
|
# means that the daemon cannot be connected to.
|
94
99
|
# <b>NOTE:</b> if the ping command returns an object which responds to
|
95
|
-
# <tt>#close</tt>, then that method will be called on
|
100
|
+
# <tt>#close</tt>, then that method will be called on it.
|
96
101
|
# This makes it possible to specify a ping command such as
|
97
102
|
# <tt>lambda { TCPSocket.new('localhost', 1234) }</tt>, without having to worry
|
98
103
|
# about closing it afterwards.
|
@@ -101,6 +106,10 @@ class DaemonController
|
|
101
106
|
# [:pid_file]
|
102
107
|
# The PID file that the daemon will write to. Used to check whether the daemon
|
103
108
|
# is running.
|
109
|
+
#
|
110
|
+
# [:lock_file]
|
111
|
+
# The lock file to use for serializing concurrent daemon management operations.
|
112
|
+
# Defaults to "(filename of PID file).lock".
|
104
113
|
#
|
105
114
|
# [:log_file]
|
106
115
|
# The log file that the daemon will write to. It will be consulted to see
|
@@ -183,7 +192,7 @@ class DaemonController
|
|
183
192
|
@log_file_activity_timeout = options[:log_file_activity_timeout] || 7
|
184
193
|
@daemonize_for_me = options[:daemonize_for_me]
|
185
194
|
@keep_ios = options[:keep_ios] || []
|
186
|
-
@lock_file = determine_lock_file(@identifier, @pid_file)
|
195
|
+
@lock_file = determine_lock_file(options, @identifier, @pid_file)
|
187
196
|
end
|
188
197
|
|
189
198
|
# Start the daemon and wait until it can be pinged.
|
@@ -538,8 +547,12 @@ private
|
|
538
547
|
return nil
|
539
548
|
end
|
540
549
|
|
541
|
-
def determine_lock_file(identifier, pid_file)
|
542
|
-
|
550
|
+
def determine_lock_file(options, identifier, pid_file)
|
551
|
+
if options[:lock_file]
|
552
|
+
return LockFile.new(File.expand_path(options[:lock_file]))
|
553
|
+
else
|
554
|
+
return LockFile.new(File.expand_path(pid_file + ".lock"))
|
555
|
+
end
|
543
556
|
end
|
544
557
|
|
545
558
|
def self.fork_supported?
|
@@ -650,11 +663,46 @@ private
|
|
650
663
|
rescue *ALLOWED_CONNECT_EXCEPTIONS
|
651
664
|
return false
|
652
665
|
end
|
666
|
+
elsif @ping_command.is_a?(Array)
|
667
|
+
type, *args = @ping_command
|
668
|
+
|
669
|
+
case type
|
670
|
+
when :tcp
|
671
|
+
socket_domain = Socket::Constants::AF_INET
|
672
|
+
hostname, port = args
|
673
|
+
sockaddr = Socket.pack_sockaddr_in(port, hostname)
|
674
|
+
when :unix
|
675
|
+
socket_domain = Socket::Constants::AF_LOCAL
|
676
|
+
sockaddr = Socket.pack_sockaddr_un(args[0])
|
677
|
+
else
|
678
|
+
raise ArgumentError, "Unknown ping command type #{type.inspect}"
|
679
|
+
end
|
680
|
+
|
681
|
+
begin
|
682
|
+
socket = Socket.new(socket_domain, Socket::Constants::SOCK_STREAM, 0)
|
683
|
+
begin
|
684
|
+
socket.connect_nonblock(sockaddr)
|
685
|
+
rescue Errno::ENOENT, Errno::EINPROGRESS, Errno::EAGAIN, Errno::EWOULDBLOCK
|
686
|
+
if select(nil, [socket], nil, 0.1)
|
687
|
+
begin
|
688
|
+
socket.connect_nonblock(sockaddr)
|
689
|
+
rescue Errno::EISCONN
|
690
|
+
end
|
691
|
+
else
|
692
|
+
raise Errno::ECONNREFUSED
|
693
|
+
end
|
694
|
+
end
|
695
|
+
return true
|
696
|
+
rescue Errno::ECONNREFUSED, Errno::ENOENT
|
697
|
+
return false
|
698
|
+
ensure
|
699
|
+
socket.close if socket
|
700
|
+
end
|
653
701
|
else
|
654
702
|
return system(@ping_command)
|
655
703
|
end
|
656
704
|
end
|
657
|
-
|
705
|
+
|
658
706
|
def safe_fork(double_fork)
|
659
707
|
pid = fork
|
660
708
|
if pid.nil?
|
@@ -674,8 +722,9 @@ private
|
|
674
722
|
"\tfrom " << e.backtrace.join("\n\tfrom ")
|
675
723
|
STDERR.write(e)
|
676
724
|
STDERR.flush
|
677
|
-
ensure
|
678
725
|
exit!
|
726
|
+
ensure
|
727
|
+
exit!(0)
|
679
728
|
end
|
680
729
|
else
|
681
730
|
if double_fork
|
@@ -361,4 +361,28 @@ describe DaemonController do
|
|
361
361
|
server.close
|
362
362
|
end
|
363
363
|
end
|
364
|
+
|
365
|
+
specify "the ping command may be [:tcp, hostname, port]" do
|
366
|
+
new_controller(:ping_command => [:tcp, "localhost", 8278])
|
367
|
+
@controller.send(:run_ping_command).should be_false
|
368
|
+
|
369
|
+
server = TCPServer.new('localhost', 8278)
|
370
|
+
begin
|
371
|
+
@controller.send(:run_ping_command).should be_true
|
372
|
+
ensure
|
373
|
+
server.close
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
specify "the ping command may be [:unix, filename]" do
|
378
|
+
new_controller(:ping_command => [:unix, "spec/foo.sock"])
|
379
|
+
@controller.send(:run_ping_command).should be_false
|
380
|
+
|
381
|
+
server = UNIXServer.new('spec/foo.sock')
|
382
|
+
begin
|
383
|
+
@controller.send(:run_ping_command).should be_true
|
384
|
+
ensure
|
385
|
+
server.close
|
386
|
+
end
|
387
|
+
end
|
364
388
|
end
|
metadata
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: daemon_controller
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
4
5
|
prerelease:
|
5
|
-
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
6
11
|
platform: ruby
|
7
12
|
authors:
|
8
13
|
- Hongli Lai
|
@@ -10,8 +15,7 @@ autorequire:
|
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
17
|
|
13
|
-
date:
|
14
|
-
default_executable:
|
18
|
+
date: 2012-02-04 00:00:00 Z
|
15
19
|
dependencies: []
|
16
20
|
|
17
21
|
description: A library for robust daemon management.
|
@@ -34,8 +38,7 @@ files:
|
|
34
38
|
- spec/daemon_controller_spec.rb
|
35
39
|
- spec/echo_server.rb
|
36
40
|
- spec/unresponsive_daemon.rb
|
37
|
-
|
38
|
-
homepage: http://github.com/FooBarWidget/daemon_controller/tree/master
|
41
|
+
homepage: https://github.com/FooBarWidget/daemon_controller
|
39
42
|
licenses: []
|
40
43
|
|
41
44
|
post_install_message:
|
@@ -48,19 +51,26 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
48
51
|
requirements:
|
49
52
|
- - ">="
|
50
53
|
- !ruby/object:Gem::Version
|
54
|
+
hash: 3
|
55
|
+
segments:
|
56
|
+
- 0
|
51
57
|
version: "0"
|
52
58
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
59
|
none: false
|
54
60
|
requirements:
|
55
61
|
- - ">="
|
56
62
|
- !ruby/object:Gem::Version
|
63
|
+
hash: 3
|
64
|
+
segments:
|
65
|
+
- 0
|
57
66
|
version: "0"
|
58
67
|
requirements: []
|
59
68
|
|
60
69
|
rubyforge_project:
|
61
|
-
rubygems_version: 1.
|
70
|
+
rubygems_version: 1.8.15
|
62
71
|
signing_key:
|
63
72
|
specification_version: 3
|
64
73
|
summary: A library for implementing daemon management capabilities
|
65
74
|
test_files: []
|
66
75
|
|
76
|
+
has_rdoc: true
|