process-daemon 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/process/daemon.rb +26 -10
- data/lib/process/daemon/controller.rb +89 -80
- data/lib/process/daemon/priviledges.rb +0 -4
- data/lib/process/daemon/version.rb +1 -1
- data/process-daemon.gemspec +4 -2
- data/test/test_daemon.rb +20 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7db4156d137b48f2986d06e6dfd5f1b5150d8afb
|
4
|
+
data.tar.gz: fec53d11a1bea87a073f68f9829edbfbafc2826c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e3b5741338cba187cba99156ee755812a8ada36ab4022cda60a870e19f87e2d373942a67567d387d00301e25becb86180cdf05726291a3976a8f320309eb7ee
|
7
|
+
data.tar.gz: da7769da5b93b2edd49541fcdb47615f6e2709765ebe38879ea87459aa899b4ba62be208a9c8c335f6606572b691fbd3ce35ebe8b4326705060c69c4efa8b367
|
data/lib/process/daemon.rb
CHANGED
@@ -130,30 +130,46 @@ module Process
|
|
130
130
|
Process.kill(0, :INT)
|
131
131
|
end
|
132
132
|
|
133
|
-
|
133
|
+
def run
|
134
|
+
startup
|
135
|
+
|
136
|
+
trap("INT") do
|
137
|
+
shutdown
|
138
|
+
end
|
139
|
+
end
|
134
140
|
|
141
|
+
# A shared instance of the daemon.
|
135
142
|
def self.instance
|
136
143
|
@instance ||= self.new
|
137
144
|
end
|
138
145
|
|
139
|
-
#
|
140
|
-
def self.
|
141
|
-
Controller.
|
146
|
+
# The process controller, responsible for managing the daemon process start, stop, restart, etc.
|
147
|
+
def self.controller(options = {})
|
148
|
+
@controller ||= Controller.new(instance, options)
|
149
|
+
end
|
150
|
+
|
151
|
+
# The main entry point for daemonized scripts.
|
152
|
+
def self.daemonize(*args)
|
153
|
+
# Wish Ruby 2.0 kwargs were backported to 1.9.3... oh well:
|
154
|
+
options = (args.last === Hash) ? args.pop : {}
|
155
|
+
argv = (args.last === Array) ? args.pop : ARGV
|
156
|
+
|
157
|
+
controller(options).daemonize(argv)
|
142
158
|
end
|
143
159
|
|
144
|
-
# Start the daemon instance
|
160
|
+
# Start the daemon instance.
|
145
161
|
def self.start
|
146
|
-
|
162
|
+
controller.start
|
147
163
|
end
|
148
164
|
|
149
|
-
# Stop the daemon instance
|
165
|
+
# Stop the daemon instance.
|
150
166
|
def self.stop
|
151
|
-
|
167
|
+
controller.stop
|
152
168
|
end
|
153
169
|
|
154
|
-
#
|
170
|
+
# Check if the daemon is runnning or not.
|
155
171
|
def self.status
|
156
|
-
|
172
|
+
controller.status
|
157
173
|
end
|
158
174
|
end
|
159
175
|
end
|
@@ -25,75 +25,63 @@ module Process
|
|
25
25
|
# Daemon startup timeout
|
26
26
|
TIMEOUT = 5
|
27
27
|
|
28
|
-
# This module contains functionality related to starting and stopping the daemon, and code for processing command line input.
|
29
|
-
|
28
|
+
# This module contains functionality related to starting and stopping the @daemon, and code for processing command line input.
|
29
|
+
class Controller
|
30
|
+
# `options[:output]` specifies where to write textual output describing what is going on.
|
31
|
+
def initialize(daemon, options = {})
|
32
|
+
@daemon = daemon
|
33
|
+
|
34
|
+
@output = options[:output] || $stdout
|
35
|
+
end
|
36
|
+
|
30
37
|
# This function is called from the daemon executable. It processes ARGV and checks whether the user is asking for `start`, `stop`, `restart`, `status`.
|
31
|
-
def
|
38
|
+
def daemonize(argv = ARGV)
|
32
39
|
case argv.shift.to_sym
|
33
40
|
when :start
|
34
|
-
start
|
35
|
-
status
|
41
|
+
start
|
42
|
+
status
|
36
43
|
when :stop
|
37
|
-
stop
|
38
|
-
status
|
39
|
-
ProcessFile.cleanup(daemon)
|
44
|
+
stop
|
45
|
+
status
|
46
|
+
ProcessFile.cleanup(@daemon)
|
40
47
|
when :restart
|
41
|
-
stop
|
42
|
-
ProcessFile.cleanup(daemon)
|
43
|
-
start
|
44
|
-
status
|
48
|
+
stop
|
49
|
+
ProcessFile.cleanup(@daemon)
|
50
|
+
start
|
51
|
+
status
|
45
52
|
when :status
|
46
|
-
status
|
53
|
+
status
|
47
54
|
else
|
48
|
-
puts "Invalid command. Please specify start, restart, stop or status."
|
49
|
-
exit 1
|
55
|
+
@stderr.puts Rainbow("Invalid command. Please specify start, restart, stop or status.").red
|
50
56
|
end
|
51
57
|
end
|
52
|
-
|
53
|
-
#
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
case ProcessFile.status(daemon)
|
58
|
-
when :running
|
59
|
-
$stderr.puts Rainbow("Daemon already running!").color(:blue)
|
60
|
-
return
|
61
|
-
when :stopped
|
62
|
-
# We are good to go...
|
63
|
-
else
|
64
|
-
$stderr.puts Rainbow("Daemon in unknown state! Will clear previous state and continue.").color(:red)
|
65
|
-
status(daemon)
|
66
|
-
ProcessFile.clear(daemon)
|
67
|
-
end
|
68
|
-
|
69
|
-
daemon.prefork
|
70
|
-
daemon.mark_log
|
58
|
+
|
59
|
+
# Fork a child process, detatch it and run the daemon code.
|
60
|
+
def spawn
|
61
|
+
@daemon.prefork
|
62
|
+
@daemon.mark_log
|
71
63
|
|
72
64
|
fork do
|
73
65
|
Process.setsid
|
74
66
|
exit if fork
|
75
67
|
|
76
|
-
ProcessFile.store(daemon, Process.pid)
|
68
|
+
ProcessFile.store(@daemon, Process.pid)
|
77
69
|
|
78
70
|
File.umask 0000
|
79
|
-
Dir.chdir daemon.working_directory
|
71
|
+
Dir.chdir @daemon.working_directory
|
80
72
|
|
81
73
|
$stdin.reopen "/dev/null"
|
82
|
-
$stdout.reopen daemon.log_file_path, "a"
|
74
|
+
$stdout.reopen @daemon.log_file_path, "a"
|
83
75
|
$stdout.sync = true
|
84
|
-
|
76
|
+
|
85
77
|
$stderr.reopen $stdout
|
86
78
|
$stderr.sync = true
|
87
79
|
|
88
80
|
begin
|
89
|
-
daemon.
|
90
|
-
|
91
|
-
trap("INT") do
|
92
|
-
daemon.shutdown
|
93
|
-
end
|
94
|
-
rescue
|
81
|
+
@daemon.run
|
82
|
+
rescue Exception => error
|
95
83
|
$stderr.puts "=== Daemon Exception Backtrace @ #{Time.now.to_s} ==="
|
96
|
-
$stderr.puts "#{
|
84
|
+
$stderr.puts "#{error.class}: #{error.message}"
|
97
85
|
$!.backtrace.each { |at| $stderr.puts at }
|
98
86
|
$stderr.puts "=== Daemon Crashed ==="
|
99
87
|
$stderr.flush
|
@@ -102,63 +90,84 @@ module Process
|
|
102
90
|
$stderr.flush
|
103
91
|
end
|
104
92
|
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# This function starts the daemon process in the background.
|
96
|
+
def start
|
97
|
+
@output.puts Rainbow("Starting daemon...").blue
|
98
|
+
|
99
|
+
case ProcessFile.status(@daemon)
|
100
|
+
when :running
|
101
|
+
@output.puts Rainbow("Daemon already running!").blue
|
102
|
+
return
|
103
|
+
when :stopped
|
104
|
+
# We are good to go...
|
105
|
+
else
|
106
|
+
@output.puts Rainbow("Daemon in unknown state! Will clear previous state and continue.").red
|
107
|
+
ProcessFile.clear(@daemon)
|
108
|
+
end
|
109
|
+
|
110
|
+
spawn
|
105
111
|
|
106
|
-
puts Rainbow("Waiting for daemon to start...").color(:blue)
|
107
112
|
sleep 0.1
|
108
113
|
timer = TIMEOUT
|
109
|
-
pid = ProcessFile.recall(daemon)
|
114
|
+
pid = ProcessFile.recall(@daemon)
|
110
115
|
|
111
116
|
while pid == nil and timer > 0
|
112
117
|
# Wait a moment for the forking to finish...
|
113
|
-
puts Rainbow("Waiting for daemon to start (#{timer}/#{TIMEOUT})").
|
118
|
+
@output.puts Rainbow("Waiting for daemon to start (#{timer}/#{TIMEOUT})").blue
|
114
119
|
sleep 1
|
115
120
|
|
116
|
-
# If the daemon has crashed, it is never going to start...
|
117
|
-
break if daemon.crashed?
|
121
|
+
# If the @daemon has crashed, it is never going to start...
|
122
|
+
break if @daemon.crashed?
|
118
123
|
|
119
|
-
pid = ProcessFile.recall(daemon)
|
124
|
+
pid = ProcessFile.recall(@daemon)
|
120
125
|
|
121
126
|
timer -= 1
|
122
127
|
end
|
123
128
|
end
|
124
|
-
|
129
|
+
|
125
130
|
# Prints out the status of the daemon
|
126
|
-
def
|
127
|
-
|
131
|
+
def status
|
132
|
+
daemon_state = ProcessFile.status(@daemon)
|
133
|
+
|
134
|
+
case daemon_state
|
128
135
|
when :running
|
129
|
-
puts Rainbow("Daemon status: running pid=#{ProcessFile.recall(daemon)}").
|
136
|
+
@output.puts Rainbow("Daemon status: running pid=#{ProcessFile.recall(@daemon)}").green
|
130
137
|
when :unknown
|
131
|
-
if daemon.crashed?
|
132
|
-
puts Rainbow("Daemon status: crashed").
|
138
|
+
if @daemon.crashed?
|
139
|
+
@output.puts Rainbow("Daemon status: crashed").red
|
133
140
|
|
134
|
-
|
135
|
-
|
136
|
-
daemon.tail_log(
|
141
|
+
@output.flush
|
142
|
+
@output.puts Rainbow("Dumping daemon crash log:").red
|
143
|
+
@daemon.tail_log(@output)
|
137
144
|
else
|
138
|
-
puts Rainbow("Daemon status: unknown").
|
145
|
+
@output.puts Rainbow("Daemon status: unknown").red
|
139
146
|
end
|
140
147
|
when :stopped
|
141
|
-
puts Rainbow("Daemon status: stopped").
|
148
|
+
@output.puts Rainbow("Daemon status: stopped").blue
|
142
149
|
end
|
150
|
+
|
151
|
+
return daemon_state
|
143
152
|
end
|
144
153
|
|
145
154
|
# Stops the daemon process.
|
146
|
-
def
|
147
|
-
puts Rainbow("Stopping daemon...").
|
155
|
+
def stop
|
156
|
+
@output.puts Rainbow("Stopping daemon...").blue
|
148
157
|
|
149
158
|
# Check if the pid file exists...
|
150
|
-
unless File.file?(daemon.process_file_path)
|
151
|
-
puts Rainbow("Pid file not found. Is the daemon running?").
|
159
|
+
unless File.file?(@daemon.process_file_path)
|
160
|
+
@output.puts Rainbow("Pid file not found. Is the daemon running?").red
|
152
161
|
return
|
153
162
|
end
|
154
163
|
|
155
|
-
pid = ProcessFile.recall(daemon)
|
164
|
+
pid = ProcessFile.recall(@daemon)
|
156
165
|
|
157
|
-
# Check if the daemon is already stopped...
|
158
|
-
unless ProcessFile.running(daemon)
|
159
|
-
puts Rainbow("Pid #{pid} is not running. Has daemon crashed?").
|
166
|
+
# Check if the @daemon is already stopped...
|
167
|
+
unless ProcessFile.running(@daemon)
|
168
|
+
@output.puts Rainbow("Pid #{pid} is not running. Has daemon crashed?").red
|
160
169
|
|
161
|
-
daemon.tail_log($stderr)
|
170
|
+
@daemon.tail_log($stderr)
|
162
171
|
|
163
172
|
return
|
164
173
|
end
|
@@ -168,29 +177,29 @@ module Process
|
|
168
177
|
Process.kill("INT", pgid)
|
169
178
|
sleep 0.1
|
170
179
|
|
171
|
-
sleep 1 if ProcessFile.running(daemon)
|
180
|
+
sleep 1 if ProcessFile.running(@daemon)
|
172
181
|
|
173
|
-
# Kill/Term loop - if the daemon didn't die easily, shoot
|
182
|
+
# Kill/Term loop - if the @daemon didn't die easily, shoot
|
174
183
|
# it a few more times.
|
175
184
|
attempts = 5
|
176
|
-
while ProcessFile.running(daemon) and attempts > 0
|
185
|
+
while ProcessFile.running(@daemon) and attempts > 0
|
177
186
|
sig = (attempts >= 2) ? "KILL" : "TERM"
|
178
187
|
|
179
|
-
puts Rainbow("Sending #{sig} to process group #{pgid}...").
|
188
|
+
@output.puts Rainbow("Sending #{sig} to process group #{pgid}...").red
|
180
189
|
Process.kill(sig, pgid)
|
181
190
|
|
182
191
|
attempts -= 1
|
183
192
|
sleep 1
|
184
193
|
end
|
185
194
|
|
186
|
-
# If after doing our best the daemon is still running (pretty odd)...
|
187
|
-
if ProcessFile.running(daemon)
|
188
|
-
puts Rainbow("Daemon appears to be still running!").
|
195
|
+
# If after doing our best the @daemon is still running (pretty odd)...
|
196
|
+
if ProcessFile.running(@daemon)
|
197
|
+
@output.puts Rainbow("Daemon appears to be still running!").red
|
189
198
|
return
|
190
199
|
end
|
191
200
|
|
192
|
-
# Otherwise the daemon has been stopped.
|
193
|
-
ProcessFile.clear(daemon)
|
201
|
+
# Otherwise the @daemon has been stopped.
|
202
|
+
ProcessFile.clear(@daemon)
|
194
203
|
end
|
195
204
|
end
|
196
205
|
end
|
@@ -25,10 +25,6 @@ module Process
|
|
25
25
|
module Priviledges
|
26
26
|
# Set the user of the current process. Supply either a user ID
|
27
27
|
# or a user name.
|
28
|
-
#
|
29
|
-
# Be aware that on Mac OS X / Ruby 1.8 there are bugs when the user id
|
30
|
-
# is negative (i.e. it doesn't work). For example "nobody" with uid -2
|
31
|
-
# won't work.
|
32
28
|
def self.change_user(user)
|
33
29
|
if user.kind_of?(String)
|
34
30
|
user = Etc.getpwnam(user).uid
|
data/process-daemon.gemspec
CHANGED
@@ -16,9 +16,11 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ["lib"]
|
19
|
-
|
19
|
+
|
20
|
+
spec.required_ruby_version = '>= 1.9.3'
|
21
|
+
|
20
22
|
spec.add_dependency "rainbow", "~> 2.0"
|
21
|
-
|
23
|
+
|
22
24
|
spec.add_development_dependency "bundler", "~> 1.3"
|
23
25
|
spec.add_development_dependency "minitest", "~> 5.3.2"
|
24
26
|
spec.add_development_dependency "rake"
|
data/test/test_daemon.rb
CHANGED
@@ -106,4 +106,24 @@ class DaemonTest < MiniTest::Test
|
|
106
106
|
def test_instances
|
107
107
|
refute_equal SleepDaemon.instance, XMLRPCDaemon.instance
|
108
108
|
end
|
109
|
+
|
110
|
+
def test_output
|
111
|
+
output = StringIO.new
|
112
|
+
|
113
|
+
controller = Process::Daemon::Controller.new(XMLRPCDaemon.instance, :output => output)
|
114
|
+
|
115
|
+
assert_equal :running, controller.status
|
116
|
+
|
117
|
+
assert_match /Daemon status: running pid=\d+/, output.string
|
118
|
+
|
119
|
+
output.rewind
|
120
|
+
controller.stop
|
121
|
+
|
122
|
+
assert_match /Stopping daemon/, output.string
|
123
|
+
|
124
|
+
output.rewind
|
125
|
+
controller.start
|
126
|
+
|
127
|
+
assert_match /Starting daemon/, output.string
|
128
|
+
end
|
109
129
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: process-daemon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-05-
|
11
|
+
date: 2014-05-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rainbow
|
@@ -99,7 +99,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
99
99
|
requirements:
|
100
100
|
- - '>='
|
101
101
|
- !ruby/object:Gem::Version
|
102
|
-
version:
|
102
|
+
version: 1.9.3
|
103
103
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
104
|
requirements:
|
105
105
|
- - '>='
|