live_console 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/udscat +51 -0
- data/doc/README +63 -20
- data/doc/lc_example.rb +6 -2
- data/doc/lc_unix_example.rb +37 -0
- data/lib/live_console/io_methods/socket_io.rb +27 -0
- data/lib/live_console/io_methods/unix_socket_io.rb +31 -0
- data/lib/live_console/io_methods.rb +53 -0
- data/lib/live_console.rb +122 -54
- data/lib/live_console_config.rb +2 -1
- metadata +52 -37
data/bin/udscat
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This is a client for the unix domain socket version of LiveConsole. It just
|
4
|
+
# talks to the socket. It has been included so that you don't have to track
|
5
|
+
# down a version of netcat that does this.
|
6
|
+
|
7
|
+
require 'socket'
|
8
|
+
|
9
|
+
socket_path = ARGV.first
|
10
|
+
if socket_path.nil?
|
11
|
+
$stderr.puts "You must supply a path to the socket you want to connect to."
|
12
|
+
exit 1
|
13
|
+
end
|
14
|
+
|
15
|
+
client = UNIXSocket.new socket_path
|
16
|
+
|
17
|
+
$stdin.sync = $stdout.sync = client.sync = true
|
18
|
+
|
19
|
+
read_thread =
|
20
|
+
Thread.new {
|
21
|
+
loop {
|
22
|
+
begin
|
23
|
+
Thread.pass
|
24
|
+
$stdout.write client.read_nonblock(1024)
|
25
|
+
rescue Errno::EAGAIN, Errno::EINTR => e
|
26
|
+
IO.select [client], [], [], 1
|
27
|
+
rescue Errno::EOFError, Errno::EPIPE => e
|
28
|
+
# nothing
|
29
|
+
end
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
Thread.new {
|
34
|
+
loop {
|
35
|
+
begin
|
36
|
+
l = $stdin.read_nonblock(1024)
|
37
|
+
rescue Errno::EAGAIN
|
38
|
+
retry
|
39
|
+
end
|
40
|
+
|
41
|
+
begin
|
42
|
+
client.print l if IO.select [], [client], [], 1
|
43
|
+
rescue Errno::EPIPE => e
|
44
|
+
$stderr.puts "Other end closed."
|
45
|
+
exit 0
|
46
|
+
end
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
trap('INT') { exit 0 }
|
51
|
+
loop { sleep 1 }
|
data/doc/README
CHANGED
@@ -2,15 +2,16 @@
|
|
2
2
|
|
3
3
|
== Summary
|
4
4
|
|
5
|
-
LiveConsole is a library for providing IRB over a TCP connection
|
6
|
-
it to your application, you can run arbitrary code
|
5
|
+
LiveConsole is a library for providing IRB over a TCP connection or a Unix
|
6
|
+
Domain Socket. If you add it to your application, you can run arbitrary code
|
7
|
+
against your application.
|
7
8
|
For example, you can:
|
8
9
|
* Inspect the state of a running application
|
9
10
|
* Change the state of the application
|
10
11
|
* Patch code on the fly, without a restart.
|
11
|
-
* Let anyone on the net 0wn you if you bind to
|
12
|
-
|
13
|
-
|
12
|
+
* Let anyone on the net 0wn you if you bind to a public interface. :)
|
13
|
+
It's useful as a diagnostic tool, a debugging tool, and a way to impress your
|
14
|
+
friends or get those Lisp guys off your back. You know the ones I mean.
|
14
15
|
|
15
16
|
== Stern Security Warning. Grrr.
|
16
17
|
|
@@ -35,14 +36,32 @@ LiveConsole is very easy to use in your own app:
|
|
35
36
|
require 'rubygems'
|
36
37
|
require 'live_console'
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
39
|
+
# Create a LiveConsole using TCP on port 1337
|
40
|
+
lc = LiveConsole.new :socket, :port => 1337
|
41
|
+
# We're not yet accepting connections. We need to start it up:
|
42
|
+
lc.start # Starts the LiveConsole thread
|
41
43
|
# At this point, users can connect and get an IRB prompt.
|
42
|
-
lc.stop
|
44
|
+
lc.stop # Kills the LiveConsole thread
|
43
45
|
# Now, no one can connect.
|
44
46
|
|
45
|
-
|
47
|
+
# Create a LiveConsole using a Unix socket in /tmp/live-console.sock
|
48
|
+
lc = LiveConsole.new :unix_socket, :path => '/tmp/live-console.sock'
|
49
|
+
# As above:
|
50
|
+
lc.start
|
51
|
+
lc.stop
|
52
|
+
|
53
|
+
# Have a LiveConsole run code in a binding other than the top-level:
|
54
|
+
lc = LiveConsole.new :unix_socket, :path => '/tmp/live-console.sock'
|
55
|
+
:bind => binding
|
56
|
+
lc.start
|
57
|
+
# That will start IRB in the current binding. There is also an accessor:
|
58
|
+
lc.bind = binding
|
59
|
+
# Of course, you must restart before IRB will see the new binding:
|
60
|
+
lc.restart
|
61
|
+
|
62
|
+
Have a look at doc/lc_example.rb or doc/lc_unix_example.rb for brief examples
|
63
|
+
of how to use LiveConsole.
|
64
|
+
|
46
65
|
Try just running it:
|
47
66
|
|
48
67
|
$ ruby doc/lc_example.rb 4000 test
|
@@ -50,30 +69,54 @@ Try just running it:
|
|
50
69
|
$ netcat localhost 4000
|
51
70
|
irb(main):001:0> puts 'Wow, magic!'
|
52
71
|
|
72
|
+
$ ruby doc/lc_unix_example.rb /tmp/live-console.sock
|
73
|
+
# Then, in a different shell:
|
74
|
+
$ udscat /tmp/live-console.sock
|
75
|
+
irb(main):001:0> puts 'Words cannot describe the joy I feel.'
|
76
|
+
|
53
77
|
You can get creative about it, only starting LiveConsole when there's an
|
54
78
|
unhandled exception in your server, and then calling LiveConsole#stop when
|
55
79
|
you've diagnosed and fixed whatever the problem was.
|
56
80
|
|
81
|
+
Additionally, if you want to run LiveConsole on a server, but run netcat
|
82
|
+
locally, you can use SSH port forwarding to avoid having to open LiveConsole
|
83
|
+
to the world:
|
84
|
+
|
85
|
+
ssh -L4000:localhost:4000 you@server
|
86
|
+
|
87
|
+
Then, locally, you can do
|
88
|
+
|
89
|
+
netcat localhost 4000
|
90
|
+
|
91
|
+
and get the remote LiveConsole. man ssh for more details. Of course, this
|
92
|
+
only works for the TCP socket mode.
|
93
|
+
|
57
94
|
== Bugs
|
58
95
|
|
59
96
|
LiveConsole lacks many of the niceties of IRB on the console, like Readline
|
60
97
|
support.
|
61
98
|
|
62
99
|
Typing exit, hitting ^D, or sending signals (like INT or STOP) doesn't work.
|
63
|
-
Just exit the program you used to connect to it.
|
100
|
+
Just exit the program you used to connect to it. This has more to do with the
|
101
|
+
program you use to connect to the socket.
|
102
|
+
|
103
|
+
For TCP connections, there is no authentication support yet, although it is
|
104
|
+
planned for the near future. This creates a security risk: anyone that can
|
105
|
+
connect to the socket can run arbitrary Ruby code as the user who owns the
|
106
|
+
process. In fact, even binding to localhost can be a security issue if you're
|
107
|
+
on a box with any untrusted users. If there's a chance you don't know what
|
108
|
+
you're doing, avoid using this library. The Unix Domain Socket version is more
|
109
|
+
secure, as you can control access via filesystem permissions.
|
64
110
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
binding to localhost can be a security issue if you're on a box with any
|
69
|
-
untrusted users. If there's a chance you don't know what you're doing, avoid
|
70
|
-
using this library.
|
111
|
+
Only one client can connect at a time. I don't think anyone needs multiple LC
|
112
|
+
connections to serve multiple instances of IRB to various clients, but if you
|
113
|
+
need it, let me know.
|
71
114
|
|
72
115
|
The README contains a slur against Lisp guys. Please stop hitting me with that PDP-10 manual. I love your language and the lambda tattoo on your chest.
|
73
116
|
|
74
|
-
Other than that, LiveConsole doesn't have any known bugs, but it is
|
75
|
-
software, so they are likely to be there. Bug
|
76
|
-
accepted.
|
117
|
+
Other than that, LiveConsole doesn't have any known bugs, but it is odd
|
118
|
+
software that also monkey-patches IRB, so they are likely to be there. Bug
|
119
|
+
reports and patches gratefully accepted.
|
77
120
|
|
78
121
|
== Credits
|
79
122
|
|
data/doc/lc_example.rb
CHANGED
@@ -14,14 +14,18 @@ and then in a different terminal, connect to it via netcat or telnet. You can
|
|
14
14
|
check that the value of $x is exactly what you set it to, and that you're
|
15
15
|
working inside this process, but there's not much to do inside the example
|
16
16
|
script. :)
|
17
|
+
|
17
18
|
EOF
|
18
19
|
|
19
20
|
port = ARGV.first.to_i
|
20
21
|
port = port.zero? ? 3333 : port
|
21
22
|
$x = ARGV[1]
|
22
23
|
|
23
|
-
lc = LiveConsole.new port
|
24
|
-
lc.
|
24
|
+
lc = LiveConsole.new :socket, :port => port, :bind => binding
|
25
|
+
lc.start
|
26
|
+
|
27
|
+
puts "My PID is #{Process.pid}, " \
|
28
|
+
"I'm running on port #{port}, and $x = #{$x.inspect}"
|
25
29
|
|
26
30
|
oldx = $x
|
27
31
|
loop {
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'live_console'
|
5
|
+
|
6
|
+
default_path = "/tmp/lc_example_#{Process.uid}.sock"
|
7
|
+
|
8
|
+
print <<-EOF
|
9
|
+
This is a demo program for LiveConsole. It starts a LiveConsole at the
|
10
|
+
specified path, and you can connect to it by using netcat or telnet to connect
|
11
|
+
to the specified port.
|
12
|
+
Usage:
|
13
|
+
#{$0} [path_to_socket [value_for_$x]]
|
14
|
+
The default path is #{default_path}, and $x is set by default to nil.
|
15
|
+
Run this program, and then in a different terminal, connect to it via
|
16
|
+
the supplied udscat program or the BSD version of netcat.
|
17
|
+
EOF
|
18
|
+
|
19
|
+
path = ARGV.first
|
20
|
+
path = path.nil? ? default_path : path
|
21
|
+
$x = ARGV[1]
|
22
|
+
|
23
|
+
lc = LiveConsole.new :unix_socket, :path => path, :bind => binding
|
24
|
+
lc.start
|
25
|
+
|
26
|
+
puts "My PID is #{Process.pid}, " \
|
27
|
+
"I'm running on #{path}, and $x = #{$x.inspect}"
|
28
|
+
|
29
|
+
oldx = $x
|
30
|
+
loop {
|
31
|
+
if $x != oldx
|
32
|
+
puts "The time is now #{Time.now.strftime('%R:%S')}.",
|
33
|
+
"The value of $x changed from #{oldx.inspect} to #{$x.inspect}."
|
34
|
+
oldx = $x
|
35
|
+
end
|
36
|
+
sleep 1
|
37
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class LiveConsole::IOMethods::SocketIO
|
2
|
+
DefaultOpts = {
|
3
|
+
:host => '127.0.0.1',
|
4
|
+
}.freeze
|
5
|
+
RequiredOpts = DefaultOpts.keys + [:port]
|
6
|
+
|
7
|
+
include LiveConsole::IOMethods::IOMethod
|
8
|
+
|
9
|
+
def start
|
10
|
+
@server ||= TCPServer.new host, port
|
11
|
+
|
12
|
+
begin
|
13
|
+
self.raw_input = self.raw_output = server.accept_nonblock
|
14
|
+
return true
|
15
|
+
rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO,
|
16
|
+
Errno::EINTR => e
|
17
|
+
select
|
18
|
+
retry
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def stop
|
23
|
+
select
|
24
|
+
raw_input.close rescue nil
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
class LiveConsole::IOMethods::UnixSocketIO
|
4
|
+
DefaultOpts = {
|
5
|
+
:mode => 0600,
|
6
|
+
:uid => Process.uid,
|
7
|
+
:gid => Process.gid,
|
8
|
+
}
|
9
|
+
RequiredOpts = DefaultOpts.keys + [:path]
|
10
|
+
|
11
|
+
include LiveConsole::IOMethods::IOMethod
|
12
|
+
|
13
|
+
def start
|
14
|
+
@server ||= UNIXServer.new path
|
15
|
+
|
16
|
+
begin
|
17
|
+
self.raw_input = self.raw_output = server.accept_nonblock
|
18
|
+
raw_input.sync = true
|
19
|
+
return true
|
20
|
+
rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO,
|
21
|
+
Errno::EINTR => e
|
22
|
+
select
|
23
|
+
retry
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def stop
|
28
|
+
select
|
29
|
+
raw_input.close
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module LiveConsole::IOMethods
|
2
|
+
List = []
|
3
|
+
|
4
|
+
Dir[File.join(File.dirname(__FILE__), 'io_methods', '*.rb')].each { |entry|
|
5
|
+
fname = entry.sub /\.rb$/, ''
|
6
|
+
classname = File.basename(entry, '.rb').capitalize.
|
7
|
+
gsub(/_(\w)/) { $1.upcase }.sub(/io$/i, 'IO').to_sym
|
8
|
+
mname = File.basename(fname).sub(/_io$/, '').to_sym
|
9
|
+
|
10
|
+
autoload classname, fname
|
11
|
+
List << mname
|
12
|
+
|
13
|
+
define_method(mname) {
|
14
|
+
const_get classname
|
15
|
+
}
|
16
|
+
}
|
17
|
+
List.freeze
|
18
|
+
|
19
|
+
extend self
|
20
|
+
|
21
|
+
module IOMethod
|
22
|
+
def initialize(opts)
|
23
|
+
self.opts = self.class::DefaultOpts.merge opts
|
24
|
+
unless missing_opts.empty?
|
25
|
+
raise ArgumentError, "Missing opts for " \
|
26
|
+
"#{self.class.name}: #{missing_opts.inspect}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def missing_opts
|
31
|
+
self.class::RequiredOpts - opts.keys
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.included(other)
|
35
|
+
other.instance_eval {
|
36
|
+
readers = [:opts, :raw_input, :raw_output]
|
37
|
+
attr_accessor *readers
|
38
|
+
private *readers.map { |r| (r.to_s + '=').to_sym }
|
39
|
+
|
40
|
+
other::RequiredOpts.each { |opt|
|
41
|
+
define_method(opt) { opts[opt] }
|
42
|
+
}
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def select
|
47
|
+
IO.select [server], [], [], 1 if server
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
attr_accessor :server
|
52
|
+
end
|
53
|
+
end
|
data/lib/live_console.rb
CHANGED
@@ -5,73 +5,119 @@
|
|
5
5
|
|
6
6
|
require 'irb'
|
7
7
|
require 'socket'
|
8
|
+
require 'live_console_config'
|
8
9
|
|
9
10
|
# LiveConsole provides a socket that can be connected to via netcat or telnet
|
10
11
|
# to use to connect to an IRB session inside a running process. It creates a
|
11
|
-
# thread that listens on the specified address/port
|
12
|
-
# clients with an IRB shell. Using this, you can
|
13
|
-
# instance of a Ruby process to inspect the state or
|
14
|
-
# fly. There is currently no readline support.
|
12
|
+
# thread that listens on the specified address/port or Unix Domain Socket path,
|
13
|
+
# and presents connecting clients with an IRB shell. Using this, you can
|
14
|
+
# execute code on a running instance of a Ruby process to inspect the state or
|
15
|
+
# even patch code on the fly. There is currently no readline support.
|
15
16
|
class LiveConsole
|
16
17
|
include Socket::Constants
|
18
|
+
autoload :IOMethods, 'live_console/io_methods'
|
17
19
|
|
18
|
-
attr_accessor :
|
19
|
-
private :
|
20
|
+
attr_accessor :io_method, :io, :thread, :bind
|
21
|
+
private :io_method=, :io=, :thread=
|
20
22
|
|
21
23
|
# call-seq:
|
22
|
-
# # Bind a LiveConsole to localhost:3030
|
23
|
-
#
|
24
|
+
# # Bind a LiveConsole to localhost:3030 (only allow clients on this
|
25
|
+
# # machine to connect):
|
26
|
+
# LiveConsole.new :socket, :port => 3030
|
24
27
|
# # Accept connections from anywhere on port 3030. Ridiculously insecure:
|
25
|
-
#
|
28
|
+
# LiveConsole.new(:socket, :port => 3030, :host => '0.0.0.0')
|
29
|
+
# # Use a Unix Domain Socket (which is more secure) instead:
|
30
|
+
# LiveConsole.new(:unix_socket, :path => '/tmp/my_liveconsole.sock',
|
31
|
+
# :mode => 0600, :uid => Process.uid, :gid => Process.gid)
|
32
|
+
# # By default, the mode is 0600, and the uid and gid are those of the
|
33
|
+
# # current process. These three options are for the file's permissions.
|
34
|
+
# # You can also supply a binding for IRB's toplevel:
|
35
|
+
# LiveConsole.new(:unix_socket,
|
36
|
+
# :path => "/tmp/live_console_#{Process.pid}.sock", :bind => binding)
|
26
37
|
#
|
27
|
-
# Creates a new LiveConsole. You must next call LiveConsole#
|
28
|
-
# want to spawn the thread to accept connections and
|
29
|
-
def initialize(
|
30
|
-
self.
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
38
|
+
# Creates a new LiveConsole. You must next call LiveConsole#start when you
|
39
|
+
# want to spawn the thread to accept connections and start the console.
|
40
|
+
def initialize(io_method, opts = {})
|
41
|
+
self.io_method = io_method.to_sym
|
42
|
+
self.bind = opts.delete :bind
|
43
|
+
unless IOMethods::List.include?(self.io_method)
|
44
|
+
raise ArgumentError, "Unknown IO method: #{io_method}"
|
45
|
+
end
|
46
|
+
|
47
|
+
init_io opts
|
48
|
+
end
|
49
|
+
|
50
|
+
# LiveConsole#start spawns a thread to listen for, accept, and provide an
|
51
|
+
# IRB console to new connections. If a thread is already running, this
|
52
|
+
# method simply returns false; otherwise, it returns the new thread.
|
53
|
+
def start
|
54
|
+
if thread
|
55
|
+
if thread.alive?
|
56
|
+
return false
|
57
|
+
else
|
58
|
+
thread.join
|
59
|
+
self.thread = nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
self.thread = Thread.new {
|
39
64
|
loop {
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
socket.close rescue nil
|
49
|
-
IO.select([tcp_server], [], [], 1)
|
50
|
-
|
51
|
-
retry
|
65
|
+
Thread.pass
|
66
|
+
if io.start
|
67
|
+
irb_io = GenericIOMethod.new io.raw_input, io.raw_output
|
68
|
+
begin
|
69
|
+
IRB.start_with_io(irb_io, bind)
|
70
|
+
rescue Errno::EPIPE => e
|
71
|
+
io.stop
|
72
|
+
end
|
52
73
|
end
|
53
74
|
}
|
54
75
|
}
|
55
|
-
|
76
|
+
thread
|
56
77
|
end
|
57
78
|
|
58
79
|
# Ends the running thread, if it exists. Returns true if a thread was
|
59
80
|
# running, false otherwise.
|
60
81
|
def stop
|
61
|
-
if
|
62
|
-
|
63
|
-
|
82
|
+
if thread
|
83
|
+
if thread == Thread.current
|
84
|
+
self.thread = nil
|
85
|
+
Thread.current.exit!
|
86
|
+
end
|
87
|
+
|
88
|
+
thread.exit
|
89
|
+
if thread.join(0.1).nil?
|
90
|
+
thread.exit!
|
91
|
+
end
|
92
|
+
self.thread = nil
|
64
93
|
true
|
65
94
|
else
|
66
95
|
false
|
67
96
|
end
|
68
97
|
end
|
69
98
|
|
99
|
+
# Restarts. Useful for binding changes. Return value is the same as for
|
100
|
+
# LiveConsole#start.
|
101
|
+
def restart
|
102
|
+
r = lambda { stop; start }
|
103
|
+
if thread == Thread.current # An odd case.
|
104
|
+
Thread.new &r
|
105
|
+
else
|
106
|
+
r.call
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
70
112
|
def init_irb
|
71
113
|
return if @@irb_inited_already
|
72
114
|
IRB.setup nil
|
73
115
|
@@irb_inited_already = true
|
74
116
|
end
|
117
|
+
|
118
|
+
def init_io opts
|
119
|
+
self.io = IOMethods.send(io_method).new opts
|
120
|
+
end
|
75
121
|
end
|
76
122
|
|
77
123
|
# We need to make a couple of changes to the IRB module to account for using a
|
@@ -80,7 +126,7 @@ module IRB
|
|
80
126
|
@inited = false
|
81
127
|
|
82
128
|
# Overridden a la FXIrb to accomodate our needs.
|
83
|
-
def IRB.start_with_io(io, &block)
|
129
|
+
def IRB.start_with_io(io, bind, &block)
|
84
130
|
unless @inited
|
85
131
|
setup '/dev/null'
|
86
132
|
IRB.parse_opts
|
@@ -88,7 +134,9 @@ module IRB
|
|
88
134
|
@inited = true
|
89
135
|
end
|
90
136
|
|
91
|
-
|
137
|
+
bind ||= IRB::Frame.top(1)
|
138
|
+
ws = IRB::WorkSpace.new(bind)
|
139
|
+
irb = Irb.new(ws, io, io)
|
92
140
|
|
93
141
|
@CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
|
94
142
|
@CONF[:MAIN_CONTEXT] = irb.context
|
@@ -102,7 +150,7 @@ module IRB
|
|
102
150
|
retry
|
103
151
|
end
|
104
152
|
}
|
105
|
-
print "\n"
|
153
|
+
irb.print "\n"
|
106
154
|
end
|
107
155
|
|
108
156
|
class Context
|
@@ -125,32 +173,52 @@ module IRB
|
|
125
173
|
end
|
126
174
|
end
|
127
175
|
|
128
|
-
# The
|
129
|
-
class
|
130
|
-
|
131
|
-
|
176
|
+
# The GenericIOMethod is a class that wraps I/O for IRB.
|
177
|
+
class GenericIOMethod < IRB::StdioInputMethod
|
178
|
+
# call-seq:
|
179
|
+
# GenericIOMethod.new io
|
180
|
+
# GenericIOMethod.new input, output
|
181
|
+
#
|
182
|
+
# Creates a GenericIOMethod, using either a single object for both input
|
183
|
+
# and output, or one object for input and another for output.
|
184
|
+
def initialize(input, output = nil)
|
185
|
+
@input, @output = input, output
|
132
186
|
@line = []
|
133
187
|
@line_no = 0
|
134
188
|
end
|
135
189
|
|
190
|
+
attr_reader :input
|
191
|
+
def output
|
192
|
+
@output || input
|
193
|
+
end
|
194
|
+
|
136
195
|
def gets
|
137
|
-
|
138
|
-
|
139
|
-
@line[@line_no += 1] =
|
140
|
-
@
|
196
|
+
output.print @prompt
|
197
|
+
output.flush
|
198
|
+
@line[@line_no += 1] = input.gets
|
199
|
+
# @io.flush # Not sure this is needed.
|
141
200
|
@line[@line_no]
|
142
201
|
end
|
143
202
|
|
144
|
-
#
|
145
|
-
|
146
|
-
|
147
|
-
|
203
|
+
# Returns the user input history.
|
204
|
+
def lines
|
205
|
+
@line.dup
|
206
|
+
end
|
148
207
|
|
149
208
|
def print(*a)
|
150
|
-
|
209
|
+
output.print *a
|
151
210
|
end
|
152
211
|
|
153
212
|
def file_name
|
154
|
-
|
213
|
+
input.inspect
|
214
|
+
end
|
215
|
+
|
216
|
+
def eof?
|
217
|
+
input.eof?
|
218
|
+
end
|
219
|
+
|
220
|
+
def close
|
221
|
+
input.close
|
222
|
+
output.close if @output
|
155
223
|
end
|
156
224
|
end
|
data/lib/live_console_config.rb
CHANGED
metadata
CHANGED
@@ -1,52 +1,67 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.4
|
3
|
-
specification_version: 1
|
4
2
|
name: live_console
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-10-19 00:00:00 -07:00
|
8
|
-
summary: A library to support adding a console to your running application.
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email: pete.elmore@gmail.com
|
12
|
-
homepage: http://debu.gs/live-console
|
13
|
-
rubyforge_project:
|
14
|
-
description:
|
15
|
-
autorequire: live_console
|
16
|
-
default_executable:
|
17
|
-
bindir: bin
|
18
|
-
has_rdoc: true
|
19
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">"
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 0.0.0
|
24
|
-
version:
|
4
|
+
version: 0.2.0
|
25
5
|
platform: ruby
|
26
|
-
signing_key:
|
27
|
-
cert_chain:
|
28
|
-
post_install_message:
|
29
6
|
authors:
|
30
7
|
- Pete Elmore
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
- doc/lc_example.rb
|
35
|
-
- lib/live_console.rb
|
36
|
-
- lib/live_console_config.rb
|
37
|
-
test_files: []
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
38
11
|
|
39
|
-
|
12
|
+
date: 2008-10-15 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: pete.elmore@gmail.com
|
18
|
+
executables:
|
19
|
+
- udscat
|
20
|
+
extensions: []
|
40
21
|
|
41
22
|
extra_rdoc_files:
|
42
|
-
- doc/
|
23
|
+
- doc/lc_example.rb
|
43
24
|
- doc/LICENSE
|
25
|
+
- doc/README
|
26
|
+
- doc/lc_unix_example.rb
|
27
|
+
files:
|
28
|
+
- bin/udscat
|
44
29
|
- doc/lc_example.rb
|
45
|
-
|
46
|
-
|
47
|
-
|
30
|
+
- doc/LICENSE
|
31
|
+
- doc/README
|
32
|
+
- doc/lc_unix_example.rb
|
33
|
+
- lib/live_console_config.rb
|
34
|
+
- lib/live_console.rb
|
35
|
+
- lib/live_console
|
36
|
+
- lib/live_console/io_methods
|
37
|
+
- lib/live_console/io_methods/socket_io.rb
|
38
|
+
- lib/live_console/io_methods/unix_socket_io.rb
|
39
|
+
- lib/live_console/io_methods.rb
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: http://debu.gs/live-console
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
48
44
|
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
49
59
|
requirements: []
|
50
60
|
|
51
|
-
|
61
|
+
rubyforge_project: live-console
|
62
|
+
rubygems_version: 1.3.0
|
63
|
+
signing_key:
|
64
|
+
specification_version: 2
|
65
|
+
summary: A library to support adding a console to your running application.
|
66
|
+
test_files: []
|
52
67
|
|