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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ad2299e94a6c61f1720944c19525b84c535c597f
4
- data.tar.gz: 38aed4cdf9ee795711e7b9507bb1008e7a9940c4
3
+ metadata.gz: 7db4156d137b48f2986d06e6dfd5f1b5150d8afb
4
+ data.tar.gz: fec53d11a1bea87a073f68f9829edbfbafc2826c
5
5
  SHA512:
6
- metadata.gz: e6653cc71627aac7a2ebb0c5bf90c71eda263507d01f6ab5d1a6bd267fdfb866cf84a92e5f9ff13dc50219f2eca3e607843a4e161b9ddfdd87559bb6a3b3c662
7
- data.tar.gz: 85831d76b49be8ee9e046e6a4921d72fa4ffd08b80fcee77dfb98652ce710504e9057a42cd645d758ed593ddef389a03de96b9b1c638ef1ba6a4e265be36ecbb
6
+ metadata.gz: 4e3b5741338cba187cba99156ee755812a8ada36ab4022cda60a870e19f87e2d373942a67567d387d00301e25becb86180cdf05726291a3976a8f320309eb7ee
7
+ data.tar.gz: da7769da5b93b2edd49541fcdb47615f6e2709765ebe38879ea87459aa899b4ba62be208a9c8c335f6606572b691fbd3ce35ebe8b4326705060c69c4efa8b367
@@ -130,30 +130,46 @@ module Process
130
130
  Process.kill(0, :INT)
131
131
  end
132
132
 
133
- # Helper methods for static daemon instance
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
- # Main entry point for command line:
140
- def self.daemonize(argv = ARGV)
141
- Controller.daemonize(instance, argv)
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
- Controller.start(instance)
162
+ controller.start
147
163
  end
148
164
 
149
- # Stop the daemon instance:
165
+ # Stop the daemon instance.
150
166
  def self.stop
151
- Controller.stop(instance)
167
+ controller.stop
152
168
  end
153
169
 
154
- # Get the status of the daemon instance, `:stopped`, `:running` or `:unknown`.
170
+ # Check if the daemon is runnning or not.
155
171
  def self.status
156
- ProcessFile.status(instance)
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
- module Controller
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 self.daemonize(daemon, argv = ARGV)
38
+ def daemonize(argv = ARGV)
32
39
  case argv.shift.to_sym
33
40
  when :start
34
- start(daemon)
35
- status(daemon)
41
+ start
42
+ status
36
43
  when :stop
37
- stop(daemon)
38
- status(daemon)
39
- ProcessFile.cleanup(daemon)
44
+ stop
45
+ status
46
+ ProcessFile.cleanup(@daemon)
40
47
  when :restart
41
- stop(daemon)
42
- ProcessFile.cleanup(daemon)
43
- start(daemon)
44
- status(daemon)
48
+ stop
49
+ ProcessFile.cleanup(@daemon)
50
+ start
51
+ status
45
52
  when :status
46
- status(daemon)
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
- # This function starts the supplied daemon
54
- def self.start(daemon)
55
- puts Rainbow("Starting daemon...").color(:blue)
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.startup
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 "#{$!.class}: #{$!.message}"
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})").color(:blue)
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 self.status(daemon)
127
- case ProcessFile.status(daemon)
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)}").color(:green)
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").color(:red)
138
+ if @daemon.crashed?
139
+ @output.puts Rainbow("Daemon status: crashed").red
133
140
 
134
- $stdout.flush
135
- $stderr.puts Rainbow("Dumping daemon crash log:").color(:red)
136
- daemon.tail_log($stderr)
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").color(:red)
145
+ @output.puts Rainbow("Daemon status: unknown").red
139
146
  end
140
147
  when :stopped
141
- puts Rainbow("Daemon status: stopped").color(:blue)
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 self.stop(daemon)
147
- puts Rainbow("Stopping daemon...").color(:blue)
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?").color(:red)
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?").color(:red)
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}...").color(:red)
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!").color(:red)
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
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Process
22
22
  class Daemon
23
- VERSION = "0.3.0"
23
+ VERSION = "0.5.0"
24
24
  end
25
25
  end
@@ -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"
@@ -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.3.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 00:00:00.000000000 Z
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: '0'
102
+ version: 1.9.3
103
103
  required_rubygems_version: !ruby/object:Gem::Requirement
104
104
  requirements:
105
105
  - - '>='