daemon_controller 0.2.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2008 Phusion
1
+ Copyright (c) 2008, 2009, 2010, 2011, 2012 Phusion
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
data/README.markdown CHANGED
@@ -31,10 +31,9 @@ It provides the following functionality:
31
31
 
32
32
  ## Resources
33
33
 
34
- * [Website](http://github.com/FooBarWidget/daemon_controller)
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 => lambda { TCPSocket.new('localhost', 1234) },
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 => lambda { TCPSocket.new('localhost', SEARCH_SERVER_PORT) },
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 can only guarantee concurrency safety between multiple threads
464
- in the same process, if all of those threads use the same DaemonController
465
- object. Synchronization between multiple processes works fine.
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
@@ -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.2.6"
5
- s.date = "2011-02-13"
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 = "http://github.com/FooBarWidget/daemon_controller/tree/master"
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 is not safe.
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 = 0
24
- MINOR = 2
25
- TINY = 6
23
+ MAJOR = 1
24
+ MINOR = 0
25
+ TINY = 0
26
26
  VERSION_STRING = "#{MAJOR}.#{MINOR}.#{TINY}"
27
27
  end # class DaemonController
@@ -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
- # or Errno::ECONNRESET, Errno::EINVAL and Errno::EADDRNOTAVAIL then that also
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 the return value.
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
- return LockFile.new(File.expand_path(pid_file + ".lock"))
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
- version: 0.2.6
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: 2011-02-13 00:00:00 +01:00
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
- has_rdoc: true
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.5.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