slave 0.0.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.
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