daemon-spawn 0.2.0 → 0.3.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/.autotest +16 -0
- data/.gitignore +2 -0
- data/History.txt +9 -0
- data/Manifest.txt +5 -3
- data/README.txt +10 -1
- data/Rakefile +18 -5
- data/VERSION +1 -0
- data/daemon-spawn.gemspec +58 -0
- data/lib/{daemon-spawn.rb → daemon_spawn.rb} +78 -40
- data/test/daemon_spawn_test.rb +102 -0
- data/test/multi_daemon_spawn_test.rb +111 -0
- data/{examples → test/servers}/echo_server.rb +9 -3
- data/test/servers/simple_server.rb +34 -0
- metadata +36 -34
- data/test/test_daemon-spawn.rb +0 -0
data/.autotest
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Autotest.add_hook(:initialize) do |at|
|
2
|
+
at.clear_mappings
|
3
|
+
|
4
|
+
at.add_mapping(/.*flymake/) do |f, _|
|
5
|
+
[]
|
6
|
+
end
|
7
|
+
|
8
|
+
at.add_mapping(%r[lib/daemon-spawn.rb]) do |f, _|
|
9
|
+
at.files_matching /^test\/.*_test\.rb$/
|
10
|
+
end
|
11
|
+
|
12
|
+
at.add_mapping(/^test\/.*_test\.rb$/) do |filename, _|
|
13
|
+
filename
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/.gitignore
ADDED
data/History.txt
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
=== 0.3.0 / 2010-04-18
|
2
|
+
|
3
|
+
* Typo fix and internal refactoring patches from Emmanuel Gomez
|
4
|
+
* Added multi-process spawning and management (inspired by Jontathan Tropper's
|
5
|
+
patches)
|
6
|
+
* Added LSB-compliance patches from Woody Peterson
|
7
|
+
* Added some actual test-coverage :-P
|
8
|
+
* Moved examples from "examples" directory to "test/servers"
|
9
|
+
|
1
10
|
=== 0.2.0 / 2009-04-22
|
2
11
|
|
3
12
|
* Allow specification of args instead of ARGV so that scripts can
|
data/Manifest.txt
CHANGED
@@ -2,6 +2,8 @@ History.txt
|
|
2
2
|
Manifest.txt
|
3
3
|
README.txt
|
4
4
|
Rakefile
|
5
|
-
lib/
|
6
|
-
test/
|
7
|
-
|
5
|
+
lib/daemon_spawn.rb
|
6
|
+
test/daemon_spawn_test.rb
|
7
|
+
test/multi_daemon_spawn_test.rb
|
8
|
+
test/servers/echo_server.rb
|
9
|
+
test/servers/simple_server.rb
|
data/README.txt
CHANGED
@@ -46,7 +46,10 @@ The <tt>spawn!</tt> method takes a hash of symbolized keys. At a minimum you
|
|
46
46
|
_must_ specify the <tt>:working_dir</tt> option. You can also override
|
47
47
|
the default locations for the log and PID files.
|
48
48
|
|
49
|
-
|
49
|
+
If you pass a <tt>:processes</tt> option to the <tt>spawn!</tt>,
|
50
|
+
daemon spawn will start that number of processes.
|
51
|
+
|
52
|
+
See the <tt>test/servers</tt> directory for working examples.
|
50
53
|
|
51
54
|
=== RUNNING A DAEMON:
|
52
55
|
|
@@ -68,6 +71,12 @@ method of an instance of your daemon class.
|
|
68
71
|
|
69
72
|
None!
|
70
73
|
|
74
|
+
== CONTRIBUTIONS:
|
75
|
+
|
76
|
+
Feel free to fork this project and send me pull requests with any
|
77
|
+
changes that you have. Please note that I won't accept any patches
|
78
|
+
with significant formatting changes or ones without tests.
|
79
|
+
|
71
80
|
== INSTALL:
|
72
81
|
|
73
82
|
* sudo gem install daemon-spawn
|
data/Rakefile
CHANGED
@@ -1,12 +1,25 @@
|
|
1
1
|
# -*- ruby -*-
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
-
require '
|
5
|
-
require './lib/
|
4
|
+
require 'jeweler'
|
5
|
+
require './lib/daemon_spawn.rb'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
begin
|
8
|
+
require 'jeweler'
|
9
|
+
Jeweler::Tasks.new do |gemspec|
|
10
|
+
gemspec.name = "daemon-spawn"
|
11
|
+
gemspec.summary = "Daemon launching and management made dead simple"
|
12
|
+
gemspec.description = %Q[With daemon-spawn you can start, stop and restart processes that run
|
13
|
+
in the background. Processed are tracked by a simple PID file written
|
14
|
+
to disk.]
|
15
|
+
gemspec.rubyforge_project = "daemon-spawn"
|
16
|
+
gemspec.email = "alex.vollmer@gmail.com"
|
17
|
+
gemspec.homepage = "http://github.com/alexvollmer/daemon-spawn"
|
18
|
+
gemspec.authors = ["Alex Vollmer"]
|
19
|
+
Jeweler::GemcutterTasks.new
|
20
|
+
end
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler not available. Install it with: sudo gem install jeweler"
|
10
23
|
end
|
11
24
|
|
12
25
|
# vim: syntax=Ruby
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.0
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{daemon-spawn}
|
8
|
+
s.version = "0.3.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Alex Vollmer"]
|
12
|
+
s.date = %q{2010-05-20}
|
13
|
+
s.description = %q{With daemon-spawn you can start, stop and restart processes that run
|
14
|
+
in the background. Processed are tracked by a simple PID file written
|
15
|
+
to disk.}
|
16
|
+
s.email = %q{alex.vollmer@gmail.com}
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"README.txt"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".autotest",
|
22
|
+
".gitignore",
|
23
|
+
"History.txt",
|
24
|
+
"Manifest.txt",
|
25
|
+
"README.txt",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"daemon-spawn.gemspec",
|
29
|
+
"lib/daemon_spawn.rb",
|
30
|
+
"test/daemon_spawn_test.rb",
|
31
|
+
"test/multi_daemon_spawn_test.rb",
|
32
|
+
"test/servers/echo_server.rb",
|
33
|
+
"test/servers/simple_server.rb"
|
34
|
+
]
|
35
|
+
s.homepage = %q{http://github.com/alexvollmer/daemon-spawn}
|
36
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
37
|
+
s.require_paths = ["lib"]
|
38
|
+
s.rubyforge_project = %q{daemon-spawn}
|
39
|
+
s.rubygems_version = %q{1.3.7}
|
40
|
+
s.summary = %q{Daemon launching and management made dead simple}
|
41
|
+
s.test_files = [
|
42
|
+
"test/daemon_spawn_test.rb",
|
43
|
+
"test/multi_daemon_spawn_test.rb",
|
44
|
+
"test/servers/echo_server.rb",
|
45
|
+
"test/servers/simple_server.rb"
|
46
|
+
]
|
47
|
+
|
48
|
+
if s.respond_to? :specification_version then
|
49
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
50
|
+
s.specification_version = 3
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
53
|
+
else
|
54
|
+
end
|
55
|
+
else
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
@@ -1,9 +1,8 @@
|
|
1
|
-
require
|
1
|
+
require 'fileutils'
|
2
2
|
|
3
3
|
# Large portions of this were liberally stolen from the
|
4
4
|
# 'simple-daemon' project at http://simple-daemon.rubyforge.org/
|
5
5
|
module DaemonSpawn
|
6
|
-
VERSION = '0.2.0'
|
7
6
|
|
8
7
|
def self.usage(msg=nil) #:nodoc:
|
9
8
|
print "#{msg}, " if msg
|
@@ -19,16 +18,16 @@ module DaemonSpawn
|
|
19
18
|
end
|
20
19
|
|
21
20
|
if !File.writable?(File.dirname(daemon.pid_file))
|
22
|
-
STDERR.puts "Unable to write
|
21
|
+
STDERR.puts "Unable to write PID file to #{daemon.pid_file}"
|
23
22
|
exit 1
|
24
23
|
end
|
25
24
|
|
26
25
|
if daemon.alive? && daemon.singleton
|
27
|
-
STDERR.puts "An instance of #{daemon.
|
26
|
+
STDERR.puts "An instance of #{daemon.app_name} is already " +
|
28
27
|
"running (PID #{daemon.pid})"
|
29
|
-
exit
|
28
|
+
exit 0
|
30
29
|
end
|
31
|
-
|
30
|
+
|
32
31
|
fork do
|
33
32
|
Process.setsid
|
34
33
|
exit if fork
|
@@ -43,7 +42,7 @@ module DaemonSpawn
|
|
43
42
|
trap("TERM") {daemon.stop; exit}
|
44
43
|
daemon.start(args)
|
45
44
|
end
|
46
|
-
puts "#{daemon.
|
45
|
+
puts "#{daemon.app_name} started."
|
47
46
|
end
|
48
47
|
|
49
48
|
def self.stop(daemon) #:nodoc:
|
@@ -55,30 +54,30 @@ module DaemonSpawn
|
|
55
54
|
rescue Errno::ECHILD
|
56
55
|
end
|
57
56
|
else
|
58
|
-
puts "
|
59
|
-
exit
|
57
|
+
puts "PID file not found. Is the daemon started?"
|
60
58
|
end
|
61
59
|
rescue Errno::ESRCH
|
62
|
-
puts "
|
60
|
+
puts "PID file found, but process was not running. The daemon may have died."
|
63
61
|
end
|
64
62
|
|
65
63
|
def self.status(daemon) #:nodoc:
|
66
|
-
|
67
|
-
puts "#{daemon.classname} is running (PID #{PidFile.recall(daemon)})"
|
68
|
-
else
|
69
|
-
puts "#{daemon.classname} is NOT running"
|
70
|
-
end
|
64
|
+
puts "#{daemon.app_name} is #{daemon.alive? ? "" : "NOT "}running (PID #{daemon.pid})"
|
71
65
|
end
|
72
66
|
|
73
67
|
class Base
|
74
|
-
attr_accessor :log_file, :pid_file, :sync_log, :working_dir, :singleton
|
68
|
+
attr_accessor :log_file, :pid_file, :sync_log, :working_dir, :app_name, :singleton, :index
|
75
69
|
|
76
|
-
def initialize(opts={
|
77
|
-
raise
|
70
|
+
def initialize(opts = {})
|
71
|
+
raise 'You must specify a :working_dir' unless opts[:working_dir]
|
78
72
|
self.working_dir = opts[:working_dir]
|
79
|
-
|
80
|
-
self.
|
81
|
-
self.
|
73
|
+
self.app_name = opts[:application] || classname
|
74
|
+
self.pid_file = opts[:pid_file] || File.join(working_dir, 'tmp', 'pids', app_name + extension)
|
75
|
+
self.log_file = opts[:log_file] || File.join(working_dir, 'logs', app_name + '.log')
|
76
|
+
self.index = opts[:index] || 0
|
77
|
+
if self.index > 0
|
78
|
+
self.pid_file += ".#{self.index}"
|
79
|
+
self.log_file += ".#{self.index}"
|
80
|
+
end
|
82
81
|
self.sync_log = opts[:sync_log]
|
83
82
|
self.singleton = opts[:singleton] || false
|
84
83
|
end
|
@@ -116,6 +115,22 @@ module DaemonSpawn
|
|
116
115
|
IO.read(self.pid_file).to_i rescue nil
|
117
116
|
end
|
118
117
|
|
118
|
+
def self.build(options)
|
119
|
+
count = options.delete(:processes) || 1
|
120
|
+
daemons = []
|
121
|
+
count.times do |index|
|
122
|
+
daemons << new(options.merge(:index => index))
|
123
|
+
end
|
124
|
+
daemons
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.find(options)
|
128
|
+
pid_file = new(options).pid_file
|
129
|
+
basename = File.basename(pid_file).split('.').first
|
130
|
+
pid_files = Dir.glob(File.join(File.dirname(pid_file), "#{basename}.*pid*"))
|
131
|
+
pid_files.map { |f| new(options.merge(:pid_file => f)) }
|
132
|
+
end
|
133
|
+
|
119
134
|
# Invoke this method to process command-line args and dispatch
|
120
135
|
# appropriately. Valid options include the following _symbols_:
|
121
136
|
# - <tt>:working_dir</tt> -- the working directory (required)
|
@@ -127,25 +142,10 @@ module DaemonSpawn
|
|
127
142
|
# args must begin with 'start', 'stop', 'status', or 'restart'.
|
128
143
|
# The first token will be removed and any remaining arguments
|
129
144
|
# passed to the daemon's start method.
|
130
|
-
def self.spawn!(opts={
|
131
|
-
case args.
|
132
|
-
when 'start'
|
133
|
-
|
134
|
-
DaemonSpawn.start(daemon, args)
|
135
|
-
when 'stop'
|
136
|
-
daemon = self.new(opts)
|
137
|
-
DaemonSpawn.stop(daemon)
|
138
|
-
when 'status'
|
139
|
-
daemon = self.new(opts)
|
140
|
-
if daemon.alive?
|
141
|
-
puts "#{daemon.classname} is running (#{daemon.pid})"
|
142
|
-
else
|
143
|
-
puts "#{daemon.classname} is NOT running"
|
144
|
-
end
|
145
|
-
when 'restart'
|
146
|
-
daemon = self.new(opts)
|
147
|
-
DaemonSpawn.stop(daemon)
|
148
|
-
DaemonSpawn.start(daemon, args)
|
145
|
+
def self.spawn!(opts = {}, args = ARGV)
|
146
|
+
case args.any? and command = args.shift
|
147
|
+
when 'start', 'stop', 'status', 'restart'
|
148
|
+
send(command, opts, args)
|
149
149
|
when '-h', '--help', 'help'
|
150
150
|
DaemonSpawn.usage
|
151
151
|
exit
|
@@ -154,5 +154,43 @@ module DaemonSpawn
|
|
154
154
|
exit 1
|
155
155
|
end
|
156
156
|
end
|
157
|
+
|
158
|
+
def self.start(opts, args)
|
159
|
+
daemons = find(opts)
|
160
|
+
if daemons.empty?
|
161
|
+
daemons = build(opts)
|
162
|
+
daemons.map { |d| DaemonSpawn.start(d, args) }
|
163
|
+
else
|
164
|
+
puts "Daemons already started! PIDS: #{daemons.map {|d| d.pid}.join(', ')}"
|
165
|
+
exit 1
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.stop(opts, args)
|
170
|
+
daemons = find(opts)
|
171
|
+
if daemons.empty?
|
172
|
+
puts "No PID files found. Is the daemon started?"
|
173
|
+
exit 1
|
174
|
+
else
|
175
|
+
daemons.each { |d| DaemonSpawn.stop(d) }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.status(opts, args)
|
180
|
+
daemons = find(opts)
|
181
|
+
if daemons.empty?
|
182
|
+
puts 'No PIDs found'
|
183
|
+
else
|
184
|
+
daemons.each { |d| DaemonSpawn.status(d) }
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def self.restart(opts, args)
|
189
|
+
daemons = find(opts)
|
190
|
+
daemons.map do |daemon|
|
191
|
+
DaemonSpawn.stop(daemon)
|
192
|
+
DaemonSpawn.start(daemon, args)
|
193
|
+
end
|
194
|
+
end
|
157
195
|
end
|
158
196
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require "socket"
|
2
|
+
require "test/unit"
|
3
|
+
|
4
|
+
class DaemonSpawnTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
SERVERS = File.join(File.dirname(__FILE__), "servers")
|
7
|
+
|
8
|
+
def with_socket
|
9
|
+
socket = TCPSocket.new('localhost', 5150)
|
10
|
+
socket.setsockopt(Socket::SOL_SOCKET,
|
11
|
+
Socket::SO_RCVTIMEO,
|
12
|
+
[1, 0].pack("l_2"))
|
13
|
+
|
14
|
+
begin
|
15
|
+
yield(socket) if block_given?
|
16
|
+
ensure
|
17
|
+
socket.close
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def echo_server(*args)
|
22
|
+
`./echo_server.rb #{args.join(' ')}`
|
23
|
+
end
|
24
|
+
|
25
|
+
def while_running
|
26
|
+
Dir.chdir(SERVERS) do
|
27
|
+
`./echo_server.rb stop`
|
28
|
+
assert_match(/EchoServer started./, `./echo_server.rb start 5150`)
|
29
|
+
sleep 1
|
30
|
+
begin
|
31
|
+
with_socket
|
32
|
+
ensure
|
33
|
+
assert_match(//, `./echo_server.rb stop`)
|
34
|
+
assert_raises(Errno::ECONNREFUSED) { TCPSocket.new('localhost', 5150) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_daemon_running
|
40
|
+
while_running do |socket|
|
41
|
+
socket << "foobar\n"
|
42
|
+
assert_equal "foobar\n", socket.readline
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_status_running
|
47
|
+
while_running do |socket|
|
48
|
+
assert_match(/EchoServer is running/, `./echo_server.rb status`)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_status_not_running
|
53
|
+
Dir.chdir(SERVERS) do
|
54
|
+
assert_match(/No PIDs found/, `./echo_server.rb status`)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_start_after_started
|
59
|
+
while_running do
|
60
|
+
pid = echo_server("status").match(/PID (\d+)/)[1]
|
61
|
+
assert_match(/Daemons already started! PIDS: #{pid}/,
|
62
|
+
echo_server("start"))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_stop_after_stopped
|
67
|
+
Dir.chdir(SERVERS) do
|
68
|
+
assert_match("No PID files found. Is the daemon started?",
|
69
|
+
`./echo_server.rb stop`)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_restart_after_stopped
|
74
|
+
Dir.chdir(SERVERS) do
|
75
|
+
assert_match(/EchoServer started/, `./echo_server.rb restart 5150`)
|
76
|
+
assert_equal(0, $?.exitstatus)
|
77
|
+
sleep 1
|
78
|
+
with_socket do |socket|
|
79
|
+
socket << "foobar\n"
|
80
|
+
assert_equal "foobar\n", socket.readline
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_restart_after_started
|
86
|
+
Dir.chdir(SERVERS) do
|
87
|
+
assert_match(/EchoServer started/, `./echo_server.rb start 5150`)
|
88
|
+
assert_equal(0, $?.exitstatus)
|
89
|
+
sleep 1
|
90
|
+
|
91
|
+
assert_match(/EchoServer started/, `./echo_server.rb restart 5150`)
|
92
|
+
assert_equal(0, $?.exitstatus)
|
93
|
+
sleep 1
|
94
|
+
|
95
|
+
with_socket do |socket|
|
96
|
+
socket << "foobar\n"
|
97
|
+
assert_equal "foobar\n", socket.readline
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "tempfile"
|
3
|
+
|
4
|
+
class MultiDaemonSpawnTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
SERVERS = File.join(File.dirname(__FILE__), "servers")
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@tmpfile = Tempfile.new("multi_daemon_spawn_test")
|
10
|
+
end
|
11
|
+
|
12
|
+
def tear_down
|
13
|
+
@tmpfile.delete
|
14
|
+
end
|
15
|
+
|
16
|
+
def simple_server(*args)
|
17
|
+
`./simple_server.rb #{args.join(" ")}`
|
18
|
+
end
|
19
|
+
|
20
|
+
def current_pids
|
21
|
+
regexp = /SimpleServer is running \(PID (\d+)\)/
|
22
|
+
pids = simple_server("status").split("\n").map do |line|
|
23
|
+
if m = regexp.match(line)
|
24
|
+
m[1]
|
25
|
+
else
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
end.compact
|
29
|
+
end
|
30
|
+
|
31
|
+
def while_running
|
32
|
+
Dir.chdir(SERVERS) do
|
33
|
+
simple_server "stop"
|
34
|
+
simple_server "start", @tmpfile.path
|
35
|
+
sleep 1
|
36
|
+
begin
|
37
|
+
yield if block_given?
|
38
|
+
ensure
|
39
|
+
simple_server "stop"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_start_multiple
|
45
|
+
while_running do
|
46
|
+
lines = open(@tmpfile.path).readlines
|
47
|
+
assert_equal 2, lines.size
|
48
|
+
assert lines.member?("SimpleServer (0) started\n")
|
49
|
+
assert lines.member?("SimpleServer (1) started\n")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_status_multiple
|
54
|
+
while_running do
|
55
|
+
lines = simple_server("status").split("\n")
|
56
|
+
lines.each do |line|
|
57
|
+
assert_match /SimpleServer is running/, line
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_stop_multiple
|
63
|
+
while_running
|
64
|
+
Dir.chdir(SERVERS) do
|
65
|
+
assert_match /No PIDs found/, simple_server("status")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_restart_multiple
|
70
|
+
while_running do
|
71
|
+
pids = current_pids
|
72
|
+
simple_server "restart"
|
73
|
+
new_pids = current_pids
|
74
|
+
assert_not_equal pids.sort, new_pids.sort
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_status_with_one_dead_process
|
79
|
+
while_running do
|
80
|
+
pids = current_pids
|
81
|
+
Process.kill(9, pids[0].to_i)
|
82
|
+
|
83
|
+
lines = simple_server("status").split("\n")
|
84
|
+
assert_equal 2, lines.size
|
85
|
+
assert lines.member?("SimpleServer is NOT running (PID #{pids[0]})")
|
86
|
+
assert lines.member?("SimpleServer is running (PID #{pids[1]})")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_restart_with_one_dead_process
|
91
|
+
while_running do
|
92
|
+
pids = current_pids
|
93
|
+
Process.kill(9, pids[0].to_i)
|
94
|
+
|
95
|
+
lines = simple_server("restart").split("\n")
|
96
|
+
assert lines.member?("PID file found, but process was not running. The daemon may have died."), lines.inspect
|
97
|
+
assert_equal 2, lines.select { |l| l == "SimpleServer started." }.size
|
98
|
+
|
99
|
+
new_pids = current_pids
|
100
|
+
assert_not_equal new_pids, pids
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_start_after_started
|
105
|
+
while_running do
|
106
|
+
pids = current_pids
|
107
|
+
assert_match(/Daemons already started! PIDS: #{pids.join(', ')}/,
|
108
|
+
simple_server("start"))
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
$:.unshift(File.join(File.dirname(__FILE__), "..", "..", "lib"))
|
4
|
+
|
5
|
+
require "daemon_spawn"
|
3
6
|
require "socket"
|
4
|
-
require "rubygems"
|
5
|
-
require "daemon-spawn"
|
6
7
|
|
7
8
|
# An echo server using daemon-spawn. It starts up local TCP server
|
8
9
|
# socket and repeats each line it receives on the connection. To fire
|
@@ -21,13 +22,18 @@ class EchoServer < DaemonSpawn::Base
|
|
21
22
|
def start(args)
|
22
23
|
port = args.empty? ? 0 : args.first.to_i
|
23
24
|
self.server_socket = TCPServer.new('127.0.0.1', port)
|
25
|
+
self.server_socket.setsockopt(Socket::SOL_SOCKET,
|
26
|
+
Socket::SO_REUSEADDR,
|
27
|
+
true)
|
24
28
|
port = self.server_socket.addr[1]
|
25
29
|
puts "EchoServer started on port #{port}"
|
26
30
|
loop do
|
27
31
|
begin
|
28
32
|
client = self.server_socket.accept
|
33
|
+
puts "Got a connection from #{client}"
|
29
34
|
while str = client.gets
|
30
35
|
client.write(str)
|
36
|
+
puts "Echoed '#{str}' to #{client}"
|
31
37
|
end
|
32
38
|
rescue Errno::ECONNRESET => e
|
33
39
|
STDERR.puts "Client reset connection"
|
@@ -41,7 +47,7 @@ class EchoServer < DaemonSpawn::Base
|
|
41
47
|
end
|
42
48
|
end
|
43
49
|
|
44
|
-
EchoServer.spawn!(:working_dir => File.join(File.dirname(__FILE__), '..'),
|
50
|
+
EchoServer.spawn!(:working_dir => File.join(File.dirname(__FILE__), '..', '..'),
|
45
51
|
:log_file => '/tmp/echo_server.log',
|
46
52
|
:pid_file => '/tmp/echo_server.pid',
|
47
53
|
:sync_log => true,
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift(File.join(File.dirname(__FILE__), "..", "..", "lib"))
|
4
|
+
|
5
|
+
require "daemon_spawn"
|
6
|
+
|
7
|
+
class SimpleServer < DaemonSpawn::Base
|
8
|
+
|
9
|
+
attr_accessor :outfile
|
10
|
+
|
11
|
+
def start(args)
|
12
|
+
abort "USAGE: phrase_server.rb LOGFILE" if args.empty?
|
13
|
+
@outfile = args.first
|
14
|
+
self.puts "SimpleServer (#{self.index}) started"
|
15
|
+
while true # keep running like a real daemon
|
16
|
+
sleep 5
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def puts(str)
|
21
|
+
open(@outfile, "a") { |f| f.puts str }
|
22
|
+
end
|
23
|
+
|
24
|
+
def stop
|
25
|
+
self.puts "SimpleServer (#{self.index}) stopped"
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
SimpleServer.spawn!(:working_dir => File.join(File.dirname(__FILE__), '..', '..'),
|
31
|
+
:log_file => '/tmp/simple_server.log',
|
32
|
+
:pid_file => '/tmp/simple_server.pid',
|
33
|
+
:sync_log => true,
|
34
|
+
:processes => 2)
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: daemon-spawn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 19
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 0.3.0
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Alex Vollmer
|
@@ -9,75 +15,71 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date:
|
18
|
+
date: 2010-05-20 00:00:00 -07:00
|
13
19
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
name: hoe
|
17
|
-
type: :development
|
18
|
-
version_requirement:
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 2.3.3
|
24
|
-
version:
|
20
|
+
dependencies: []
|
21
|
+
|
25
22
|
description: |-
|
26
|
-
Daemon launching and management made dead simple.
|
27
|
-
|
28
23
|
With daemon-spawn you can start, stop and restart processes that run
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
In addition, you can choose to either execute ruby in your daemonized
|
33
|
-
process or 'exec' another process altogether (handy for wrapping other
|
34
|
-
services).
|
35
|
-
email:
|
36
|
-
- alex.vollmer@gmail.com
|
24
|
+
in the background. Processed are tracked by a simple PID file written
|
25
|
+
to disk.
|
26
|
+
email: alex.vollmer@gmail.com
|
37
27
|
executables: []
|
38
28
|
|
39
29
|
extensions: []
|
40
30
|
|
41
31
|
extra_rdoc_files:
|
42
|
-
- History.txt
|
43
|
-
- Manifest.txt
|
44
32
|
- README.txt
|
45
33
|
files:
|
34
|
+
- .autotest
|
35
|
+
- .gitignore
|
46
36
|
- History.txt
|
47
37
|
- Manifest.txt
|
48
38
|
- README.txt
|
49
39
|
- Rakefile
|
50
|
-
-
|
51
|
-
-
|
52
|
-
-
|
40
|
+
- VERSION
|
41
|
+
- daemon-spawn.gemspec
|
42
|
+
- lib/daemon_spawn.rb
|
43
|
+
- test/daemon_spawn_test.rb
|
44
|
+
- test/multi_daemon_spawn_test.rb
|
45
|
+
- test/servers/echo_server.rb
|
46
|
+
- test/servers/simple_server.rb
|
53
47
|
has_rdoc: true
|
54
48
|
homepage: http://github.com/alexvollmer/daemon-spawn
|
55
49
|
licenses: []
|
56
50
|
|
57
51
|
post_install_message:
|
58
52
|
rdoc_options:
|
59
|
-
- --
|
60
|
-
- README.txt
|
53
|
+
- --charset=UTF-8
|
61
54
|
require_paths:
|
62
55
|
- lib
|
63
56
|
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
64
58
|
requirements:
|
65
59
|
- - ">="
|
66
60
|
- !ruby/object:Gem::Version
|
61
|
+
hash: 3
|
62
|
+
segments:
|
63
|
+
- 0
|
67
64
|
version: "0"
|
68
|
-
version:
|
69
65
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
70
67
|
requirements:
|
71
68
|
- - ">="
|
72
69
|
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
73
|
version: "0"
|
74
|
-
version:
|
75
74
|
requirements: []
|
76
75
|
|
77
76
|
rubyforge_project: daemon-spawn
|
78
|
-
rubygems_version: 1.3.
|
77
|
+
rubygems_version: 1.3.7
|
79
78
|
signing_key:
|
80
79
|
specification_version: 3
|
81
80
|
summary: Daemon launching and management made dead simple
|
82
81
|
test_files:
|
83
|
-
- test/
|
82
|
+
- test/daemon_spawn_test.rb
|
83
|
+
- test/multi_daemon_spawn_test.rb
|
84
|
+
- test/servers/echo_server.rb
|
85
|
+
- test/servers/simple_server.rb
|
data/test/test_daemon-spawn.rb
DELETED
File without changes
|