slave 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/gemspec.rb ADDED
@@ -0,0 +1,23 @@
1
+ lib, version = File::basename(File::dirname(File::expand_path(__FILE__))).split %r/-/, 2
2
+
3
+ require 'rubygems'
4
+
5
+ Gem::Specification::new do |spec|
6
+ spec.name = lib
7
+ spec.version = version
8
+ spec.platform = Gem::Platform::RUBY
9
+ spec.summary = lib
10
+
11
+ spec.files = Dir::glob "**/**"
12
+ spec.executables = Dir::glob("bin/*").map{|exe| File::basename exe}
13
+
14
+ spec.require_path = "lib"
15
+ spec.autorequire = lib
16
+
17
+ spec.has_rdoc = File::exist? "doc"
18
+ spec.test_suite_file = "test/#{ lib }.rb" if File::directory? "test"
19
+
20
+ spec.author = "Ara T. Howard"
21
+ spec.email = "ara.t.howard@noaa.gov"
22
+ spec.homepage = "http://codeforpeople.com/lib/ruby/#{ lib }/"
23
+ end
data/install.rb ADDED
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rbconfig'
3
+ require 'find'
4
+ require 'ftools'
5
+ require 'tempfile'
6
+ include Config
7
+
8
+ LIBDIR = "lib"
9
+ LIBDIR_MODE = 0644
10
+
11
+ BINDIR = "bin"
12
+ BINDIR_MODE = 0755
13
+
14
+
15
+ $srcdir = CONFIG["srcdir"]
16
+ $version = CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
17
+ $libdir = File.join(CONFIG["libdir"], "ruby", $version)
18
+ $archdir = File.join($libdir, CONFIG["arch"])
19
+ $site_libdir = $:.find {|x| x =~ /site_ruby$/}
20
+ $bindir = CONFIG["bindir"] || CONFIG['BINDIR']
21
+ $ruby_install_name = CONFIG['ruby_install_name'] || CONFIG['RUBY_INSTALL_NAME'] || 'ruby'
22
+ $ruby_ext = CONFIG['EXEEXT'] || ''
23
+ $ruby = File.join($bindir, ($ruby_install_name + $ruby_ext))
24
+
25
+ if !$site_libdir
26
+ $site_libdir = File.join($libdir, "site_ruby")
27
+ elsif $site_libdir !~ %r/#{Regexp.quote($version)}/
28
+ $site_libdir = File.join($site_libdir, $version)
29
+ end
30
+
31
+ def install_rb(srcdir=nil, destdir=nil, mode=nil, bin=nil)
32
+ #{{{
33
+ path = []
34
+ dir = []
35
+ Find.find(srcdir) do |f|
36
+ next unless FileTest.file?(f)
37
+ next if (f = f[srcdir.length+1..-1]) == nil
38
+ next if (/CVS$/ =~ File.dirname(f))
39
+ next if f =~ %r/\.lnk/
40
+ path.push f
41
+ dir |= [File.dirname(f)]
42
+ end
43
+ for f in dir
44
+ next if f == "."
45
+ next if f == "CVS"
46
+ File::makedirs(File.join(destdir, f))
47
+ end
48
+ for f in path
49
+ next if (/\~$/ =~ f)
50
+ next if (/^\./ =~ File.basename(f))
51
+ unless bin
52
+ File::install(File.join(srcdir, f), File.join(destdir, f), mode, true)
53
+ else
54
+ from = File.join(srcdir, f)
55
+ to = File.join(destdir, f)
56
+ shebangify(from) do |sf|
57
+ $deferr.print from, " -> ", File::catname(from, to), "\n"
58
+ $deferr.printf "chmod %04o %s\n", mode, to
59
+ File::install(sf, to, mode, false)
60
+ end
61
+ end
62
+ end
63
+ #}}}
64
+ end
65
+ def shebangify f
66
+ #{{{
67
+ open(f) do |fd|
68
+ buf = fd.read 42
69
+ if buf =~ %r/^\s*#\s*!.*ruby/o
70
+ ftmp = Tempfile::new("#{ $$ }_#{ File::basename(f) }")
71
+ begin
72
+ fd.rewind
73
+ ftmp.puts "#!#{ $ruby }"
74
+ while((buf = fd.read(8192)))
75
+ ftmp.write buf
76
+ end
77
+ ftmp.close
78
+ yield ftmp.path
79
+ ensure
80
+ ftmp.close!
81
+ end
82
+ else
83
+ yield f
84
+ end
85
+ end
86
+ #}}}
87
+ end
88
+ def ARGV.switch
89
+ #{{{
90
+ return nil if self.empty?
91
+ arg = self.shift
92
+ return nil if arg == '--'
93
+ if arg =~ /^-(.)(.*)/
94
+ return arg if $1 == '-'
95
+ raise 'unknown switch "-"' if $2.index('-')
96
+ self.unshift "-#{$2}" if $2.size > 0
97
+ "-#{$1}"
98
+ else
99
+ self.unshift arg
100
+ nil
101
+ end
102
+ #}}}
103
+ end
104
+ def ARGV.req_arg
105
+ #{{{
106
+ self.shift || raise('missing argument')
107
+ #}}}
108
+ end
109
+ def linkify d, linked = []
110
+ #--{{{
111
+ if test ?d, d
112
+ versioned = Dir[ File::join(d, "*-[0-9].[0-9].[0-9].rb") ]
113
+ versioned.each do |v|
114
+ src, dst = v, v.gsub(%r/\-[\d\.]+\.rb$/, '.rb')
115
+ lnk = nil
116
+ begin
117
+ if test ?l, dst
118
+ lnk = "#{ dst }.lnk"
119
+ puts "#{ dst } -> #{ lnk }"
120
+ File::rename dst, lnk
121
+ end
122
+ unless test ?e, dst
123
+ puts "#{ src } -> #{ dst }"
124
+ File::copy src, dst
125
+ linked << dst
126
+ end
127
+ ensure
128
+ if lnk
129
+ at_exit do
130
+ puts "#{ lnk } -> #{ dst }"
131
+ File::rename lnk, dst
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+ linked
138
+ #--}}}
139
+ end
140
+
141
+
142
+ #
143
+ # main program
144
+ #
145
+
146
+ libdir = $site_libdir
147
+ bindir = $bindir
148
+ no_linkify = false
149
+ linked = nil
150
+ help = false
151
+
152
+ usage = <<-usage
153
+ #{ File::basename $0 }
154
+ -d, --destdir <destdir>
155
+ -l, --libdir <libdir>
156
+ -b, --bindir <bindir>
157
+ -r, --ruby <ruby>
158
+ -n, --no_linkify
159
+ -h, --help
160
+ usage
161
+
162
+ begin
163
+ while switch = ARGV.switch
164
+ case switch
165
+ when '-d', '--destdir'
166
+ libdir = ARGV.req_arg
167
+ when '-l', '--libdir'
168
+ libdir = ARGV.req_arg
169
+ when '-b', '--bindir'
170
+ bindir = ARGV.req_arg
171
+ when '-r', '--ruby'
172
+ $ruby = ARGV.req_arg
173
+ when '-n', '--no_linkify'
174
+ no_linkify = true
175
+ when '-h', '--help'
176
+ help = true
177
+ else
178
+ raise "unknown switch #{switch.dump}"
179
+ end
180
+ end
181
+ rescue
182
+ STDERR.puts $!.to_s
183
+ STDERR.puts usage
184
+ exit 1
185
+ end
186
+
187
+ if help
188
+ STDOUT.puts usage
189
+ exit
190
+ end
191
+
192
+ unless no_linkify
193
+ linked = linkify('lib') + linkify('bin')
194
+ end
195
+
196
+ install_rb(LIBDIR, libdir, LIBDIR_MODE)
197
+ install_rb(BINDIR, bindir, BINDIR_MODE, bin=true)
198
+
199
+ if linked
200
+ linked.each{|path| File::rm_f path}
201
+ end
@@ -0,0 +1,292 @@
1
+ require 'drb/drb'
2
+ require 'fileutils'
3
+ require 'tmpdir'
4
+ require 'tempfile'
5
+
6
+ #
7
+ # the Slave class encapsulates the work of setting up a drb server in another
8
+ # process.
9
+ #
10
+ class Slave
11
+ #--{{{
12
+ DEFAULT_SOCKET_CREATION_ATTEMPTS =
13
+ Integer(ENV['SLAVE_SOCKET_CREATION_ATTEMPTS'] || 42)
14
+
15
+ DEFAULT_PULSE_RATE =
16
+ Float(ENV['SLAVE_PULSE_RATE'] || 8)
17
+
18
+ DEFAULT_DEBUG =
19
+ (ENV['SLAVE_DEBUG'] ? true : false)
20
+
21
+ @socket_creation_attempts = DEFAULT_SOCKET_CREATION_ATTEMPTS
22
+ @pulse_rate = DEFAULT_PULSE_RATE
23
+ @debug = DEFAULT_DEBUG
24
+
25
+ class << self
26
+ #--{{{
27
+ # defineds how many attempts will be made to create a temporary unix domain
28
+ # socket
29
+ attr :socket_creation_attempts, true
30
+
31
+ # defined the rate of pinging in the Heartbeat object
32
+ attr :pulse_rate, true
33
+
34
+ # if this is true and you are running from a terminal information is printed
35
+ # on STDERR
36
+ attr :debug, true
37
+
38
+ # look up a value in an option hash failing back to class defaults
39
+ def getval key, opts = {}
40
+ #--{{{
41
+ keys = [key, key.to_s, key.to_s.intern]
42
+ keys.each{|k| return opts[k] if opts.has_key?(k)}
43
+ send key rescue nil
44
+ #--}}}
45
+ end
46
+ # just fork with out silly warnings
47
+ def fork &block
48
+ #--{{{
49
+ v = $VERBOSE
50
+ begin
51
+ $VERBOSE = nil
52
+ Process::fork &block
53
+ ensure
54
+ $VERBOSE = v
55
+ end
56
+ #--}}}
57
+ end
58
+ #--}}}
59
+ end
60
+
61
+ attr :object
62
+ attr :obj
63
+ attr :psname
64
+ attr :pid
65
+ attr :ppid
66
+ attr :uri
67
+ attr :pulse_rate
68
+ attr :socket
69
+ attr :debug
70
+
71
+ # 'obj' can be any object and 'opts' may contain the keys
72
+ # 'socket_creation_attempts', 'pulse_rate', 'psname', or 'debug'
73
+ def initialize obj, opts = {}
74
+ #--{{{
75
+ @obj = obj
76
+
77
+ @socket_creation_attempts = getval('socket_creation_attempts', opts)
78
+ @pulse_rate = getval('pulse_rate', opts)
79
+ @debug = getval('debug', opts)
80
+ @psname = getval('psname', opts) || gen_psname(@obj)
81
+
82
+ trace{ "socket_creation_attempts <#{ @socket_creation_attempts }>" }
83
+ trace{ "pulse_rate <#{ @pulse_rate }>" }
84
+ trace{ "psname <#{ @psname }>" }
85
+
86
+ @shutdown = false
87
+
88
+ @heartbeat = Heartbeat::new @pulse_rate, @debug
89
+ @r, @w = IO::pipe
90
+ #
91
+ # child
92
+ #
93
+ unless((@pid = Slave::fork))
94
+ begin
95
+ $0 = @psname
96
+ @pid = Process::pid
97
+ @ppid = Process::ppid
98
+
99
+ @r.close
100
+ @socket = nil
101
+ @uri = nil
102
+
103
+ tmpdir = Dir::tmpdir
104
+ basename = File::basename @psname
105
+
106
+ @socket_creation_attempts.times do |attempt|
107
+ begin
108
+ s = File::join(tmpdir, "#{ basename }_#{ attempt }")
109
+ u = "drbunix://#{ s }"
110
+ DRb::start_service u, obj
111
+ @socket = s
112
+ @uri = u
113
+ trace{ "child - socket <#{ @socket }>" }
114
+ trace{ "child - uri <#{ @uri }>" }
115
+ break
116
+ rescue Errno::EADDRINUSE
117
+ nil
118
+ end
119
+ end
120
+
121
+ if @socket and @uri
122
+ @heartbeat.start
123
+ @w.write @socket
124
+ @w.close
125
+ trap('SIGUSR2') do
126
+ @heartbeat.stop rescue nil
127
+ DBb::thread.kill rescue nil
128
+ FileUtils::rm_f @socket rescue nil
129
+ exit!
130
+ end
131
+ DRb::thread.join
132
+ else
133
+ @w.close
134
+ end
135
+ rescue => e
136
+ trace{ %Q[#{ e.message } (#{ e.class })\n#{ e.backtrace.join "\n" }] }
137
+ ensure
138
+ exit!
139
+ end
140
+ #
141
+ # parent
142
+ #
143
+ else
144
+ Process::detach @pid
145
+ @w.close
146
+ @socket = @r.read
147
+ @r.close
148
+
149
+ trace{ "parent - socket <#{ @socket }>" }
150
+
151
+ if @socket and File::exist? @socket
152
+ at_exit{ FileUtils::rm_f @socket }
153
+ @uri = "drbunix://#{ socket }"
154
+ trace{ "parent - uri <#{ @uri }>" }
155
+ @heartbeat.start
156
+ #
157
+ # starting drb on localhost avoids dns lookups!
158
+ #
159
+ DRb::start_service('druby://localhost:0', nil) unless DRb::thread
160
+ @object = DRbObject::new nil, @uri
161
+ else
162
+ raise "failed to find slave socket <#{ @socket }>"
163
+ end
164
+ end
165
+ #--}}}
166
+ end
167
+ # stops the heartbeat thread and kills the child process
168
+ def shutdown
169
+ #--{{{
170
+ raise "already shutdown" if @shutdown
171
+ @heartbeat.stop rescue nil
172
+ Process::kill('SIGUSR2', @pid) rescue nil
173
+ Process::kill('SIGTERM', @pid) rescue nil
174
+ FileUtils::rm_f @socket
175
+ @shutdown = true
176
+ #--}}}
177
+ end
178
+ # generate a default name to appear in ps/top
179
+ def gen_psname obj
180
+ #--{{{
181
+ "#{ obj.class }_slave_of_#{ Process::pid }".downcase
182
+ #--}}}
183
+ end
184
+ # see docs for class.getval
185
+ def getval key, opts = {}
186
+ #--{{{
187
+ self.class.getval key
188
+ #--}}}
189
+ end
190
+ # debugging output
191
+ def trace
192
+ #--{{{
193
+ STDERR.puts(yield) if @debug and STDERR.tty?
194
+ #--}}}
195
+ end
196
+
197
+ #
198
+ # the Heartbeat class is essentially wrapper over an IPC channel that sends a
199
+ # ping on the channel indicating process health. if either end of the channel
200
+ # is detached the ping will fail and an error will be raised. in this was it
201
+ # is ensured that Slave object cannot continue to live without their parent
202
+ # being alive.
203
+ #
204
+ class Heartbeat
205
+ #--{{{
206
+ def initialize pulse_rate = 4.2, debug = false
207
+ #--{{{
208
+ @pulse_rate = Float pulse_rate
209
+ @debug = debug
210
+ @r, @w = IO::pipe
211
+ @pid = Process::pid
212
+ @ppid = Process::ppid
213
+ @cid = nil
214
+ @thread = nil
215
+ @ppid = nil
216
+ @whoami = nil
217
+ @beating = nil
218
+ @pipe = nil
219
+ #--}}}
220
+ end
221
+ def start
222
+ #--{{{
223
+ if Process::pid == @pid
224
+ @w.close
225
+ @pipe = @r
226
+ parent_start
227
+ else
228
+ @r.close
229
+ @pipe = @w
230
+ child_start
231
+ end
232
+ @beating = true
233
+ #--}}}
234
+ end
235
+ def parent_start
236
+ #--{{{
237
+ @whoami = 'parent'
238
+ @thread =
239
+ Thread::new(Thread::current) do |cur|
240
+ begin
241
+ loop do
242
+ buf = @pipe.gets
243
+ trace{ "<#{ @whoami }> <#{ @pid }> gets <#{ buf.inspect }>" }
244
+ @cid = Integer buf.strip if @cid.nil? and buf =~ %r/^\s*\d+\s*$/
245
+ end
246
+ rescue => e
247
+ cur.raise e
248
+ ensure
249
+ @pipe.close rescue nil
250
+ end
251
+ end
252
+ #--}}}
253
+ end
254
+ def child_start
255
+ #--{{{
256
+ @whoami = 'child'
257
+ @pid = Process::pid
258
+ @ppid = Process::ppid
259
+ @thread =
260
+ Thread::new(Thread::current) do |cur|
261
+ begin
262
+ loop do
263
+ trace{ "<#{ @whoami }> <#{ @pid }> puts <#{ @pid }>" }
264
+ @pipe.puts @pid
265
+ Process::kill 0, @ppid
266
+ sleep @pulse_rate
267
+ end
268
+ rescue => e
269
+ cur.raise e
270
+ ensure
271
+ @pipe.close rescue nil
272
+ end
273
+ end
274
+ #--}}}
275
+ end
276
+ def stop
277
+ #--{{{
278
+ raise "not beating" unless @beating
279
+ @thread.kill
280
+ @pipe.close rescue nil
281
+ @beating = false
282
+ #--}}}
283
+ end
284
+ def trace
285
+ #--{{{
286
+ STDERR.puts(yield) if @debug and STDERR.tty?
287
+ #--}}}
288
+ end
289
+ #--}}}
290
+ end # class Heartbeat
291
+ #--}}}
292
+ end # class Slave