live_console 0.2.1 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,16 +4,16 @@ Permission is hereby granted, free of charge, to any person obtaining a
4
4
  copy of this software and associated documentation files (the "Software"),
5
5
  to deal in the Software without restriction, including without limitation
6
6
  the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
- and/or sell copies of the Software, and to permit persons to whom the
7
+ and/or sell copies of the Software, and to permit persons to whom the
8
8
  Software is furnished to do so, subject to the following conditions:
9
9
 
10
- The above copyright notice and this permission notice shall be included in
11
- all copies or substantial portions of the Software.
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
12
 
13
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19
19
  DEALINGS IN THE SOFTWARE.
@@ -11,13 +11,12 @@ For example, you can:
11
11
  * Patch code on the fly, without a restart.
12
12
  * Let anyone on the net 0wn you if you bind to a public interface. :)
13
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
+ friends.
15
15
 
16
16
  == Stern Security Warning. Grrr.
17
17
 
18
18
  Have a look at the bugs section. It should be pretty apparent that incorrect
19
- use of this library could create a large security hole, especially before
20
- authentication is implemented.
19
+ use of this library could create a large security hole.
21
20
 
22
21
  == Installation
23
22
 
@@ -25,10 +24,6 @@ You can install via rubygems,
25
24
 
26
25
  gem install live_console
27
26
 
28
- or plain old setup.rb:
29
-
30
- ruby setup.rb install
31
-
32
27
  == How to use LiveConsole
33
28
 
34
29
  LiveConsole is very easy to use in your own app:
@@ -59,17 +54,17 @@ LiveConsole is very easy to use in your own app:
59
54
  # Of course, you must restart before IRB will see the new binding:
60
55
  lc.restart
61
56
 
62
- Have a look at doc/lc_example.rb or doc/lc_unix_example.rb for brief examples
57
+ Have a look at examples/lc_example.rb or examples/lc_unix_example.rb for brief examples
63
58
  of how to use LiveConsole.
64
59
 
65
60
  Try just running it:
66
61
 
67
- $ ruby doc/lc_example.rb 4000 test
62
+ $ ruby examples/lc_example.rb 4000 test
68
63
  # Then, in a different shell:
69
64
  $ netcat localhost 4000
70
65
  irb(main):001:0> puts 'Wow, magic!'
71
66
 
72
- $ ruby doc/lc_unix_example.rb /tmp/live-console.sock
67
+ $ ruby examples/lc_unix_example.rb /tmp/live-console.sock
73
68
  # Then, in a different shell:
74
69
  $ udscat /tmp/live-console.sock
75
70
  irb(main):001:0> puts 'Words cannot describe the joy I feel.'
@@ -80,11 +75,11 @@ you've diagnosed and fixed whatever the problem was.
80
75
 
81
76
  Additionally, if you want to run LiveConsole on a server, but run netcat
82
77
  locally, you can use SSH port forwarding to avoid having to open LiveConsole
83
- to the world:
78
+ to the world:
84
79
 
85
80
  ssh -L4000:localhost:4000 you@server
86
81
 
87
- Then, locally, you can do
82
+ Then, locally, you can do
88
83
 
89
84
  netcat localhost 4000
90
85
 
@@ -93,26 +88,27 @@ only works for the TCP socket mode.
93
88
 
94
89
  == Bugs
95
90
 
96
- LiveConsole lacks many of the niceties of IRB on the console, like Readline
97
- support.
91
+ LiveConsole lacks many of the niceties of IRB on the console, like full Readline
92
+ support. LiveConsole does offer tab completion if you write a client that sends
93
+ the phrase to complete, ending with a tab and newline. LiveConsole will activate the IRB
94
+ completion proc and send back the results, terminated by newline.
95
+
96
+ Readline history support can be added if anyone finds it useful to manage history
97
+ remotely.
98
+
99
+ For full Readline support, you can actually use the wonderful rlwrap program, which
100
+ wraps an arbitrary interactive program in readline. For example, to connect to
101
+ a LiveConsole on localhost:3333, use
102
+ rlwrap netcat localhost 3333
103
+ rlwrap is available with most Linux distributions or at
104
+ http://utopia.knoware.nl/~hlub/uck/rlwrap/ . It is seriously an incredibly
105
+ useful piece of software.
106
+
98
107
 
99
108
  Typing exit, hitting ^D, or sending signals (like INT or STOP) doesn't work.
100
109
  Just exit the program you used to connect to it. This has more to do with the
101
110
  program you use to connect to the socket.
102
111
 
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.
110
-
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.
114
-
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.
116
112
 
117
113
  Other than that, LiveConsole doesn't have any known bugs, but it is odd
118
114
  software that also monkey-patches IRB, so they are likely to be there. Bug
@@ -120,8 +116,9 @@ reports and patches gratefully accepted.
120
116
 
121
117
  == Credits
122
118
 
123
- Pete Elmore -- (pete.elmore(a)gmail.com)
119
+ Jennifer Hickey, project owner -- (jencompgeek(a)gmail.com)
124
120
 
125
- == Home page
121
+ Pete Elmore, original author -- (pete.elmore(a)gmail.com)
126
122
 
127
- http://debu.gs/live-console
123
+ Roger D. Pack (http://betterlogic.com/roger/) provided patches and Windows
124
+ support
File without changes
File without changes
@@ -1,227 +1,295 @@
1
1
  # LiveConsole
2
2
  # Pete Elmore (pete.elmore@gmail.com), 2007-10-18
3
- # debu.gs/live-console
3
+ # Jennifer Hickey
4
4
  # See doc/LICENSE.
5
5
 
6
6
  require 'irb'
7
7
  require 'irb/frame'
8
8
  require 'socket'
9
- require 'live_console_config'
9
+ require 'irb/completion'
10
10
 
11
11
  # LiveConsole provides a socket that can be connected to via netcat or telnet
12
- # to use to connect to an IRB session inside a running process. It creates a
13
- # thread that listens on the specified address/port or Unix Domain Socket path,
12
+ # to use to connect to an IRB session inside a running process. It listens on the
13
+ # specified address/port or Unix Domain Socket path,
14
14
  # and presents connecting clients with an IRB shell. Using this, you can
15
15
  # execute code on a running instance of a Ruby process to inspect the state or
16
- # even patch code on the fly. There is currently no readline support.
16
+ # even patch code on the fly.
17
17
  class LiveConsole
18
- include Socket::Constants
19
- autoload :IOMethods, 'live_console/io_methods'
20
-
21
- attr_accessor :io_method, :io, :thread, :bind
22
- private :io_method=, :io=, :thread=
23
-
24
- # call-seq:
25
- # # Bind a LiveConsole to localhost:3030 (only allow clients on this
26
- # # machine to connect):
27
- # LiveConsole.new :socket, :port => 3030
28
- # # Accept connections from anywhere on port 3030. Ridiculously insecure:
29
- # LiveConsole.new(:socket, :port => 3030, :host => '0.0.0.0')
30
- # # Use a Unix Domain Socket (which is more secure) instead:
31
- # LiveConsole.new(:unix_socket, :path => '/tmp/my_liveconsole.sock',
32
- # :mode => 0600, :uid => Process.uid, :gid => Process.gid)
33
- # # By default, the mode is 0600, and the uid and gid are those of the
34
- # # current process. These three options are for the file's permissions.
35
- # # You can also supply a binding for IRB's toplevel:
36
- # LiveConsole.new(:unix_socket,
37
- # :path => "/tmp/live_console_#{Process.pid}.sock", :bind => binding)
38
- #
39
- # Creates a new LiveConsole. You must next call LiveConsole#start when you
40
- # want to spawn the thread to accept connections and start the console.
41
- def initialize(io_method, opts = {})
42
- self.io_method = io_method.to_sym
43
- self.bind = opts.delete :bind
44
- unless IOMethods::List.include?(self.io_method)
45
- raise ArgumentError, "Unknown IO method: #{io_method}"
46
- end
47
-
48
- init_io opts
49
- end
50
-
51
- # LiveConsole#start spawns a thread to listen for, accept, and provide an
52
- # IRB console to new connections. If a thread is already running, this
53
- # method simply returns false; otherwise, it returns the new thread.
54
- def start
55
- if thread
56
- if thread.alive?
57
- return false
58
- else
59
- thread.join
60
- self.thread = nil
61
- end
62
- end
63
-
64
- self.thread = Thread.new {
65
- loop {
66
- Thread.pass
67
- if io.start
68
- irb_io = GenericIOMethod.new io.raw_input, io.raw_output
69
- begin
70
- IRB.start_with_io(irb_io, bind)
71
- rescue Errno::EPIPE => e
72
- io.stop
73
- end
74
- end
75
- }
76
- }
77
- thread
78
- end
79
-
80
- # Ends the running thread, if it exists. Returns true if a thread was
81
- # running, false otherwise.
82
- def stop
83
- if thread
84
- if thread == Thread.current
85
- self.thread = nil
86
- Thread.current.exit!
87
- end
88
-
89
- thread.exit
90
- if thread.join(0.1).nil?
91
- thread.exit!
92
- end
93
- self.thread = nil
94
- true
95
- else
96
- false
97
- end
98
- end
99
-
100
- # Restarts. Useful for binding changes. Return value is the same as for
101
- # LiveConsole#start.
102
- def restart
103
- r = lambda { stop; start }
104
- if thread == Thread.current
105
- Thread.new &r # Leaks a thread, but works.
106
- else
107
- r.call
108
- end
109
- end
110
-
111
- private
112
-
113
- def init_irb
114
- return if @@irb_inited_already
115
- IRB.setup nil
116
- @@irb_inited_already = true
117
- end
118
-
119
- def init_io opts
120
- self.io = IOMethods.send(io_method).new opts
121
- end
18
+ include Socket::Constants
19
+ autoload :IOMethods, 'live_console/io_methods'
20
+
21
+ attr_accessor :io_method, :io, :thread, :bind, :authenticate, :readline
22
+ private :io_method=, :io=, :thread=, :authenticate=, :readline=
23
+
24
+ # call-seq:
25
+ ## Bind a LiveConsole to localhost:3030 (only allow clients on this
26
+ ## machine to connect):
27
+ # LiveConsole.new :socket, :port => 3030
28
+ ## Accept connections from anywhere on port 3030. Ridiculously insecure:
29
+ # LiveConsole.new(:socket, :port => 3030, :host => '0.0.0.0')
30
+ ## Accept connections from anywhere on port 3030, secured with a plain-text credentials file
31
+ ## credentials_file should be of the form:
32
+ ### username: <username>
33
+ ### password: <password>
34
+ # LiveConsole.new(:socket, :port => 3030, :host => '0.0.0.0', :authenticate=>true,
35
+ # :credentials_file='/path/to/.consoleaccess)
36
+ ## Use a Unix Domain Socket (which is more secure) instead:
37
+ # LiveConsole.new(:unix_socket, :path => '/tmp/my_liveconsole.sock',
38
+ # :mode => 0600, :uid => Process.uid, :gid => Process.gid)
39
+ ## By default, the mode is 0600, and the uid and gid are those of the
40
+ ## current process. These three options are for the file's permissions.
41
+ ## You can also supply a binding for IRB's toplevel:
42
+ # LiveConsole.new(:unix_socket,
43
+ # :path => "/tmp/live_console_#{Process.pid}.sock", :bind => binding)
44
+ #
45
+ # Creates a new LiveConsole. You must next call LiveConsole#start or LiveConsole#start_blocking
46
+ # when you want to accept connections and start the console.
47
+ def initialize(io_method, opts = {})
48
+ self.io_method = io_method.to_sym
49
+ self.bind = opts.delete :bind
50
+ self.authenticate = opts.delete :authenticate
51
+ self.readline = opts.delete :readline
52
+ unless IOMethods::List.include?(self.io_method)
53
+ raise ArgumentError, "Unknown IO method: #{io_method}"
54
+ end
55
+ init_io opts
56
+ end
57
+
58
+ # LiveConsole#start spawns a thread to listen for, accept, and provide an
59
+ # IRB console to new connections. If a thread is already running, this
60
+ # method simply returns false; otherwise, it returns the new thread.
61
+ def start
62
+ if thread
63
+ if thread.alive?
64
+ return false
65
+ else
66
+ thread.join
67
+ self.thread = nil
68
+ end
69
+ end
70
+
71
+ self.thread = Thread.new {
72
+ start_blocking
73
+ }
74
+ thread
75
+ end
76
+
77
+ # LiveConsole#start_blocking executes a loop to listen for, accept,
78
+ # and provide an IRB console to new connections.
79
+ # This is a blocking call and the only way to stop it is to kill the process
80
+ def start_blocking
81
+ loop {
82
+ conn = io.get_connection
83
+ #This will block until a connection is made or a failure occurs
84
+ if conn.start
85
+ #fork a new process for the session to redirect stdout/stderr
86
+ pid = fork {
87
+ begin
88
+ start_irb = true
89
+ if authenticate && !conn.authenticate
90
+ conn.stop
91
+ start_irb = false
92
+ end
93
+ if start_irb
94
+ irb_io = GenericIOMethod.new conn.raw_input, conn.raw_output, readline
95
+ begin
96
+ IRB.start_with_io(irb_io, bind)
97
+ rescue Errno::EPIPE => e
98
+ conn.stop
99
+ end
100
+ end
101
+ rescue Exception => e
102
+ puts "Error during connection: #{e.message}"
103
+ conn.stop
104
+ end
105
+ }
106
+ Process.detach(pid)
107
+ end
108
+ }
109
+ end
110
+
111
+ # Ends the running thread, if it exists. Returns true if a thread was
112
+ # running, false otherwise.
113
+ def stop
114
+ if thread
115
+ if thread == Thread.current
116
+ self.thread = nil
117
+ Thread.current.exit!
118
+ end
119
+
120
+ thread.exit
121
+ if thread.join(0.1).nil?
122
+ thread.exit!
123
+ end
124
+ self.thread = nil
125
+ true
126
+ else
127
+ false
128
+ end
129
+ end
130
+
131
+ # Restarts. Useful for binding changes. Return value is the same as for
132
+ # LiveConsole#start.
133
+ def restart
134
+ r = lambda { stop; start }
135
+ if thread == Thread.current
136
+ Thread.new &r # Leaks a thread, but works.
137
+ else
138
+ r.call
139
+ end
140
+ end
141
+
142
+ private
143
+
144
+ def init_irb
145
+ return if @@irb_inited_already
146
+ IRB.setup nil
147
+ @@irb_inited_already = true
148
+ end
149
+
150
+ def init_io opts
151
+ self.io = IOMethods.send(io_method).new opts
152
+ end
122
153
  end
123
154
 
124
155
  # We need to make a couple of changes to the IRB module to account for using a
125
- # weird I/O method and re-starting IRB from time to time.
156
+ # weird I/O method and re-starting IRB from time to time.
126
157
  module IRB
127
- @inited = false
128
-
129
- ARGV = []
130
-
131
- # Overridden a la FXIrb to accomodate our needs.
132
- def IRB.start_with_io(io, bind, &block)
133
- unless @inited
134
- setup '/dev/null'
135
- IRB.parse_opts
136
- IRB.load_modules
137
- @inited = true
138
- end
139
-
140
- bind ||= IRB::Frame.top(1)
141
- ws = IRB::WorkSpace.new(bind)
142
- irb = Irb.new(ws, io, io)
143
-
144
- @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
145
- @CONF[:MAIN_CONTEXT] = irb.context
146
- @CONF[:PROMPT_MODE] = :INF_RUBY
147
-
148
- catch(:IRB_EXIT) {
149
- begin
150
- irb.eval_input
151
- rescue StandardError => e
152
- irb.print([e.to_s, e.backtrace].flatten.join("\n") + "\n")
153
- retry
154
- end
155
- }
156
- irb.print "\n"
157
- end
158
-
159
- class Context
160
- # Fix an IRB bug; it ignores your output method.
161
- def output *args
162
- @output_method.print *args
163
- end
164
- end
165
-
166
- class Irb
167
- # Fix an IRB bug; it ignores your output method.
168
- def printf(*args)
169
- context.output(sprintf(*args))
170
- end
171
-
172
- # Fix an IRB bug; it ignores your output method.
173
- def print(*args)
174
- context.output *args
175
- end
176
- end
158
+ @inited = false
159
+
160
+ ARGV = []
161
+
162
+ # Overridden a la FXIrb to accomodate our needs.
163
+ def IRB.start_with_io(io, bind, &block)
164
+ unless @inited
165
+ setup '/dev/null'
166
+ IRB.parse_opts
167
+ IRB.load_modules
168
+ @inited = true
169
+ end
170
+
171
+ #Redirect stdout and stderr to cover output of Ruby commands like puts
172
+ $stdout.sync=true
173
+ $stderr.sync=true
174
+ $stdout.reopen(io.output)
175
+ $stderr.reopen(io.output)
176
+ ws = IRB::WorkSpace.new(bind)
177
+
178
+ @CONF[:PROMPT_MODE] = :DEFAULT
179
+ #Remove the context from all prompts-can be too long depending on binding
180
+ @CONF[:PROMPT][:DEFAULT][:PROMPT_I] = "%N():%03n:%i> "
181
+ @CONF[:PROMPT][:DEFAULT][:PROMPT_N] = "%N():%03n:%i> "
182
+ #Add > to S and C as they don't get picked up by telnet prompt scan
183
+ @CONF[:PROMPT][:DEFAULT][:PROMPT_S] = "%N():%03n:%i%l> "
184
+ @CONF[:PROMPT][:DEFAULT][:PROMPT_C] = "%N():%03n:%i*> "
185
+
186
+ @CONF[:USE_READLINE] = false
187
+ irb = Irb.new(ws, io, io)
188
+ bind ||= IRB::Frame.top(1) rescue TOPLEVEL_BINDING
189
+
190
+ @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
191
+ @CONF[:MAIN_CONTEXT] = irb.context
192
+
193
+ catch(:IRB_EXIT) {
194
+ begin
195
+ irb.eval_input
196
+ rescue StandardError => e
197
+ irb.print([e.to_s, e.backtrace].flatten.join("\n") + "\n")
198
+ retry
199
+ end
200
+ }
201
+ irb.print "\n"
202
+ end
203
+
204
+ class Context
205
+ # Fix an IRB bug; it ignores your output method.
206
+ def output *args
207
+ @output_method.print *args
208
+ end
209
+ end
210
+
211
+ class Irb
212
+ # Fix an IRB bug; it ignores your output method.
213
+ def printf(*args)
214
+ context.output(sprintf(*args))
215
+ end
216
+
217
+ # Fix an IRB bug; it ignores your output method.
218
+ def print(*args)
219
+ context.output *args
220
+ end
221
+ end
177
222
  end
178
223
 
179
224
  # The GenericIOMethod is a class that wraps I/O for IRB.
180
225
  class GenericIOMethod < IRB::StdioInputMethod
181
- # call-seq:
182
- # GenericIOMethod.new io
183
- # GenericIOMethod.new input, output
184
- #
185
- # Creates a GenericIOMethod, using either a single object for both input
186
- # and output, or one object for input and another for output.
187
- def initialize(input, output = nil)
188
- @input, @output = input, output
189
- @line = []
190
- @line_no = 0
191
- end
192
-
193
- attr_reader :input
194
- def output
195
- @output || input
196
- end
197
-
198
- def gets
199
- output.print @prompt
200
- output.flush
201
- @line[@line_no += 1] = input.gets
202
- # @io.flush # Not sure this is needed.
203
- @line[@line_no]
204
- end
205
-
206
- # Returns the user input history.
207
- def lines
208
- @line.dup
209
- end
210
-
211
- def print(*a)
212
- output.print *a
213
- end
214
-
215
- def file_name
216
- input.inspect
217
- end
218
-
219
- def eof?
220
- input.eof?
221
- end
222
-
223
- def close
224
- input.close
225
- output.close if @output
226
- end
226
+ # call-seq:
227
+ # GenericIOMethod.new io
228
+ # GenericIOMethod.new input, output
229
+ #
230
+ # Creates a GenericIOMethod, using either a single object for both input
231
+ # and output, or one object for input and another for output.
232
+ def initialize(input, output = nil, readline=false)
233
+ @input, @output = input, output
234
+ @line = []
235
+ @line_no = 0
236
+ @readline = readline
237
+ end
238
+
239
+ attr_reader :input
240
+ def output
241
+ @output || input
242
+ end
243
+
244
+ def gets
245
+ output.print @prompt
246
+ output.flush
247
+ line = input.gets
248
+ if @readline
249
+ #Return tab completion data as long as we receive input that ends in '\t'
250
+ while line.chomp =~ /\t\z/n
251
+ run_completion_proc line.chomp.chop
252
+ line = input.gets
253
+ end
254
+ end
255
+ @line[@line_no += 1] = line
256
+ @line[@line_no]
257
+ end
258
+
259
+ # Returns the user input history.
260
+ def lines
261
+ @line.dup
262
+ end
263
+
264
+ def print(*a)
265
+ output.print *a
266
+ end
267
+
268
+ def file_name
269
+ input.inspect
270
+ end
271
+
272
+ def eof?
273
+ input.eof?
274
+ end
275
+
276
+ def encoding
277
+ input.external_encoding
278
+ end
279
+
280
+ def close
281
+ input.close
282
+ output.close if @output
283
+ end
284
+
285
+ private
286
+ # Runs the IRB CompletionProc with the given argument and writes the array to output stream as
287
+ # a comma-separated String, terminated by newline (so clients know when all data received)
288
+ def run_completion_proc(line)
289
+ opts = IRB::InputCompletor::CompletionProc.call(line)
290
+ opts.compact!
291
+ optstrng = opts.join(",") + "\n"
292
+ output.print optstrng
293
+ output.flush
294
+ end
227
295
  end
@@ -1,53 +1,46 @@
1
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
2
+ List = []
3
+
4
+ Dir[File.join(File.dirname(__FILE__), 'io_methods', '*')].each { |dir|
5
+ entry = dir + '/' + File.basename(dir)
6
+ fname = entry.sub /\.rb$/, ''
7
+ classname = File.basename(entry,'.rb').capitalize.
8
+ gsub(/_(\w)/) { $1.upcase }.sub(/io$/i, 'IO').to_sym
9
+ mname = File.basename(fname).sub(/_io$/, '').to_sym
10
+
11
+ autoload classname, fname
12
+ List << mname
13
+
14
+ define_method(mname) {
15
+ const_get classname
16
+ }
17
+ }
18
+ List.freeze
19
+
20
+ extend self
21
+
22
+ module IOMethod
23
+ def initialize(opts)
24
+ self.opts = self.class::DefaultOpts.merge opts
25
+ unless missing_opts.empty?
26
+ raise ArgumentError, "Missing opts for " \
27
+ "#{self.class.name}: #{missing_opts.inspect}"
28
+ end
29
+ end
30
+
31
+ def missing_opts
32
+ self.class::RequiredOpts - opts.keys
33
+ end
34
+
35
+ def self.included(other)
36
+ other.instance_eval {
37
+ readers = [:opts]
38
+ attr_accessor *readers
39
+ private *readers.map { |r| (r.to_s + '=').to_sym }
40
+ other::RequiredOpts.each { |opt|
41
+ define_method(opt) { opts[opt] }
42
+ }
43
+ }
44
+ end
45
+ end
53
46
  end
@@ -0,0 +1,52 @@
1
+ require 'yaml'
2
+
3
+ class LiveConsole::IOMethods::SocketIOConnection
4
+
5
+ attr_accessor :raw_input, :raw_output
6
+
7
+ def initialize(server, opts)
8
+ @server = server
9
+ @opts = opts
10
+ end
11
+
12
+ def start
13
+ begin
14
+ IO.select([@server])
15
+ self.raw_input = self.raw_output = @server.accept
16
+ return true
17
+ rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO,
18
+ Errno::EINTR => e
19
+ select
20
+ retry
21
+ end
22
+ end
23
+
24
+ def stop
25
+ select
26
+ raw_input.close rescue nil
27
+ end
28
+
29
+ def select
30
+ IO.select [@server], [], [], 1 if @server
31
+ end
32
+
33
+ # Retrieves credentials from I/O and matches them against the specified file
34
+ # credentials_file should be of the form:
35
+ # username: <username>
36
+ # password: <password>
37
+ def authenticate
38
+ authenticated = true
39
+ raw_output.print "Login: "
40
+ raw_output.flush
41
+ username = raw_input.gets
42
+ raw_output.print "Password: "
43
+ password = raw_input.gets
44
+ credentials_file= @opts[:credentials_file] || '.consoleaccess'
45
+ credentials =YAML.load_file(credentials_file)
46
+ if username.chomp != credentials['username'] || password.chomp != credentials['password']
47
+ raw_output.puts "Login failed."
48
+ authenticated = false
49
+ end
50
+ authenticated
51
+ end
52
+ end
@@ -0,0 +1,19 @@
1
+ require 'live_console/io_methods/socket_io/connection'
2
+
3
+ class LiveConsole::IOMethods::SocketIO
4
+ DefaultOpts = {
5
+ :host => '127.0.0.1',
6
+ }.freeze
7
+ RequiredOpts = DefaultOpts.keys + [:port]
8
+
9
+ include LiveConsole::IOMethods::IOMethod
10
+
11
+ def initialize(opts)
12
+ super opts
13
+ @server = TCPServer.new host, port
14
+ end
15
+
16
+ def get_connection
17
+ LiveConsole::IOMethods::SocketIOConnection.new(@server, opts)
18
+ end
19
+ end
@@ -0,0 +1,36 @@
1
+ require 'socket'
2
+
3
+ class LiveConsole::IOMethods::UnixSocketConnection
4
+
5
+ attr_accessor :raw_input, :raw_output
6
+
7
+ def initialize(server, opts)
8
+ @server = server
9
+ @opts = opts
10
+ end
11
+
12
+ def start
13
+ begin
14
+ self.raw_input = self.raw_output = @server.accept_nonblock
15
+ raw_input.sync = true
16
+ return true
17
+ rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO,
18
+ Errno::EINTR => e
19
+ select
20
+ retry
21
+ end
22
+ end
23
+
24
+ def stop
25
+ select
26
+ raw_input.close
27
+ end
28
+
29
+ def select
30
+ IO.select [@server], [], [], 1 if @server
31
+ end
32
+
33
+ def authenticate
34
+ true
35
+ end
36
+ end
@@ -0,0 +1,22 @@
1
+ require 'live_console/io_methods/unix_socket/connection'
2
+ require 'socket'
3
+
4
+ class LiveConsole::IOMethods::UnixSocket
5
+ DefaultOpts = {
6
+ :mode => 0600,
7
+ :uid => Process.uid,
8
+ :gid => Process.gid,
9
+ }
10
+ RequiredOpts = DefaultOpts.keys + [:path]
11
+
12
+ include LiveConsole::IOMethods::IOMethod
13
+
14
+ def initialize(opts)
15
+ super opts
16
+ @server = UNIXServer.new path
17
+ end
18
+
19
+ def get_connection
20
+ LiveConsole::IOMethods::UnixSocketConnection.new(@server,opts)
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module LiveConsole
2
+ VERSION = '0.2.4'
3
+ end
metadata CHANGED
@@ -1,68 +1,62 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: live_console
3
- version: !ruby/object:Gem::Version
4
- version: 0.2.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.4
5
+ prerelease:
5
6
  platform: ruby
6
- authors:
7
+ authors:
7
8
  - Pete Elmore
9
+ - Jennifer Hickey
8
10
  autorequire:
9
11
  bindir: bin
10
12
  cert_chain: []
11
-
12
- date: 2009-02-18 00:00:00 -08:00
13
- default_executable:
13
+ date: 2012-03-11 00:00:00.000000000Z
14
14
  dependencies: []
15
-
16
- description:
17
- email: pete.elmore@gmail.com
18
- executables:
15
+ description: A library to support adding an irb console to your running application.
16
+ email: jencompgeek@gmail.com
17
+ executables:
19
18
  - udscat
20
19
  extensions: []
21
-
22
- extra_rdoc_files:
23
- - doc/README
24
- - doc/lc_example.rb
25
- - doc/lc_unix_example.rb
26
- - doc/LICENSE
27
- files:
28
- - lib/live_console_config.rb
29
- - lib/live_console.rb
30
- - lib/live_console
31
- - lib/live_console/io_methods
32
- - lib/live_console/io_methods/unix_socket_io.rb
33
- - lib/live_console/io_methods/socket_io.rb
34
- - lib/live_console/io_methods.rb
35
- - doc/README
36
- - doc/lc_example.rb
37
- - doc/lc_unix_example.rb
38
- - doc/LICENSE
20
+ extra_rdoc_files:
21
+ - LICENSE
22
+ - README
23
+ - examples/lc_example.rb
24
+ - examples/lc_unix_example.rb
25
+ files:
26
+ - LICENSE
27
+ - README
28
+ - examples/lc_example.rb
29
+ - examples/lc_unix_example.rb
39
30
  - bin/udscat
40
- - Rakefile
41
- has_rdoc: true
42
- homepage: http://debu.gs/live-console
31
+ - lib/live_console/io_methods/socket_io/connection.rb
32
+ - lib/live_console/io_methods/socket_io/socket_io.rb
33
+ - lib/live_console/io_methods/unix_socket/connection.rb
34
+ - lib/live_console/io_methods/unix_socket/unix_socket.rb
35
+ - lib/live_console/io_methods.rb
36
+ - lib/live_console.rb
37
+ - lib/version.rb
38
+ homepage: https://github.com/jencompgeek/live-console
39
+ licenses: []
43
40
  post_install_message:
44
41
  rdoc_options: []
45
-
46
- require_paths:
42
+ require_paths:
47
43
  - lib
48
- required_ruby_version: !ruby/object:Gem::Requirement
49
- requirements:
50
- - - ">="
51
- - !ruby/object:Gem::Version
52
- version: "0"
53
- version:
54
- required_rubygems_version: !ruby/object:Gem::Requirement
55
- requirements:
56
- - - ">="
57
- - !ruby/object:Gem::Version
58
- version: "0"
59
- version:
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
60
56
  requirements: []
61
-
62
- rubyforge_project: live-console
63
- rubygems_version: 1.3.1
57
+ rubyforge_project:
58
+ rubygems_version: 1.8.17
64
59
  signing_key:
65
- specification_version: 2
60
+ specification_version: 3
66
61
  summary: A library to support adding an irb console to your running application.
67
62
  test_files: []
68
-
data/Rakefile DELETED
@@ -1,88 +0,0 @@
1
- require 'rake/gempackagetask'
2
- require 'rake/rdoctask'
3
- require 'lib/live_console_config'
4
- require 'fileutils'
5
-
6
- $: << "#{File.dirname(__FILE__)}/lib"
7
-
8
- spec = Gem::Specification.new { |s|
9
- s.name = LiveConsoleConfig::PkgName
10
- s.version = LiveConsoleConfig::Version
11
- s.author = LiveConsoleConfig::Authors
12
- s.email = LiveConsoleConfig::Email
13
- s.homepage = LiveConsoleConfig::URL
14
- s.rubyforge_project = LiveConsoleConfig::Project
15
-
16
- s.platform = Gem::Platform::RUBY
17
-
18
- s.files = Dir["{lib,doc,bin,ext}/**/*"].delete_if {|f|
19
- /\/rdoc(\/|$)/i.match f
20
- } + %w(Rakefile)
21
- s.require_path = 'lib'
22
- s.has_rdoc = true
23
- s.extra_rdoc_files = Dir['doc/*'].select(&File.method(:file?))
24
- s.extensions << 'ext/extconf.rb' if File.exist? 'ext/extconf.rb'
25
- Dir['bin/*'].map(&File.method(:basename)).map(&s.executables.method(:<<))
26
-
27
- s.summary = 'A library to support adding an irb console to your ' \
28
- 'running application.'
29
- %w().each &s.method(:add_dependency)
30
- }
31
-
32
- Rake::RDocTask.new(:doc) { |t|
33
- t.main = 'doc/README'
34
- t.rdoc_files.include 'lib/**/*.rb', 'doc/*', 'bin/*', 'ext/**/*.c',
35
- 'ext/**/*.rb'
36
- t.options << '-S' << '-N'
37
- t.rdoc_dir = 'doc/rdoc'
38
- }
39
-
40
- Rake::GemPackageTask.new(spec) { |pkg|
41
- pkg.need_tar_bz2 = true
42
- }
43
-
44
- desc "Builds and installs the gem for #{spec.name}"
45
- task(:install => :package) {
46
- g = "pkg/#{spec.name}-#{spec.version}.gem"
47
- system "sudo gem install -l #{g}"
48
- }
49
-
50
- desc "Runs IRB, automatically require()ing #{spec.name}."
51
- task(:irb) {
52
- exec "irb -Ilib -r#{spec.name}"
53
- }
54
-
55
- desc "Cleans up the pkg directory."
56
- task(:clean) {
57
- FileUtils.rm_rf 'pkg'
58
- }
59
-
60
-
61
- desc "Generates a static gemspec file; useful for github."
62
- task(:static_gemspec) {
63
- # This whole thing is hacky.
64
- spec.validate
65
- spec_attrs = %w(
66
- platform author email files require_path has_rdoc extra_rdoc_files
67
- extensions executables name summary homepage
68
- ).map { |attr|
69
- "\ts.#{attr} = #{spec.send(attr).inspect}\n"
70
- }.join <<
71
- "\ts.version = #{spec.version.to_s.inspect}\n" <<
72
- spec.dependencies.map { |dep|
73
- "\ts.add_dependency #{dep.inspect}\n"
74
- }.join
75
-
76
- File.open("#{spec.name}.gemspec", 'w') { |f|
77
- f.print <<-EOGEMSPEC
78
- # This is a static gempsec automatically generated by rake. It's better to
79
- # edit the Rakefile than this file. It is kept in the repository for the
80
- # benefit of github.
81
-
82
- spec = Gem::Specification.new { |s|
83
- #{spec_attrs}}
84
-
85
- Gem::Builder.new(spec).build if __FILE__ == $0
86
- EOGEMSPEC
87
- }
88
- }
@@ -1,27 +0,0 @@
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
@@ -1,31 +0,0 @@
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
@@ -1,9 +0,0 @@
1
- # This module houses a pile of informative constants for LiveConsole.
2
- module LiveConsoleConfig
3
- Authors = 'Pete Elmore'
4
- Email = 'pete.elmore@gmail.com'
5
- PkgName = 'live_console'
6
- Version = '0.2.1'
7
- URL = 'http://debu.gs/live-console'
8
- Project = 'live-console'
9
- end