bluepill 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +3 -0
- data/VERSION +1 -1
- data/bluepill.gemspec +3 -2
- data/lib/bluepill/application.rb +5 -5
- data/lib/bluepill/process.rb +25 -28
- data/lib/bluepill/system.rb +111 -9
- data/lib/example.rb +12 -7
- metadata +3 -3
data/Rakefile
CHANGED
@@ -15,6 +15,9 @@ begin
|
|
15
15
|
gem.add_dependency("daemons", ">= 1.0.9")
|
16
16
|
gem.add_dependency("pluginaweek-state_machine", ">= 0.8.0")
|
17
17
|
gem.add_dependency("activesupport", ">= 2.3.4")
|
18
|
+
|
19
|
+
gem.files -= ["bin/sample_forking_server"]
|
20
|
+
gem.executables = ["bluepill"]
|
18
21
|
end
|
19
22
|
rescue LoadError
|
20
23
|
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.7
|
data/bluepill.gemspec
CHANGED
@@ -5,11 +5,12 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{bluepill}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.7"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Arya Asemanfar", "Gary Tsang", "Rohith Ravi"]
|
12
|
-
s.date = %q{2009-10-
|
12
|
+
s.date = %q{2009-10-23}
|
13
|
+
s.default_executable = %q{bluepill}
|
13
14
|
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.}
|
14
15
|
s.email = %q{entombedvirus@gmail.com}
|
15
16
|
s.executables = ["bluepill"]
|
data/lib/bluepill/application.rb
CHANGED
@@ -21,7 +21,7 @@ module Bluepill
|
|
21
21
|
start_server
|
22
22
|
rescue StandardError => e
|
23
23
|
logger.err("Got exception: %s `%s`" % [e.class.name, e.message])
|
24
|
-
|
24
|
+
e.backtrace.each {|l| logger.err(l)}
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -32,7 +32,7 @@ module Bluepill
|
|
32
32
|
|
33
33
|
if self.groups.has_key?(nil)
|
34
34
|
self.groups[nil].processes.each do |p|
|
35
|
-
buffer << "%s%s: %s" % [" " * depth, p.name, p.state]
|
35
|
+
buffer << "%s%s(pid:%s): %s" % [" " * depth, p.name, p.actual_pid.inspect, p.state]
|
36
36
|
|
37
37
|
if p.monitor_children?
|
38
38
|
depth += 2
|
@@ -52,7 +52,7 @@ module Bluepill
|
|
52
52
|
group.processes.each do |p|
|
53
53
|
depth += 2
|
54
54
|
|
55
|
-
buffer << "%s%s(pid:%
|
55
|
+
buffer << "%s%s(pid:%s): %s" % [" " * depth, p.name, p.actual_pid.inspect, p.state]
|
56
56
|
|
57
57
|
if p.monitor_children?
|
58
58
|
depth += 2
|
@@ -149,8 +149,8 @@ private
|
|
149
149
|
client.close
|
150
150
|
end
|
151
151
|
rescue StandardError => e
|
152
|
-
logger.err(e.
|
153
|
-
|
152
|
+
logger.err("Got exception in cmd listener: %s `%s`" % [e.class.name, e.message])
|
153
|
+
e.backtrace.each {|l| logger.err(l)}
|
154
154
|
end
|
155
155
|
end
|
156
156
|
end
|
data/lib/bluepill/process.rb
CHANGED
@@ -230,13 +230,16 @@ module Bluepill
|
|
230
230
|
logger.warning "Executing start command: #{start_command}"
|
231
231
|
|
232
232
|
if self.daemonize?
|
233
|
-
|
234
|
-
child_pid = Daemonize.call_as_daemon(starter)
|
233
|
+
child_pid = System.daemonize(start_command, :uid => self.uid, :gid => self.gid)
|
235
234
|
File.open(pid_file, "w") {|f| f.write(child_pid)}
|
235
|
+
|
236
236
|
else
|
237
237
|
# This is a self-daemonizing process
|
238
|
-
|
239
|
-
|
238
|
+
result = System.execute_blocking(start_command, :uid => self.uid, :gid => self.gid, :logger => logger)
|
239
|
+
|
240
|
+
unless result[:exit_code].zero?
|
241
|
+
logger.warning "Start command execution returned non-zero exit code:"
|
242
|
+
logger.warning result.inspect
|
240
243
|
end
|
241
244
|
end
|
242
245
|
|
@@ -247,9 +250,11 @@ module Bluepill
|
|
247
250
|
if stop_command
|
248
251
|
cmd = stop_command.to_s.gsub("{{PID}}", actual_pid.to_s)
|
249
252
|
logger.warning "Executing stop command: #{cmd}"
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
+
|
254
|
+
result = System.execute_blocking(cmd, :uid => self.uid, :gid => self.gid)
|
255
|
+
unless result[:exit_code].zero?
|
256
|
+
logger.warning "Stop command execution returned non-zero exit code:"
|
257
|
+
logger.warning result.inspect
|
253
258
|
end
|
254
259
|
|
255
260
|
else
|
@@ -265,8 +270,10 @@ module Bluepill
|
|
265
270
|
if restart_command
|
266
271
|
logger.warning "Executing restart command: #{restart_command}"
|
267
272
|
|
268
|
-
|
269
|
-
|
273
|
+
result = System.execute_blocking(restart_command, :uid => self.uid, :gid => self.gid)
|
274
|
+
unless result[:exit_code].zero?
|
275
|
+
logger.warning "Restart command execution returned non-zero exit code:"
|
276
|
+
logger.warning result.inspect
|
270
277
|
end
|
271
278
|
|
272
279
|
self.skip_ticks_for(restart_grace_time)
|
@@ -294,7 +301,14 @@ module Bluepill
|
|
294
301
|
|
295
302
|
def actual_pid
|
296
303
|
@actual_pid ||= begin
|
297
|
-
|
304
|
+
if pid_file
|
305
|
+
if File.exists?(pid_file)
|
306
|
+
File.read(pid_file).to_i
|
307
|
+
else
|
308
|
+
logger.warning("pid_file #{pid_file} does not exist or cannot be read")
|
309
|
+
nil
|
310
|
+
end
|
311
|
+
end
|
298
312
|
end
|
299
313
|
end
|
300
314
|
|
@@ -310,24 +324,7 @@ module Bluepill
|
|
310
324
|
File.unlink(pid_file) if pid_file && File.exists?(pid_file)
|
311
325
|
end
|
312
326
|
|
313
|
-
|
314
|
-
begin
|
315
|
-
require 'etc'
|
316
|
-
|
317
|
-
uid_num = Etc.getpwnam(self.uid).uid if self.uid
|
318
|
-
gid_num = Etc.getgrnam(self.gid).gid if self.gid
|
319
|
-
|
320
|
-
::Process.groups = [gid_num] if self.gid
|
321
|
-
::Process::Sys.setgid(gid_num) if self.gid
|
322
|
-
::Process::Sys.setuid(uid_num) if self.uid
|
323
|
-
rescue ArgumentError, Errno::EPERM, Errno::ENOENT => e
|
324
|
-
# TODO: log exceptions elsewhere
|
325
|
-
File.open("/tmp/exception.log", "w+"){|f| puts e}
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
329
|
-
|
330
|
-
# Internal State Methods
|
327
|
+
# Internal State Methods
|
331
328
|
def skip_ticks_for(seconds)
|
332
329
|
# TODO: should this be addative or longest wins?
|
333
330
|
# i.e. if two calls for skip_ticks_for come in for 5 and 10, should it skip for 10 or 15?
|
data/lib/bluepill/system.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'etc'
|
2
|
+
|
1
3
|
module Bluepill
|
2
4
|
# This class represents the system that bluepill is running on.. It's mainly used to memoize
|
3
5
|
# results of running ps auxx etc so that every watch in the every process will not result in a fork
|
@@ -28,21 +30,110 @@ module Bluepill
|
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
# Returns the pid of the child that executes the cmd
|
34
|
+
def daemonize(cmd, options = {})
|
35
|
+
rd, wr = IO.pipe
|
36
|
+
|
37
|
+
if child = Daemonize.safefork
|
38
|
+
# we do not wanna create zombies, so detach ourselves from the child exit status
|
39
|
+
::Process.detach(child)
|
40
|
+
|
41
|
+
# parent
|
42
|
+
wr.close
|
43
|
+
|
44
|
+
daemon_id = rd.read.to_i
|
45
|
+
rd.close
|
46
|
+
|
47
|
+
return daemon_id if daemon_id > 0
|
35
48
|
|
36
49
|
else
|
37
|
-
#
|
38
|
-
|
39
|
-
|
50
|
+
# child
|
51
|
+
rd.close
|
52
|
+
|
53
|
+
drop_privileges(options[:uid], options[:gid])
|
54
|
+
|
55
|
+
daemon_id = Daemonize.call_as_daemon(lambda { ::Kernel.exec(cmd); exit }, nil, cmd)
|
56
|
+
|
57
|
+
wr.write daemon_id
|
58
|
+
wr.close
|
59
|
+
|
40
60
|
exit
|
41
61
|
end
|
42
62
|
end
|
43
63
|
|
44
|
-
|
45
|
-
|
64
|
+
# Returns the stdout, stderr and exit code of the cmd
|
65
|
+
def execute_blocking(cmd, options = {})
|
66
|
+
rd, wr = IO.pipe
|
67
|
+
|
68
|
+
if child = Daemonize.safefork
|
69
|
+
# parent
|
70
|
+
wr.close
|
71
|
+
|
72
|
+
cmd_status = rd.read
|
73
|
+
rd.close
|
74
|
+
|
75
|
+
::Process.waitpid(child)
|
76
|
+
|
77
|
+
return Marshal.load(cmd_status)
|
78
|
+
|
79
|
+
else
|
80
|
+
# child
|
81
|
+
rd.close
|
82
|
+
|
83
|
+
# create a child in which we can override the stdin, stdout and stderr
|
84
|
+
cmd_out_read, cmd_out_write = IO.pipe
|
85
|
+
cmd_err_read, cmd_err_write = IO.pipe
|
86
|
+
|
87
|
+
pid = fork {
|
88
|
+
# grandchild
|
89
|
+
drop_privileges(options[:uid], options[:gid])
|
90
|
+
|
91
|
+
# close unused fds so ancestors wont hang. This line is the only reason we are not
|
92
|
+
# using something like popen3. If this fd is not closed, the .read call on the parent
|
93
|
+
# will never return because "wr" would still be open in the "exec"-ed cmd
|
94
|
+
wr.close
|
95
|
+
|
96
|
+
# we do not care about stdin of cmd
|
97
|
+
STDIN.reopen("/dev/null")
|
98
|
+
|
99
|
+
# point stdout of cmd to somewhere we can read
|
100
|
+
cmd_out_read.close
|
101
|
+
STDOUT.reopen(cmd_out_write)
|
102
|
+
cmd_out_write.close
|
103
|
+
|
104
|
+
# same thing for stderr
|
105
|
+
cmd_err_read.close
|
106
|
+
STDERR.reopen(cmd_err_write)
|
107
|
+
cmd_err_write.close
|
108
|
+
|
109
|
+
# finally, replace grandchild with cmd
|
110
|
+
::Kernel.exec(cmd)
|
111
|
+
}
|
112
|
+
|
113
|
+
# we do not use these ends of the pipes in the child
|
114
|
+
cmd_out_write.close
|
115
|
+
cmd_err_write.close
|
116
|
+
|
117
|
+
# wait for the cmd to finish executing and acknowledge it's death
|
118
|
+
::Process.waitpid(pid)
|
119
|
+
|
120
|
+
# collect stdout, stderr and exitcode
|
121
|
+
result = {
|
122
|
+
:stdout => cmd_out_read.read,
|
123
|
+
:stderr => cmd_err_read.read,
|
124
|
+
:exit_code => $?.exitstatus
|
125
|
+
}
|
126
|
+
|
127
|
+
# We're done with these ends of the pipes as well
|
128
|
+
cmd_out_read.close
|
129
|
+
cmd_err_read.close
|
130
|
+
|
131
|
+
# Time to tell the parent about what went down
|
132
|
+
wr.write Marshal.dump(result)
|
133
|
+
wr.close
|
134
|
+
|
135
|
+
exit
|
136
|
+
end
|
46
137
|
end
|
47
138
|
|
48
139
|
def store
|
@@ -68,5 +159,16 @@ module Bluepill
|
|
68
159
|
end
|
69
160
|
end
|
70
161
|
end
|
162
|
+
|
163
|
+
# be sure to call this from a fork otherwise it will modify the attributes
|
164
|
+
# of the bluepill daemon
|
165
|
+
def drop_privileges(uid, gid)
|
166
|
+
uid_num = Etc.getpwnam(uid).uid if uid
|
167
|
+
gid_num = Etc.getgrnam(gid).gid if gid
|
168
|
+
|
169
|
+
::Process.groups = [gid_num] if gid
|
170
|
+
::Process::Sys.setgid(gid_num) if gid
|
171
|
+
::Process::Sys.setuid(uid_num) if uid
|
172
|
+
end
|
71
173
|
end
|
72
174
|
end
|
data/lib/example.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'bluepill'
|
3
|
+
require 'logger'
|
3
4
|
|
4
5
|
ROOT_DIR = "/tmp/bp"
|
5
6
|
|
6
7
|
# Watch with
|
7
8
|
# watch -n0.2 'ps axu | egrep "(CPU|forking|bluepill|sleep)" | grep -v grep | sort'
|
8
9
|
Bluepill.application(:sample_app) do |app|
|
9
|
-
|
10
|
+
0.times do |i|
|
10
11
|
app.process("process_#{i}") do |process|
|
11
12
|
process.pid_file = "#{ROOT_DIR}/pids/process_#{i}.pid"
|
12
13
|
|
@@ -21,7 +22,7 @@ Bluepill.application(:sample_app) do |app|
|
|
21
22
|
process.restart_grace_time = 7.seconds
|
22
23
|
process.stop_grace_time = 7.seconds
|
23
24
|
|
24
|
-
process.uid = "
|
25
|
+
process.uid = "rohith"
|
25
26
|
process.gid = "staff"
|
26
27
|
|
27
28
|
# process.checks :cpu_usage, :every => 10, :below => 0.5, :times => [5, 5]
|
@@ -45,14 +46,18 @@ Bluepill.application(:sample_app) do |app|
|
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
48
|
-
|
49
|
+
1.times do |i|
|
49
50
|
app.process("group_process_#{i}") do |process|
|
50
|
-
process.start_command = "sleep #{rand(30) + i}"
|
51
51
|
process.group = "Poopfaced"
|
52
|
-
process.
|
53
|
-
process.
|
52
|
+
process.pid_file = "/Users/rohith/ffs/tmp/pids/mongrel_#{i}.pid"
|
53
|
+
process.start_command = "cd ~/ffs && mongrel_rails start -P #{process.pid_file} -p 3000 -d"
|
54
54
|
|
55
|
-
process.
|
55
|
+
process.start_grace_time = 10.seconds
|
56
|
+
|
57
|
+
process.uid = "rohith"
|
58
|
+
process.gid = "staff"
|
59
|
+
|
60
|
+
# process.checks :always_true, :every => 10
|
56
61
|
end
|
57
62
|
end
|
58
63
|
|
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.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arya Asemanfar
|
@@ -11,8 +11,8 @@ autorequire:
|
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
13
|
|
14
|
-
date: 2009-10-
|
15
|
-
default_executable:
|
14
|
+
date: 2009-10-23 00:00:00 -07:00
|
15
|
+
default_executable: bluepill
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: rspec
|