process-daemon 0.3.0 → 0.5.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.
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
  - - '>='