bluepill 0.0.60 → 0.0.61

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -149,6 +149,17 @@ You can also set an app-wide uid/gid:
149
149
  end
150
150
  ```
151
151
 
152
+ To track resources of child processes, use :include_children:
153
+ ```ruby
154
+ Bluepill.application("app_name") do |app|
155
+ app.process("process_name") do |process|
156
+ process.start_command = "/usr/bin/some_start_command"
157
+ process.pid_file = "/tmp/some_pid_file.pid"
158
+ process.checks :mem_usage, :every => 1.seconds, :below => 5.megabytes, :times => [3,5], :include_children => true
159
+ end
160
+ end
161
+ ```
162
+
152
163
  To check for flapping:
153
164
 
154
165
  ```ruby
@@ -254,23 +265,39 @@ We recommend that you _not_ do that and instead use the config options to captur
254
265
  The main benefit of using the config options is that Bluepill will be able to monitor the correct process instead of just watching the shell that spawned your actual server.
255
266
 
256
267
  ### CLI
257
- To start a bluepill process and load a config:
268
+
269
+ #### Usage
270
+
271
+ bluepill [app_name] command [options]
272
+
273
+ For the "load" command, the _app_name_ is specified in the config file, and
274
+ must not be provided on the command line.
275
+
276
+ For all other commands, the _app_name_ is optional if there is only
277
+ one bluepill daemon running. Otherwise, the _app_name_ must be
278
+ provided, because the command will fail when there are multiple
279
+ bluepill daemons running. The example commands below leaves out the
280
+ _app_name_.
281
+
282
+ #### Commands
283
+
284
+ To start a bluepill daemon and load the config for an application:
258
285
 
259
286
  sudo bluepill load /path/to/production.pill
260
287
 
261
- To act on a process or group:
288
+ To act on a process or group for an application:
262
289
 
263
290
  sudo bluepill <start|stop|restart|unmonitor> <process_or_group_name>
264
291
 
265
- To view process statuses:
292
+ To view process statuses for an application:
266
293
 
267
294
  sudo bluepill status
268
295
 
269
- To view the log for a process or group:
296
+ To view the log for a process or group for an application:
270
297
 
271
298
  sudo bluepill log <process_or_group_name>
272
299
 
273
- To quit bluepill:
300
+ To quit the bluepill daemon for an application:
274
301
 
275
302
  sudo bluepill quit
276
303
 
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
14
14
  s.summary = %q{A process monitor written in Ruby with stability and minimalism in mind.}
15
15
  s.description = %q{Bluepill keeps your daemons up while taking up as little resources as possible. After all you probably want the resources of your server to be used by whatever daemons you are running rather than the thing that's supposed to make sure they are brought back up, should they die or misbehave.}
16
16
 
17
- s.add_dependency 'daemons', ['~> 1.1.4', '<= 1.1.6']
17
+ s.add_dependency 'daemons', ['~> 1.1.4']
18
18
  s.add_dependency 'state_machine', '~> 1.1.0'
19
19
  s.add_dependency 'activesupport', '>= 3.0.0'
20
20
  s.add_dependency 'i18n', '>= 0.5.0'
@@ -1,11 +1,13 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  require 'thread'
3
+ require 'bluepill/system'
4
+ require 'bluepill/process_journal'
3
5
 
4
6
  module Bluepill
5
7
  class Application
6
8
  PROCESS_COMMANDS = [:start, :stop, :restart, :unmonitor, :status]
7
9
 
8
- attr_accessor :name, :logger, :base_dir, :socket, :pid_file
10
+ attr_accessor :name, :logger, :base_dir, :socket, :pid_file, :kill_timeout
9
11
  attr_accessor :groups, :work_queue
10
12
  attr_accessor :pids_dir, :log_file
11
13
 
@@ -17,10 +19,11 @@ module Bluepill
17
19
  self.base_dir = options[:base_dir] || '/var/run/bluepill'
18
20
  self.pid_file = File.join(self.base_dir, 'pids', self.name + ".pid")
19
21
  self.pids_dir = File.join(self.base_dir, 'pids', self.name)
22
+ self.kill_timeout = options[:kill_timeout] || 10
20
23
 
21
24
  self.groups = {}
22
25
 
23
- self.logger = Bluepill::Logger.new(:log_file => self.log_file, :stdout => foreground?).prefix_with(self.name)
26
+ self.logger = ProcessJournal.logger = Bluepill::Logger.new(:log_file => self.log_file, :stdout => foreground?).prefix_with(self.name)
24
27
 
25
28
  self.setup_signal_traps
26
29
  self.setup_pids_dir
@@ -115,6 +118,9 @@ module Bluepill
115
118
 
116
119
  def start_server
117
120
  self.kill_previous_bluepill
121
+ ProcessJournal.kill_all_from_all_journals
122
+ ProcessJournal.clear_all_atomic_fs_locks
123
+ ::Process.setpgid(0, 0)
118
124
 
119
125
  Daemonize.daemonize unless foreground?
120
126
 
@@ -141,17 +147,22 @@ module Bluepill
141
147
  end
142
148
  sleep 1
143
149
  end
144
- cleanup
145
150
  end
146
151
 
147
152
  def cleanup
148
- File.unlink(self.socket.path) if self.socket
149
- File.unlink(self.pid_file) if File.exists?(self.pid_file)
153
+ ProcessJournal.kill_all_from_all_journals
154
+ ProcessJournal.clear_all_atomic_fs_locks
155
+ begin
156
+ System.delete_if_exists(self.socket.path) if self.socket
157
+ rescue IOError
158
+ end
159
+ System.delete_if_exists(self.pid_file)
150
160
  end
151
161
 
152
162
  def setup_signal_traps
153
163
  terminator = Proc.new do
154
164
  puts "Terminating..."
165
+ cleanup
155
166
  @running = false
156
167
  end
157
168
 
@@ -183,7 +194,7 @@ module Bluepill
183
194
  $stderr.puts "#{e.class}: #{e.message}"
184
195
  exit(4) unless e.is_a?(Errno::ESRCH)
185
196
  else
186
- 10.times do |i|
197
+ kill_timeout.times do |i|
187
198
  sleep 0.5
188
199
  break unless System.pid_alive?(previous_pid)
189
200
  end
@@ -15,6 +15,7 @@ module Bluepill
15
15
  @every = options.delete(:every)
16
16
  @times = options.delete(:times) || [1,1]
17
17
  @times = [@times, @times] unless @times.is_a?(Array) # handles :times => 5
18
+ @include_children = options.delete(:include_children) || false
18
19
 
19
20
  self.clear_history!
20
21
 
@@ -25,7 +26,7 @@ module Bluepill
25
26
  if @last_ran_at.nil? || (@last_ran_at + @every) <= tick_number
26
27
  @last_ran_at = tick_number
27
28
 
28
- value = @process_condition.run(pid)
29
+ value = @process_condition.run(pid, @include_children)
29
30
  @history << HistoryValue.new(@process_condition.format_value(value), @process_condition.check(value))
30
31
  self.logger.info(self.to_s)
31
32
 
@@ -1,5 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  require 'fileutils'
3
+ require 'bluepill/system'
3
4
 
4
5
  module Bluepill
5
6
  class Controller
@@ -90,8 +91,8 @@ module Bluepill
90
91
  if !pid || !System.pid_alive?(pid)
91
92
  pid_file = File.join(self.pids_dir, "#{app}.pid")
92
93
  sock_file = File.join(self.sockets_dir, "#{app}.sock")
93
- File.unlink(pid_file) if File.exists?(pid_file)
94
- File.unlink(sock_file) if File.exists?(sock_file)
94
+ System.delete_if_exists(pid_file)
95
+ System.delete_if_exists(sock_file)
95
96
  end
96
97
  end
97
98
  end
@@ -5,10 +5,13 @@ gem "state_machine"
5
5
 
6
6
  require "state_machine"
7
7
  require "daemons"
8
+ require "bluepill/system"
9
+ require "bluepill/process_journal"
8
10
 
9
11
  module Bluepill
10
12
  class Process
11
13
  CONFIGURABLE_ATTRIBUTES = [
14
+ :pre_start_command,
12
15
  :start_command,
13
16
  :stop_command,
14
17
  :restart_command,
@@ -263,11 +266,19 @@ module Bluepill
263
266
  end
264
267
 
265
268
  def start_process
269
+ ProcessJournal.kill_all_from_journal(name) # be sure nothing else is running from previous runs
270
+ pre_start_process
266
271
  logger.warning "Executing start command: #{start_command}"
267
-
268
272
  if self.daemonize?
269
- System.daemonize(start_command, self.system_command_options)
270
-
273
+ daemon_id = System.daemonize(start_command, self.system_command_options)
274
+ if daemon_id > 0
275
+ ProcessJournal.append_pid_to_journal(name, daemon_id)
276
+ children.each {|child|
277
+ child_pid = child.actual_id
278
+ ProcessJournal.append_pid_to_journal(name, child_id)
279
+ } if self.monitor_children?
280
+ end
281
+ daemon_id
271
282
  else
272
283
  # This is a self-daemonizing process
273
284
  with_timeout(start_grace_time) do
@@ -283,7 +294,23 @@ module Bluepill
283
294
  self.skip_ticks_for(start_grace_time)
284
295
  end
285
296
 
297
+ def pre_start_process
298
+ return unless pre_start_command
299
+ logger.warning "Executing pre start command: #{pre_start_command}"
300
+ result = System.execute_blocking(pre_start_command, self.system_command_options)
301
+ unless result[:exit_code].zero?
302
+ logger.warning "Pre start command execution returned non-zero exit code:"
303
+ logger.warning result.inspect
304
+ end
305
+ end
306
+
286
307
  def stop_process
308
+ if monitor_children
309
+ System.get_children(self.actual_pid).each do |child_pid|
310
+ ProcessJournal.append_pid_to_journal(name, child_pid)
311
+ end
312
+ end
313
+
287
314
  if stop_command
288
315
  cmd = self.prepare_command(stop_command)
289
316
  logger.warning "Executing stop command: #{cmd}"
@@ -325,6 +352,7 @@ module Bluepill
325
352
  logger.warning "Executing default stop command. Sending TERM signal to #{actual_pid}"
326
353
  signal_process("TERM")
327
354
  end
355
+ ProcessJournal.kill_all_from_journal(name) # finish cleanup
328
356
  self.unlink_pid # TODO: we only write the pid file if we daemonize, should we only unlink it if we daemonize?
329
357
 
330
358
  self.skip_ticks_for(stop_grace_time)
@@ -404,6 +432,7 @@ module Bluepill
404
432
  end
405
433
 
406
434
  def actual_pid=(pid)
435
+ ProcessJournal.append_pid_to_journal(name, pid) # be sure to always log the pid
407
436
  @actual_pid = pid
408
437
  end
409
438
 
@@ -412,8 +441,7 @@ module Bluepill
412
441
  end
413
442
 
414
443
  def unlink_pid
415
- File.unlink(pid_file) if pid_file && File.exists?(pid_file)
416
- rescue Errno::ENOENT
444
+ System.delete_if_exists(pid_file)
417
445
  end
418
446
 
419
447
  # Internal State Methods
@@ -440,6 +468,7 @@ module Bluepill
440
468
 
441
469
  # Construct a new process wrapper for each new found children
442
470
  new_children_pids.each do |child_pid|
471
+ ProcessJournal.append_pid_to_journal(name, child_pid)
443
472
  name = "<child(pid:#{child_pid})>"
444
473
  logger = self.logger.prefix_with(name)
445
474
 
@@ -6,7 +6,7 @@ module Bluepill
6
6
  @below = options[:below]
7
7
  end
8
8
 
9
- def run(pid)
9
+ def run(pid, include_children)
10
10
  1
11
11
  end
12
12
 
@@ -15,4 +15,4 @@ module Bluepill
15
15
  end
16
16
  end
17
17
  end
18
- end
18
+ end
@@ -6,9 +6,9 @@ module Bluepill
6
6
  @below = options[:below]
7
7
  end
8
8
 
9
- def run(pid)
9
+ def run(pid, include_children)
10
10
  # third col in the ps axu output
11
- System.cpu_usage(pid).to_f
11
+ System.cpu_usage(pid, include_children).to_f
12
12
  end
13
13
 
14
14
  def check(value)
@@ -16,4 +16,4 @@ module Bluepill
16
16
  end
17
17
  end
18
18
  end
19
- end
19
+ end
@@ -18,7 +18,7 @@ module Bluepill
18
18
  @read_timeout = (options[:read_timeout] || options[:timeout] || 5).to_i
19
19
  end
20
20
 
21
- def run(pid)
21
+ def run(pid, include_children)
22
22
  session = Net::HTTP.new(@uri.host, @uri.port)
23
23
  if @uri.scheme == 'https'
24
24
  require 'net/https'
@@ -11,9 +11,9 @@ module Bluepill
11
11
  @below = options[:below]
12
12
  end
13
13
 
14
- def run(pid)
14
+ def run(pid, include_children)
15
15
  # rss is on the 5th col
16
- System.memory_usage(pid).to_f
16
+ System.memory_usage(pid, include_children).to_f
17
17
  end
18
18
 
19
19
  def check(value)
@@ -29,4 +29,4 @@ module Bluepill
29
29
  end
30
30
  end
31
31
  end
32
- end
32
+ end
@@ -6,7 +6,7 @@ module Bluepill
6
6
  @options = options
7
7
  end
8
8
 
9
- def run(pid)
9
+ def run(pid, include_children)
10
10
  raise "Implement in subclass!"
11
11
  end
12
12
 
@@ -19,4 +19,4 @@ module Bluepill
19
19
  end
20
20
  end
21
21
  end
22
- end
22
+ end
@@ -0,0 +1,207 @@
1
+ require 'bluepill/system'
2
+
3
+ module Bluepill
4
+ module ProcessJournal
5
+ extend self
6
+
7
+ class << self
8
+ attr_reader :logger
9
+
10
+ def logger=(new_logger)
11
+ @logger ||= new_logger
12
+ end
13
+ end
14
+
15
+ def skip_pid?(pid)
16
+ !pid.is_a?(Integer) || pid <= 1
17
+ end
18
+
19
+ def skip_pgid?(pgid)
20
+ !pgid.is_a?(Integer) || pgid <= 1
21
+ end
22
+
23
+ # atomic operation on POSIX filesystems, since
24
+ # f.flock(File::LOCK_SH) is not available on all platforms
25
+ def acquire_atomic_fs_lock(name)
26
+ times = 0
27
+ name += '.lock'
28
+ Dir.mkdir name, 0700
29
+ logger.debug("Acquired lock #{name}")
30
+ yield
31
+ rescue Errno::EEXIST
32
+ times += 1
33
+ logger.debug("Waiting for lock #{name}")
34
+ sleep 1
35
+ unless times >= 10
36
+ retry
37
+ else
38
+ logger.info("Timeout waiting for lock #{name}")
39
+ raise "Timeout waiting for lock #{name}"
40
+ end
41
+ ensure
42
+ clear_atomic_fs_lock(name)
43
+ end
44
+
45
+ def clear_all_atomic_fs_locks
46
+ Dir['.*.lock'].each do |f|
47
+ System.delete_if_exists(f) if File.directory?(f)
48
+ end
49
+ end
50
+
51
+ def pid_journal_filename(journal_name)
52
+ ".bluepill_pids_journal.#{journal_name}"
53
+ end
54
+
55
+ def pgid_journal_filename(journal_name)
56
+ ".bluepill_pgids_journal.#{journal_name}"
57
+ end
58
+
59
+ def pid_journal(filename)
60
+ logger.debug("pid journal PWD=#{Dir.pwd}")
61
+ result = File.open(filename, 'r').readlines.map(&:to_i).reject {|pid| skip_pid?(pid)}
62
+ logger.debug("pid journal = #{result.join(' ')}")
63
+ result
64
+ rescue Errno::ENOENT
65
+ []
66
+ end
67
+
68
+ def pgid_journal(filename)
69
+ logger.debug("pgid journal PWD=#{Dir.pwd}")
70
+ result = File.open(filename, 'r').readlines.map(&:to_i).reject {|pgid| skip_pgid?(pgid)}
71
+ logger.debug("pgid journal = #{result.join(' ')}")
72
+ result
73
+ rescue Errno::ENOENT
74
+ []
75
+ end
76
+
77
+ def clear_atomic_fs_lock(name)
78
+ if File.directory?(name)
79
+ Dir.rmdir(name)
80
+ logger.debug("Cleared lock #{name}")
81
+ end
82
+ end
83
+
84
+ def kill_all_from_all_journals
85
+ Dir[".bluepill_pids_journal.*"].map { |x|
86
+ x.sub(/^\.bluepill_pids_journal\./,"")
87
+ }.reject { |y|
88
+ y =~ /\.lock$/
89
+ }.each do |journal_name|
90
+ kill_all_from_journal(journal_name)
91
+ end
92
+ end
93
+
94
+ def kill_all_from_journal(journal_name)
95
+ kill_all_pids_from_journal(journal_name)
96
+ kill_all_pgids_from_journal(journal_name)
97
+ end
98
+
99
+ def kill_all_pgids_from_journal(journal_name)
100
+ filename = pgid_journal_filename(journal_name)
101
+ j = pgid_journal(filename)
102
+ if j.length > 0
103
+ acquire_atomic_fs_lock(filename) do
104
+ j.each do |pgid|
105
+ begin
106
+ ::Process.kill('TERM', -pgid)
107
+ logger.info("Termed old process group #{pgid}")
108
+ rescue Errno::ESRCH
109
+ logger.debug("Unable to term missing process group #{pgid}")
110
+ end
111
+ end
112
+
113
+ if j.select { |pgid| System.pid_alive?(pgid) }.length > 1
114
+ sleep(1)
115
+ j.each do |pgid|
116
+ begin
117
+ ::Process.kill('KILL', -pgid)
118
+ logger.info("Killed old process group #{pgid}")
119
+ rescue Errno::ESRCH
120
+ logger.debug("Unable to kill missing process group #{pgid}")
121
+ end
122
+ end
123
+ end
124
+ System.delete_if_exists(filename) # reset journal
125
+ logger.debug('Journal cleanup completed')
126
+ end
127
+ else
128
+ logger.debug('No previous process journal - Skipping cleanup')
129
+ end
130
+ end
131
+
132
+ def kill_all_pids_from_journal(journal_name)
133
+ filename = pid_journal_filename(journal_name)
134
+ j = pid_journal(filename)
135
+ if j.length > 0
136
+ acquire_atomic_fs_lock(filename) do
137
+ j.each do |pid|
138
+ begin
139
+ ::Process.kill('TERM', pid)
140
+ logger.info("Termed old process #{pid}")
141
+ rescue Errno::ESRCH
142
+ logger.debug("Unable to term missing process #{pid}")
143
+ end
144
+ end
145
+
146
+ if j.select { |pid| System.pid_alive?(pid) }.length > 1
147
+ sleep(1)
148
+ j.each do |pid|
149
+ begin
150
+ ::Process.kill('KILL', pid)
151
+ logger.info("Killed old process #{pid}")
152
+ rescue Errno::ESRCH
153
+ logger.debug("Unable to kill missing process #{pid}")
154
+ end
155
+ end
156
+ end
157
+ System.delete_if_exists(filename) # reset journal
158
+ logger.debug('Journal cleanup completed')
159
+ end
160
+ else
161
+ logger.debug('No previous process journal - Skipping cleanup')
162
+ end
163
+ end
164
+
165
+ def append_pgid_to_journal(journal_name, pgid)
166
+ if skip_pgid?(pgid)
167
+ logger.debug("Skipping invalid pgid #{pgid} (class #{pgid.class})")
168
+ return
169
+ end
170
+
171
+ filename = pgid_journal_filename(journal_name)
172
+ acquire_atomic_fs_lock(filename) do
173
+ unless pgid_journal(filename).include?(pgid)
174
+ logger.debug("Saving pgid #{pgid} to process journal #{journal_name}")
175
+ File.open(filename, 'a+', 0600) { |f| f.puts(pgid) }
176
+ logger.info("Saved pgid #{pgid} to journal #{journal_name}")
177
+ logger.debug("Journal now = #{File.open(filename, 'r').read}")
178
+ else
179
+ logger.debug("Skipping duplicate pgid #{pgid} already in journal #{journal_name}")
180
+ end
181
+ end
182
+ end
183
+
184
+ def append_pid_to_journal(journal_name, pid)
185
+ begin
186
+ append_pgid_to_journal(journal_name, ::Process.getpgid(pid))
187
+ rescue Errno::ESRCH
188
+ end
189
+ if skip_pid?(pid)
190
+ logger.debug("Skipping invalid pid #{pid} (class #{pid.class})")
191
+ return
192
+ end
193
+
194
+ filename = pid_journal_filename(journal_name)
195
+ acquire_atomic_fs_lock(filename) do
196
+ unless pid_journal(filename).include?(pid)
197
+ logger.debug("Saving pid #{pid} to process journal #{journal_name}")
198
+ File.open(filename, 'a+', 0600) { |f| f.puts(pid) }
199
+ logger.info("Saved pid #{pid} to journal #{journal_name}")
200
+ logger.debug("Journal now = #{File.open(filename, 'r').read}")
201
+ else
202
+ logger.debug("Skipping duplicate pid #{pid} already in journal #{journal_name}")
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end
@@ -21,17 +21,31 @@ module Bluepill
21
21
  begin
22
22
  ::Process.kill(0, pid)
23
23
  true
24
+ rescue Errno::EPERM # no permission, but it is definitely alive
25
+ true
24
26
  rescue Errno::ESRCH
25
27
  false
26
28
  end
27
29
  end
28
30
 
29
- def cpu_usage(pid)
30
- ps_axu[pid] && ps_axu[pid][IDX_MAP[:pcpu]].to_f
31
+ def cpu_usage(pid, include_children)
32
+ ps = ps_axu
33
+ return unless ps[pid]
34
+ cpu_used = ps[pid][IDX_MAP[:pcpu]].to_f
35
+ get_children(pid).each { |child_pid|
36
+ cpu_used += ps[child_pid][IDX_MAP[:pcpu]].to_f if ps[child_pid]
37
+ } if include_children
38
+ cpu_used
31
39
  end
32
40
 
33
- def memory_usage(pid)
34
- ps_axu[pid] && ps_axu[pid][IDX_MAP[:rss]].to_f
41
+ def memory_usage(pid, include_children)
42
+ ps = ps_axu
43
+ return unless ps[pid]
44
+ mem_used = ps[pid][IDX_MAP[:rss]].to_f
45
+ get_children(pid).each { |child_pid|
46
+ mem_used += ps[child_pid][IDX_MAP[:rss]].to_f if ps[child_pid]
47
+ } if include_children
48
+ mem_used
35
49
  end
36
50
 
37
51
  def get_children(parent_pid)
@@ -39,7 +53,8 @@ module Bluepill
39
53
  ps_axu.each_pair do |pid, chunks|
40
54
  child_pids << chunks[IDX_MAP[:pid]].to_i if chunks[IDX_MAP[:ppid]].to_i == parent_pid.to_i
41
55
  end
42
- child_pids
56
+ grand_children = child_pids.map{|pid| get_children(pid)}.flatten
57
+ child_pids.concat grand_children
43
58
  end
44
59
 
45
60
  # Returns the pid of the child that executes the cmd
@@ -69,7 +84,7 @@ module Bluepill
69
84
 
70
85
  to_daemonize = lambda do
71
86
  # Setting end PWD env emulates bash behavior when dealing with symlinks
72
- Dir.chdir(ENV["PWD"] = options[:working_dir]) if options[:working_dir]
87
+ Dir.chdir(ENV["PWD"] = options[:working_dir].to_s) if options[:working_dir]
73
88
  options[:environment].each { |key, value| ENV[key.to_s] = value.to_s } if options[:environment]
74
89
 
75
90
  redirect_io(*options.values_at(:stdin, :stdout, :stderr))
@@ -89,6 +104,15 @@ module Bluepill
89
104
  end
90
105
  end
91
106
 
107
+ def delete_if_exists(filename)
108
+ tries = 0
109
+ File.unlink(filename) if filename && File.exists?(filename)
110
+ rescue IOError, Errno::ENOENT
111
+ rescue Errno::EACCES
112
+ retry if (tries += 1) < 3
113
+ $stderr.puts("Warning: permission denied trying to delete #{filename}")
114
+ end
115
+
92
116
  # Returns the stdout, stderr and exit code of the cmd
93
117
  def execute_blocking(cmd, options = {})
94
118
  rd, wr = IO.pipe
@@ -115,7 +139,7 @@ module Bluepill
115
139
  # grandchild
116
140
  drop_privileges(options[:uid], options[:gid], options[:supplementary_groups])
117
141
 
118
- Dir.chdir(ENV["PWD"] = options[:working_dir]) if options[:working_dir]
142
+ Dir.chdir(ENV["PWD"] = options[:working_dir].to_s) if options[:working_dir]
119
143
  options[:environment].each { |key, value| ENV[key.to_s] = value.to_s } if options[:environment]
120
144
 
121
145
  # close unused fds so ancestors wont hang. This line is the only reason we are not
@@ -1,4 +1,4 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  module Bluepill
3
- VERSION = "0.0.60".freeze
3
+ VERSION = "0.0.61".freeze
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bluepill
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.60
4
+ version: 0.0.61
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,25 +11,27 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2012-03-05 00:00:00.000000000 Z
14
+ date: 2013-03-18 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: daemons
18
- requirement: &2154561020 !ruby/object:Gem::Requirement
18
+ requirement: !ruby/object:Gem::Requirement
19
19
  none: false
20
20
  requirements:
21
21
  - - ~>
22
22
  - !ruby/object:Gem::Version
23
23
  version: 1.1.4
24
- - - <=
25
- - !ruby/object:Gem::Version
26
- version: 1.1.6
27
24
  type: :runtime
28
25
  prerelease: false
29
- version_requirements: *2154561020
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ~>
30
+ - !ruby/object:Gem::Version
31
+ version: 1.1.4
30
32
  - !ruby/object:Gem::Dependency
31
33
  name: state_machine
32
- requirement: &2154560280 !ruby/object:Gem::Requirement
34
+ requirement: !ruby/object:Gem::Requirement
33
35
  none: false
34
36
  requirements:
35
37
  - - ~>
@@ -37,10 +39,15 @@ dependencies:
37
39
  version: 1.1.0
38
40
  type: :runtime
39
41
  prerelease: false
40
- version_requirements: *2154560280
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 1.1.0
41
48
  - !ruby/object:Gem::Dependency
42
49
  name: activesupport
43
- requirement: &2154559820 !ruby/object:Gem::Requirement
50
+ requirement: !ruby/object:Gem::Requirement
44
51
  none: false
45
52
  requirements:
46
53
  - - ! '>='
@@ -48,10 +55,15 @@ dependencies:
48
55
  version: 3.0.0
49
56
  type: :runtime
50
57
  prerelease: false
51
- version_requirements: *2154559820
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: 3.0.0
52
64
  - !ruby/object:Gem::Dependency
53
65
  name: i18n
54
- requirement: &2154559340 !ruby/object:Gem::Requirement
66
+ requirement: !ruby/object:Gem::Requirement
55
67
  none: false
56
68
  requirements:
57
69
  - - ! '>='
@@ -59,10 +71,15 @@ dependencies:
59
71
  version: 0.5.0
60
72
  type: :runtime
61
73
  prerelease: false
62
- version_requirements: *2154559340
74
+ version_requirements: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: 0.5.0
63
80
  - !ruby/object:Gem::Dependency
64
81
  name: bundler
65
- requirement: &2154558840 !ruby/object:Gem::Requirement
82
+ requirement: !ruby/object:Gem::Requirement
66
83
  none: false
67
84
  requirements:
68
85
  - - ! '>='
@@ -70,10 +87,15 @@ dependencies:
70
87
  version: 1.0.10
71
88
  type: :development
72
89
  prerelease: false
73
- version_requirements: *2154558840
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: 1.0.10
74
96
  - !ruby/object:Gem::Dependency
75
97
  name: rake
76
- requirement: &2154558280 !ruby/object:Gem::Requirement
98
+ requirement: !ruby/object:Gem::Requirement
77
99
  none: false
78
100
  requirements:
79
101
  - - ! '!='
@@ -81,10 +103,15 @@ dependencies:
81
103
  version: 0.9.0
82
104
  type: :development
83
105
  prerelease: false
84
- version_requirements: *2154558280
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ! '!='
110
+ - !ruby/object:Gem::Version
111
+ version: 0.9.0
85
112
  - !ruby/object:Gem::Dependency
86
113
  name: rspec-core
87
- requirement: &2154557740 !ruby/object:Gem::Requirement
114
+ requirement: !ruby/object:Gem::Requirement
88
115
  none: false
89
116
  requirements:
90
117
  - - ~>
@@ -92,10 +119,15 @@ dependencies:
92
119
  version: '2.0'
93
120
  type: :development
94
121
  prerelease: false
95
- version_requirements: *2154557740
122
+ version_requirements: !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ~>
126
+ - !ruby/object:Gem::Version
127
+ version: '2.0'
96
128
  - !ruby/object:Gem::Dependency
97
129
  name: rspec-expectations
98
- requirement: &2154544000 !ruby/object:Gem::Requirement
130
+ requirement: !ruby/object:Gem::Requirement
99
131
  none: false
100
132
  requirements:
101
133
  - - ~>
@@ -103,10 +135,15 @@ dependencies:
103
135
  version: '2.0'
104
136
  type: :development
105
137
  prerelease: false
106
- version_requirements: *2154544000
138
+ version_requirements: !ruby/object:Gem::Requirement
139
+ none: false
140
+ requirements:
141
+ - - ~>
142
+ - !ruby/object:Gem::Version
143
+ version: '2.0'
107
144
  - !ruby/object:Gem::Dependency
108
145
  name: rr
109
- requirement: &2154543540 !ruby/object:Gem::Requirement
146
+ requirement: !ruby/object:Gem::Requirement
110
147
  none: false
111
148
  requirements:
112
149
  - - ~>
@@ -114,10 +151,15 @@ dependencies:
114
151
  version: '1.0'
115
152
  type: :development
116
153
  prerelease: false
117
- version_requirements: *2154543540
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ none: false
156
+ requirements:
157
+ - - ~>
158
+ - !ruby/object:Gem::Version
159
+ version: '1.0'
118
160
  - !ruby/object:Gem::Dependency
119
161
  name: faker
120
- requirement: &2154543080 !ruby/object:Gem::Requirement
162
+ requirement: !ruby/object:Gem::Requirement
121
163
  none: false
122
164
  requirements:
123
165
  - - ~>
@@ -125,10 +167,15 @@ dependencies:
125
167
  version: '0.9'
126
168
  type: :development
127
169
  prerelease: false
128
- version_requirements: *2154543080
170
+ version_requirements: !ruby/object:Gem::Requirement
171
+ none: false
172
+ requirements:
173
+ - - ~>
174
+ - !ruby/object:Gem::Version
175
+ version: '0.9'
129
176
  - !ruby/object:Gem::Dependency
130
177
  name: yard
131
- requirement: &2154542580 !ruby/object:Gem::Requirement
178
+ requirement: !ruby/object:Gem::Requirement
132
179
  none: false
133
180
  requirements:
134
181
  - - ~>
@@ -136,7 +183,12 @@ dependencies:
136
183
  version: '0.7'
137
184
  type: :development
138
185
  prerelease: false
139
- version_requirements: *2154542580
186
+ version_requirements: !ruby/object:Gem::Requirement
187
+ none: false
188
+ requirements:
189
+ - - ~>
190
+ - !ruby/object:Gem::Version
191
+ version: '0.7'
140
192
  description: Bluepill keeps your daemons up while taking up as little resources as
141
193
  possible. After all you probably want the resources of your server to be used by
142
194
  whatever daemons you are running rather than the thing that's supposed to make sure
@@ -186,6 +238,7 @@ files:
186
238
  - lib/bluepill/process_conditions/http.rb
187
239
  - lib/bluepill/process_conditions/mem_usage.rb
188
240
  - lib/bluepill/process_conditions/process_condition.rb
241
+ - lib/bluepill/process_journal.rb
189
242
  - lib/bluepill/process_statistics.rb
190
243
  - lib/bluepill/socket.rb
191
244
  - lib/bluepill/system.rb
@@ -218,7 +271,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
218
271
  version: '0'
219
272
  requirements: []
220
273
  rubyforge_project:
221
- rubygems_version: 1.8.10
274
+ rubygems_version: 1.8.24
222
275
  signing_key:
223
276
  specification_version: 3
224
277
  summary: A process monitor written in Ruby with stability and minimalism in mind.