session 2.4.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,51 @@
1
+ ======== ======== ======== ========
2
+ #1
3
+ ======== ======== ======== ========
4
+ STDOUT:
5
+ README
6
+ install.rb
7
+ lib
8
+ sample
9
+ STDERR:
10
+ STATUS: 0
11
+ ======== ======== ======== ========
12
+ ======== ======== ======== ========
13
+ #2
14
+ ======== ======== ======== ========
15
+ STDOUT:
16
+ README
17
+ STDERR:
18
+ STDOUT:
19
+ install.rb
20
+ STDERR:
21
+ STDOUT:
22
+ lib
23
+ STDERR:
24
+ STDOUT:
25
+ sample
26
+ STDERR:
27
+ STATUS: 0
28
+ ======== ======== ======== ========
29
+ ======== ======== ======== ========
30
+ #3
31
+ ======== ======== ======== ========
32
+ STDOUT:
33
+ README
34
+ install.rb
35
+ lib
36
+ sample
37
+ STDERR:
38
+ STATUS: 0
39
+ ======== ======== ======== ========
40
+ ======== ======== ======== ========
41
+ #4
42
+ ======== ======== ======== ========
43
+ 42
44
+ 42
45
+ 42
46
+ 42
47
+ 42
48
+ 42
49
+ 42
50
+ 42
51
+ ... (repeats forever)
data/sample/driver.rb ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+ require 'tempfile'
3
+ require 'logger'
4
+ $:.unshift '.', '..', 'lib', File.join('..','lib')
5
+ require 'session'
6
+
7
+ DIV = ('=' * 79) << "\n"
8
+
9
+
10
+ # start session with bash
11
+ bash = Session::Bash.new
12
+
13
+
14
+ # create two tempory external programs to drive
15
+ prog_a = Tempfile.new('prog_a_')
16
+ prog_a.write <<-code
17
+ puts $0
18
+ puts 42
19
+ code
20
+ prog_a.close
21
+ prog_a = prog_a.path
22
+
23
+ prog_b = Tempfile.new('prog_b_')
24
+ prog_b.write <<-code
25
+ puts $0
26
+ puts 'forty-two'
27
+ code
28
+ prog_b.close
29
+ prog_b = prog_b.path
30
+
31
+
32
+ # set up logging
33
+ logger = Logger.new STDOUT
34
+
35
+
36
+ # run both programs redirect the stdout into the log
37
+ logger.info{ 'running program a' }
38
+ logger << DIV
39
+ bash.execute "ruby #{ prog_a}", :stdout => logger, :stderr => STDERR
40
+ logger << DIV
41
+
42
+ logger.info{ 'running program b' }
43
+ logger << DIV
44
+ bash.execute "ruby #{ prog_b}", :stdout => logger, :stderr => STDERR
45
+ logger << DIV
46
+
47
+
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ require 'tempfile'
3
+ require 'readline'
4
+ include Readline
5
+
6
+ $:.unshift '.', '..', 'lib', File.join('..','lib')
7
+ require 'session'
8
+
9
+ idl = Session::IDL.new
10
+
11
+ n = 0
12
+ loop do
13
+ command = readline("#{ n }: IDL_WRAP > ", true)
14
+ if command =~ /^\s*\!*history\!*\s*$/
15
+ open('idl_wrap.history','w'){|f| f.puts idl.history}
16
+ next
17
+ end
18
+ exit if command =~ /^\s*exit\s*$/io
19
+
20
+ out, err = idl.execute command
21
+ out ||= ''
22
+ err ||= ''
23
+ printf "STDOUT:\n%s\nSTDERR:\n%s\n", out.gsub(%r/^/,"\t"), err.gsub(%r/^/,"\t")
24
+ n += 1
25
+ end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ require 'tempfile'
3
+ require 'readline'
4
+ include Readline
5
+
6
+ $:.unshift '.', '..', 'lib', File.join('..','lib')
7
+ require 'session'
8
+
9
+ shell = Session::Shell.new
10
+
11
+ require 'tempfile'
12
+ require 'readline'
13
+ include Readline
14
+
15
+ n = 0
16
+ loop do
17
+ command = readline("#{ n }: SHELL > ", true)
18
+ if command =~ /^\s*\!*history\!*\s*$/
19
+ open('shell.history','w'){|f| f.puts shell.history}
20
+ next
21
+ end
22
+ exit if command =~ /^\s*(?:exit|quit)\s*$/io
23
+
24
+ out, err = shell.execute command
25
+ out ||= ''
26
+ err ||= ''
27
+ printf "STDOUT:\n%s\nSTDERR:\n%s\n", out.gsub(%r/^/,"\t"), err.gsub(%r/^/,"\t")
28
+ n += 1
29
+ end
data/sample/sh0.rb ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+ require 'tempfile'
3
+
4
+ $:.unshift '.', '..', 'lib', File.join('..','lib')
5
+ require 'session'
6
+
7
+ shell = Session::Shell.new :history => false
8
+
9
+ shell.execute('ls -ltar') do |out, err|
10
+ if out
11
+ puts "OUT:\n#{ out }"
12
+ elsif err
13
+ puts "ERR:\n#{ err }"
14
+ end
15
+ end
16
+ puts shell.history
17
+
18
+ shell.outproc = lambda{|out| puts "OUT:\n#{ out }"}
19
+ shell.errproc = lambda{|err| puts "ERR:\n#{ err }"}
20
+ #shell.execute('ls -1')
21
+ #shell.execute('ls no-exit')
22
+ shell.execute('while test 1; do echo `date` && ls no-exist; sleep 1; done')
23
+
data/sample/stdin.rb ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift '.', '..', 'lib', File.join('..','lib')
3
+ require 'session'
4
+
5
+ sh = Session::new
6
+
7
+ stdout, stderr = sh.execute('cat', :stdin => open('/etc/passwd'))
8
+
9
+ stdout.each{|line| print line}
10
+
11
+
12
+
13
+ stdin, stdout, stderr = "42\n", '', ''
14
+
15
+ sh.execute('cat', 0 => stdin, 1 => stdout, 2 => stderr)
16
+
17
+ stdout.each{|line| print line}
@@ -0,0 +1,42 @@
1
+ # usage:
2
+ # ruby -I lib -r session test/threadtest.rb
3
+ # SESSION_USE_SPAWN=1 ruby -I lib -r session test/threadtest.rb
4
+
5
+ %w(lib ../lib . ..).each{|d| $:.unshift d}
6
+ require 'session'
7
+
8
+ def display obj
9
+ Thread.critical = true
10
+ STDOUT.print obj
11
+ STDOUT.flush
12
+ ensure
13
+ Thread.critical = false
14
+ end
15
+
16
+ $VERBOSE=nil
17
+ STDOUT.sync = true
18
+ STDERR.sync = true
19
+ STDOUT.puts "Session::VERSION <#{ Session::VERSION }>"
20
+ STDOUT.puts "Session.use_spawn <#{ Session.use_spawn ? 'true' : 'false' }>"
21
+ STDOUT.puts "the timestamps of each tid should come back about 1 second apart or there are problems..."
22
+ STDOUT.puts
23
+
24
+
25
+ threads = []
26
+
27
+ 3.times do |i|
28
+ threads <<
29
+ Thread::new(i) do |i|
30
+ cmd = 'echo 42; sleep 1;' * 3
31
+ sh = Session.new #:use_spawn=>true
32
+ sh.execute(cmd) do |o,e|
33
+ which = o ? 'stdout' : 'stderr'
34
+ line = (o || e).strip
35
+ indent = '| ' * i
36
+ display "#{ indent }tid<#{ i }> #{ which }<#{ line }> time<#{ Time.now.to_f }>\n"
37
+ end
38
+ end
39
+ sleep rand
40
+ end
41
+
42
+ threads.map{|t| t.join}
data/session.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ ## session.gemspec
2
+ #
3
+
4
+ Gem::Specification::new do |spec|
5
+ spec.name = "session"
6
+ spec.version = "3.1.0"
7
+ spec.platform = Gem::Platform::RUBY
8
+ spec.summary = "session"
9
+ spec.description = "description: session kicks the ass"
10
+
11
+ spec.files = ["gemspec.rb", "lib", "lib/session.rb", "LICENSE", "Rakefile", "README", "sample", "sample/bash.rb", "sample/bash.rb.out", "sample/driver.rb", "sample/session_idl.rb", "sample/session_sh.rb", "sample/sh0.rb", "sample/stdin.rb", "sample/threadtest.rb", "session.gemspec", "test", "test/session.rb"]
12
+ spec.executables = []
13
+
14
+ spec.require_path = "lib"
15
+
16
+ spec.has_rdoc = true
17
+ spec.test_files = "test/session.rb"
18
+ #spec.add_dependency 'lib', '>= version'
19
+ spec.add_dependency 'fattr'
20
+
21
+ spec.extensions.push(*[])
22
+
23
+ spec.rubyforge_project = "codeforpeople"
24
+ spec.author = "Ara T. Howard"
25
+ spec.email = "ara.t.howard@gmail.com"
26
+ spec.homepage = "http://github.com/ahoward/session/tree/master"
27
+ end
data/test/session.rb CHANGED
@@ -17,6 +17,9 @@ Session::debug = true
17
17
  class TimeoutError < StandardError; end
18
18
  def timeout n
19
19
  #{{{
20
+ # JRuby does not support fork, so we stub out timeout here
21
+ return yield if defined? JRUBY_VERSION
22
+
20
23
  ret = nil
21
24
  cid = fork
22
25
  unless cid
metadata CHANGED
@@ -1,41 +1,78 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.11
3
- specification_version: 1
4
2
  name: session
5
3
  version: !ruby/object:Gem::Version
6
- version: 2.4.0
7
- date: 2005-11-13 00:00:00.000000 -07:00
8
- summary: session
9
- require_paths:
10
- - lib
11
- email: ara.t.howard@noaa.gov
12
- homepage: http://codeforpeople.com/lib/ruby/session/
13
- rubyforge_project:
14
- description:
15
- autorequire: session
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: false
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- -
22
- - ">"
23
- - !ruby/object:Gem::Version
24
- version: 0.0.0
25
- version:
4
+ version: 3.1.0
26
5
  platform: ruby
27
- signing_key:
28
- cert_chain:
29
6
  authors:
30
- - Ara T. Howard
31
- files:
32
- - lib/session.rb
33
- - lib/session-2.4.0.rb
34
- test_files:
35
- - test/session.rb
36
- rdoc_options: []
37
- extra_rdoc_files: []
7
+ - Ara T. Howard
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-05-20 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: fattr
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: "description: session kicks the ass"
26
+ email: ara.t.howard@gmail.com
38
27
  executables: []
28
+
39
29
  extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - gemspec.rb
35
+ - lib/session.rb
36
+ - LICENSE
37
+ - Rakefile
38
+ - README
39
+ - sample/bash.rb
40
+ - sample/bash.rb.out
41
+ - sample/driver.rb
42
+ - sample/session_idl.rb
43
+ - sample/session_sh.rb
44
+ - sample/sh0.rb
45
+ - sample/stdin.rb
46
+ - sample/threadtest.rb
47
+ - session.gemspec
48
+ - test/session.rb
49
+ has_rdoc: true
50
+ homepage: http://github.com/ahoward/session/tree/master
51
+ licenses: []
52
+
53
+ post_install_message:
54
+ rdoc_options: []
55
+
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
40
70
  requirements: []
41
- dependencies: []
71
+
72
+ rubyforge_project: codeforpeople
73
+ rubygems_version: 1.3.5
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: session
77
+ test_files:
78
+ - test/session.rb
data/lib/session-2.4.0.rb DELETED
@@ -1,744 +0,0 @@
1
- require 'open3'
2
- require 'tmpdir'
3
- require 'thread'
4
- require 'yaml'
5
- require 'tempfile'
6
-
7
- module Session
8
- #--{{{
9
- VERSION = '2.4.0'
10
-
11
- @track_history = ENV['SESSION_HISTORY'] || ENV['SESSION_TRACK_HISTORY']
12
- @use_spawn = ENV['SESSION_USE_SPAWN']
13
- @use_open3 = ENV['SESSION_USE_OPEN3']
14
- @debug = ENV['SESSION_DEBUG']
15
-
16
- class << self
17
- #--{{{
18
- attr :track_history, true
19
- attr :use_spawn, true
20
- attr :use_open3, true
21
- attr :debug, true
22
- def new(*a, &b)
23
- #--{{{
24
- Sh::new(*a, &b)
25
- #--}}}
26
- end
27
- alias [] new
28
- #--}}}
29
- end
30
-
31
- class PipeError < StandardError; end
32
- class ExecutionError < StandardError; end
33
-
34
- class History
35
- #--{{{
36
- def initialize; @a = []; end
37
- def method_missing(m,*a,&b); @a.send(m,*a,&b); end
38
- def to_yaml(*a,&b); @a.to_yaml(*a,&b); end
39
- alias to_s to_yaml
40
- alias to_str to_yaml
41
- #--}}}
42
- end # class History
43
- class Command
44
- #--{{{
45
- class << self
46
- #--{{{
47
- def cmdno; @cmdno ||= 0; end
48
- def cmdno= n; @cmdno = n; end
49
- #--}}}
50
- end
51
-
52
- # attributes
53
- #--{{{
54
- attr :cmd
55
- attr :cmdno
56
- attr :out,true
57
- attr :err,true
58
- attr :cid
59
- attr :begin_out
60
- attr :end_out
61
- attr :begin_out_pat
62
- attr :end_out_pat
63
- attr :begin_err
64
- attr :end_err
65
- attr :begin_err_pat
66
- attr :end_err_pat
67
- #--}}}
68
-
69
- def initialize(command)
70
- #--{{{
71
- @cmd = command.to_s
72
- @cmdno = self.class.cmdno
73
- self.class.cmdno += 1
74
- @err = ''
75
- @out = ''
76
- @cid = "%d_%d_%d" % [$$, cmdno, rand(Time.now.usec)]
77
- @begin_out = "__CMD_OUT_%s_BEGIN__" % cid
78
- @end_out = "__CMD_OUT_%s_END__" % cid
79
- @begin_out_pat = %r/#{ Regexp.escape(@begin_out) }/
80
- @end_out_pat = %r/#{ Regexp.escape(@end_out) }/
81
- @begin_err = "__CMD_ERR_%s_BEGIN__" % cid
82
- @end_err = "__CMD_ERR_%s_END__" % cid
83
- @begin_err_pat = %r/#{ Regexp.escape(@begin_err) }/
84
- @end_err_pat = %r/#{ Regexp.escape(@end_err) }/
85
- #--}}}
86
- end
87
- def to_hash
88
- #--{{{
89
- %w(cmdno cmd out err cid).inject({}){|h,k| h.update k => send(k) }
90
- #--}}}
91
- end
92
- def to_yaml(*a,&b)
93
- #--{{{
94
- to_hash.to_yaml(*a,&b)
95
- #--}}}
96
- end
97
- alias to_s to_yaml
98
- alias to_str to_yaml
99
- #--}}}
100
- end # class Command
101
- class AbstractSession
102
- #--{{{
103
-
104
- # class methods
105
- class << self
106
- #--{{{
107
- def default_prog
108
- #--{{{
109
- return @default_prog if defined? @default_prog and @default_prog
110
- if defined? self::DEFAULT_PROG
111
- return @default_prog = self::DEFAULT_PROG
112
- else
113
- @default_prog = ENV["SESSION_#{ self }_PROG"]
114
- end
115
- nil
116
- #--}}}
117
- end
118
- def default_prog= prog
119
- #--{{{
120
- @default_prog = prog
121
- #--}}}
122
- end
123
- attr :track_history, true
124
- attr :use_spawn, true
125
- attr :use_open3, true
126
- attr :debug, true
127
- def init
128
- #--{{{
129
- @track_history = nil
130
- @use_spawn = nil
131
- @use_open3 = nil
132
- @debug = nil
133
- #--}}}
134
- end
135
- alias [] new
136
- #--}}}
137
- end
138
-
139
- # class init
140
- init
141
-
142
- # attributes
143
- #--{{{
144
- attr :opts
145
- attr :prog
146
- attr :stdin
147
- alias i stdin
148
- attr :stdout
149
- alias o stdout
150
- attr :stderr
151
- alias e stderr
152
- attr :history
153
- attr :track_history
154
- attr :outproc, true
155
- attr :errproc, true
156
- attr :use_spawn
157
- attr :use_open3
158
- attr :debug, true
159
- alias debug? debug
160
- attr :threads
161
- #--}}}
162
-
163
- # instance methods
164
- def initialize(*args)
165
- #--{{{
166
- @opts = hashify(*args)
167
-
168
- @prog = getopt('prog', opts, getopt('program', opts, self.class::default_prog))
169
-
170
- raise(ArgumentError, "no program specified") unless @prog
171
-
172
- @track_history = nil
173
- @track_history = Session::track_history unless Session::track_history.nil?
174
- @track_history = self.class::track_history unless self.class::track_history.nil?
175
- @track_history = getopt('history', opts) if hasopt('history', opts)
176
- @track_history = getopt('track_history', opts) if hasopt('track_history', opts)
177
-
178
- @use_spawn = nil
179
- @use_spawn = Session::use_spawn unless Session::use_spawn.nil?
180
- @use_spawn = self.class::use_spawn unless self.class::use_spawn.nil?
181
- @use_spawn = getopt('use_spawn', opts) if hasopt('use_spawn', opts)
182
-
183
- @use_open3 = nil
184
- @use_open3 = Session::use_open3 unless Session::use_open3.nil?
185
- @use_open3 = self.class::use_open3 unless self.class::use_open3.nil?
186
- @use_open3 = getopt('use_open3', opts) if hasopt('use_open3', opts)
187
-
188
- @debug = nil
189
- @debug = Session::debug unless Session::debug.nil?
190
- @debug = self.class::debug unless self.class::debug.nil?
191
- @debug = getopt('debug', opts) if hasopt('debug', opts)
192
-
193
- @history = nil
194
- @history = History::new if @track_history
195
-
196
- @outproc = nil
197
- @errproc = nil
198
-
199
- @stdin, @stdout, @stderr =
200
- if @use_spawn
201
- Spawn::spawn @prog
202
- elsif @use_open3
203
- Open3::popen3 @prog
204
- else
205
- __popen3 @prog
206
- end
207
-
208
- @threads = []
209
-
210
- clear
211
-
212
- if block_given?
213
- ret = nil
214
- begin
215
- ret = yield self
216
- ensure
217
- self.close!
218
- end
219
- return ret
220
- end
221
-
222
- return self
223
- #--}}}
224
- end
225
- def getopt opt, hash, default = nil
226
- #--{{{
227
- key = opt
228
- return hash[key] if hash.has_key? key
229
- key = "#{ key }"
230
- return hash[key] if hash.has_key? key
231
- key = key.intern
232
- return hash[key] if hash.has_key? key
233
- return default
234
- #--}}}
235
- end
236
- def hasopt opt, hash
237
- #--{{{
238
- key = opt
239
- return key if hash.has_key? key
240
- key = "#{ key }"
241
- return key if hash.has_key? key
242
- key = key.intern
243
- return key if hash.has_key? key
244
- return false
245
- #--}}}
246
- end
247
- def __popen3(*cmd)
248
- #--{{{
249
- pw = IO::pipe # pipe[0] for read, pipe[1] for write
250
- pr = IO::pipe
251
- pe = IO::pipe
252
-
253
- pid =
254
- __fork{
255
- # child
256
- pw[1].close
257
- STDIN.reopen(pw[0])
258
- pw[0].close
259
-
260
- pr[0].close
261
- STDOUT.reopen(pr[1])
262
- pr[1].close
263
-
264
- pe[0].close
265
- STDERR.reopen(pe[1])
266
- pe[1].close
267
-
268
- exec(*cmd)
269
- }
270
-
271
- Process::detach pid # avoid zombies
272
-
273
- pw[0].close
274
- pr[1].close
275
- pe[1].close
276
- pi = [pw[1], pr[0], pe[0]]
277
- pw[1].sync = true
278
- if defined? yield
279
- begin
280
- return yield(*pi)
281
- ensure
282
- pi.each{|p| p.close unless p.closed?}
283
- end
284
- end
285
- pi
286
- #--}}}
287
- end
288
- def __fork(*a, &b)
289
- #--{{{
290
- verbose = $VERBOSE
291
- begin
292
- $VERBOSE = nil
293
- Kernel::fork(*a, &b)
294
- ensure
295
- $VERBOSE = verbose
296
- end
297
- #--}}}
298
- end
299
-
300
- # abstract methods
301
- def clear
302
- #--{{{
303
- raise NotImplementedError
304
- #--}}}
305
- end
306
- alias flush clear
307
- def path
308
- #--{{{
309
- raise NotImplementedError
310
- #--}}}
311
- end
312
- def path=
313
- #--{{{
314
- raise NotImplementedError
315
- #--}}}
316
- end
317
- def send_command cmd
318
- #--{{{
319
- raise NotImplementedError
320
- #--}}}
321
- end
322
-
323
- # concrete methods
324
- def track_history= bool
325
- #--{{{
326
- @history ||= History::new
327
- @track_history = bool
328
- #--}}}
329
- end
330
- def ready?
331
- #--{{{
332
- (stdin and stdout and stderr) and
333
- (IO === stdin and IO === stdout and IO === stderr) and
334
- (not (stdin.closed? or stdout.closed? or stderr.closed?))
335
- #--}}}
336
- end
337
- def close!
338
- #--{{{
339
- [stdin, stdout, stderr].each{|pipe| pipe.close}
340
- stdin, stdout, stderr = nil, nil, nil
341
- true
342
- #--}}}
343
- end
344
- alias close close!
345
- def hashify(*a)
346
- #--{{{
347
- a.inject({}){|o,h| o.update(h)}
348
- #--}}}
349
- end
350
- private :hashify
351
- def execute(command, redirects = {})
352
- #--{{{
353
- $session_command = command if @debug
354
-
355
- raise(PipeError, command) unless ready?
356
-
357
- # clear buffers
358
- clear
359
-
360
- # setup redirects
361
- rerr = redirects[:e] || redirects[:err] || redirects[:stderr] ||
362
- redirects['stderr'] || redirects['e'] || redirects['err'] ||
363
- redirects[2] || redirects['2']
364
-
365
- rout = redirects[:o] || redirects[:out] || redirects[:stdout] ||
366
- redirects['stdout'] || redirects['o'] || redirects['out'] ||
367
- redirects[1] || redirects['1']
368
-
369
- # create cmd object and add to history
370
- cmd = Command::new command.to_s
371
-
372
- # store cmd if tracking history
373
- history << cmd if track_history
374
-
375
- # mutex for accessing shared data
376
- mutex = Mutex::new
377
-
378
- # io data for stderr and stdout
379
- err = {
380
- :io => stderr,
381
- :cmd => cmd.err,
382
- :name => 'stderr',
383
- :begin => false,
384
- :end => false,
385
- :begin_pat => cmd.begin_err_pat,
386
- :end_pat => cmd.end_err_pat,
387
- :redirect => rerr,
388
- :proc => errproc,
389
- :yield => lambda{|buf| yield(nil, buf)},
390
- :mutex => mutex,
391
- }
392
- out = {
393
- :io => stdout,
394
- :cmd => cmd.out,
395
- :name => 'stdout',
396
- :begin => false,
397
- :end => false,
398
- :begin_pat => cmd.begin_out_pat,
399
- :end_pat => cmd.end_out_pat,
400
- :redirect => rout,
401
- :proc => outproc,
402
- :yield => lambda{|buf| yield(buf, nil)},
403
- :mutex => mutex,
404
- }
405
-
406
- begin
407
- # send command in the background so we can begin processing output
408
- # immediately - thanks to tanaka akira for this suggestion
409
- threads << Thread::new { send_command cmd }
410
-
411
- # init
412
- main = Thread::current
413
- exceptions = []
414
-
415
- # fire off reader threads
416
- [err, out].each do |iodat|
417
- threads <<
418
- Thread::new(iodat, main) do |iodat, main|
419
-
420
- loop do
421
- main.raise(PipeError, command) unless ready?
422
- main.raise ExecutionError, iodat[:name] if iodat[:end] and not iodat[:begin]
423
-
424
- break if iodat[:end] or iodat[:io].eof?
425
-
426
- line = iodat[:io].gets
427
-
428
- buf = nil
429
-
430
- case line
431
- when iodat[:end_pat]
432
- iodat[:end] = true
433
- # handle the special case of non-newline terminated output
434
- if((m = %r/(.+)__CMD/o.match(line)) and (pre = m[1]))
435
- buf = pre
436
- end
437
- when iodat[:begin_pat]
438
- iodat[:begin] = true
439
- else
440
- next unless iodat[:begin] and not iodat[:end] # ignore chaff
441
- buf = line
442
- end
443
-
444
- if buf
445
- iodat[:mutex].synchronize do
446
- iodat[:cmd] << buf
447
- iodat[:redirect] << buf if iodat[:redirect]
448
- iodat[:proc].call buf if iodat[:proc]
449
- iodat[:yield].call buf if block_given?
450
- end
451
- end
452
- end
453
-
454
- true
455
- end
456
- end
457
- ensure
458
- # reap all threads - accumulating and rethrowing any exceptions
459
- begin
460
- while((t = threads.shift))
461
- t.join
462
- raise ExecutionError, 'iodat thread failure' unless t.value
463
- end
464
- rescue => e
465
- exceptions << e
466
- retry unless threads.empty?
467
- ensure
468
- unless exceptions.empty?
469
- meta_message = '<' << exceptions.map{|e| "#{ e.message } - (#{ e.class })"}.join('|') << '>'
470
- meta_backtrace = exceptions.map{|e| e.backtrace}.flatten
471
- raise ExecutionError, meta_message, meta_backtrace
472
- end
473
- end
474
- end
475
-
476
- # this should only happen if eof was reached before end pat
477
- [err, out].each do |iodat|
478
- raise ExecutionError, iodat[:name] unless iodat[:begin] and iodat[:end]
479
- end
480
-
481
-
482
- # get the exit status
483
- get_status if respond_to? :get_status
484
-
485
- out = err = iodat = nil
486
-
487
- return [cmd.out, cmd.err]
488
- #--}}}
489
- end
490
- #--}}}
491
- end # class AbstractSession
492
- class Sh < AbstractSession
493
- #--{{{
494
- DEFAULT_PROG = 'sh'
495
- ECHO = 'echo'
496
-
497
- attr :status
498
- alias exit_status status
499
- alias exitstatus status
500
-
501
- def clear
502
- #--{{{
503
- stdin.puts "#{ ECHO } __clear__ 1>&2"
504
- stdin.puts "#{ ECHO } __clear__"
505
- stdin.flush
506
- while((line = stderr.gets) and line !~ %r/__clear__/o); end
507
- while((line = stdout.gets) and line !~ %r/__clear__/o); end
508
- self
509
- #--}}}
510
- end
511
- def send_command cmd
512
- #--{{{
513
- stdin.printf "%s '%s' 1>&2\n", ECHO, cmd.begin_err
514
- stdin.printf "%s '%s' \n", ECHO, cmd.begin_out
515
-
516
- stdin.printf "%s\n", cmd.cmd
517
- stdin.printf "export __exit_status__=$?\n"
518
-
519
- stdin.printf "%s '%s' 1>&2\n", ECHO, cmd.end_err
520
- stdin.printf "%s '%s' \n", ECHO, cmd.end_out
521
-
522
- stdin.flush
523
- #--}}}
524
- end
525
- def get_status
526
- #--{{{
527
- @status = get_var '__exit_status__'
528
- unless @status =~ /^\s*\d+\s*$/o
529
- raise ExecutionError, "could not determine exit status from <#{ @status.inspect }>"
530
- end
531
-
532
- @status = Integer @status
533
- #--}}}
534
- end
535
- def set_var name, value
536
- #--{{{
537
- stdin.puts "export #{ name }=#{ value }"
538
- stdin.flush
539
- #--}}}
540
- end
541
- def get_var name
542
- #--{{{
543
- stdin.puts "#{ ECHO } \"#{ name }=${#{ name }}\""
544
- stdin.flush
545
-
546
- var = nil
547
- while((line = stdout.gets))
548
- m = %r/#{ name }\s*=\s*(.*)/.match line
549
- if m
550
- var = m[1]
551
- raise ExecutionError, "could not determine <#{ name }> from <#{ line.inspect }>" unless var
552
- break
553
- end
554
- end
555
-
556
- var
557
- #--}}}
558
- end
559
- def path
560
- #--{{{
561
- var = get_var 'PATH'
562
- var.strip.split %r/:/o
563
- #--}}}
564
- end
565
- def path= arg
566
- #--{{{
567
- case arg
568
- when Array
569
- arg = arg.join ':'
570
- else
571
- arg = arg.to_s.strip
572
- end
573
-
574
- set_var 'PATH', "'#{ arg }'"
575
- self.path
576
- #--}}}
577
- end
578
- def execute(command, redirects = {}, &block)
579
- #--{{{
580
- # setup redirect on stdin
581
- rin = redirects[:i] || redirects[:in] || redirects[:stdin] ||
582
- redirects['stdin'] || redirects['i'] || redirects['in'] ||
583
- redirects[0] || redirects['0']
584
-
585
- if rin
586
- tmp =
587
- begin
588
- Tempfile::new rand.to_s
589
- rescue
590
- Tempfile::new rand.to_s
591
- end
592
-
593
- begin
594
- tmp.write(
595
- if rin.respond_to? 'read'
596
- rin.read
597
- elsif rin.respond_to? 'to_s'
598
- rin.to_s
599
- else
600
- rin
601
- end
602
- )
603
- tmp.flush
604
- command = "{ #{ command } ;} < #{ tmp.path }"
605
- #puts command
606
- super(command, redirects, &block)
607
- ensure
608
- tmp.close! if tmp
609
- end
610
-
611
- else
612
- super
613
- end
614
- #--}}}
615
- end
616
- #--}}}
617
- end # class Sh
618
- class Bash < Sh
619
- #--{{{
620
- DEFAULT_PROG = 'bash'
621
- class Login < Bash
622
- DEFAULT_PROG = 'bash --login'
623
- end
624
- #--}}}
625
- end # class Bash
626
- class Shell < Bash; end
627
- # IDL => interactive data language - see http://www.rsinc.com/
628
- class IDL < AbstractSession
629
- #--{{{
630
- class LicenseManagerError < StandardError; end
631
- DEFAULT_PROG = 'idl'
632
- MAX_TRIES = 32
633
- def initialize(*args)
634
- #--{{{
635
- tries = 0
636
- ret = nil
637
- begin
638
- ret = super
639
- rescue LicenseManagerError => e
640
- tries += 1
641
- if tries < MAX_TRIES
642
- sleep 1
643
- retry
644
- else
645
- raise LicenseManagerError, "<#{ MAX_TRIES }> attempts <#{ e.message }>"
646
- end
647
- end
648
- ret
649
- #--}}}
650
- end
651
- def clear
652
- #--{{{
653
- stdin.puts "retall"
654
- stdin.puts "printf, -2, '__clear__'"
655
- stdin.puts "printf, -1, '__clear__'"
656
- stdin.flush
657
- while((line = stderr.gets) and line !~ %r/__clear__/o)
658
- raise LicenseManagerError, line if line =~ %r/license\s*manager/io
659
- end
660
- while((line = stdout.gets) and line !~ %r/__clear__/o)
661
- raise LicenseManagerError, line if line =~ %r/license\s*manager/io
662
- end
663
- self
664
- #--}}}
665
- end
666
- def send_command cmd
667
- #--{{{
668
- stdin.printf "printf, -2, '%s'\n", cmd.begin_err
669
- stdin.printf "printf, -1, '%s'\n", cmd.begin_out
670
-
671
- stdin.printf "%s\n", cmd.cmd
672
- stdin.printf "retall\n"
673
-
674
- stdin.printf "printf, -2, '%s'\n", cmd.end_err
675
- stdin.printf "printf, -1, '%s'\n", cmd.end_out
676
- stdin.flush
677
- #--}}}
678
- end
679
- def path
680
- #--{{{
681
- stdout, stderr = execute "print, !path"
682
- stdout.strip.split %r/:/o
683
- #--}}}
684
- end
685
- def path= arg
686
- #--{{{
687
- case arg
688
- when Array
689
- arg = arg.join ':'
690
- else
691
- arg = arg.to_s.strip
692
- end
693
- stdout, stderr = execute "!path='#{ arg }'"
694
-
695
- self.path
696
- #--}}}
697
- end
698
- #--}}}
699
- end # class IDL
700
- module Spawn
701
- #--{{{
702
- class << self
703
- def spawn command
704
- #--{{{
705
- ipath = tmpfifo
706
- opath = tmpfifo
707
- epath = tmpfifo
708
-
709
- cmd = "#{ command } < #{ ipath } 1> #{ opath } 2> #{ epath } &"
710
- system cmd
711
-
712
- i = open ipath, 'w'
713
- o = open opath, 'r'
714
- e = open epath, 'r'
715
-
716
- [i,o,e]
717
- #--}}}
718
- end
719
- def tmpfifo
720
- #--{{{
721
- path = nil
722
- 42.times do |i|
723
- tpath = File::join(Dir::tmpdir, "#{ $$ }.#{ rand }.#{ i }")
724
- v = $VERBOSE
725
- begin
726
- $VERBOSE = nil
727
- system "mkfifo #{ tpath }"
728
- ensure
729
- $VERBOSE = v
730
- end
731
- next unless $? == 0
732
- path = tpath
733
- at_exit{ File::unlink(path) rescue STDERR.puts("rm <#{ path }> failed") }
734
- break
735
- end
736
- raise "could not generate tmpfifo" unless path
737
- path
738
- #--}}}
739
- end
740
- end
741
- #--}}}
742
- end # module Spawn
743
- #--}}}
744
- end # module Session