forked from basho/riak-ruby-client
-
Notifications
You must be signed in to change notification settings - Fork 0
/
console.rb
136 lines (122 loc) · 3.99 KB
/
console.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
require 'expect'
require 'pathname'
require 'riak/util/translation'
if ENV['DEBUG_RIAK_CONSOLE']
$expect_verbose = true
end
module Riak
class Node
# Eases working with the Erlang console when attached to the Riak
# node.
class Console
include Util::Translation
# @return [String] the name of the connected node
attr_accessor :nodename
# Opens a {Console} by connecting to the node.
# @return [Console] the opened console
def self.open(node)
new node.pipe, node.name
end
# Creates a {Console} from the IO pipes connected to the node.
# @param [String,Pathname] pipedir path to the pipes opened by
# run_erl
# @param [String] nodename the name of the node the Console will
# be attached to
def initialize(pipedir, nodename)
@nodename = nodename
@mutex = Mutex.new
@winch = Signal.trap("WINCH", &method(:handle_winch))
@prompt = /\(#{Regexp.escape(nodename)}\)\d+>\s*/
pipedir = Pathname(pipedir)
pipedir.children.each do |path|
debug "Non-pipe found! #{path}" unless path.pipe?
if path.fnmatch("*.r") # Read pipe
# debug "Found read pipe: #{path}"
@rfile ||= path
elsif path.fnmatch("*.w") # Write pipe
# debug "Found write pipe: #{path}"
@wfile ||= path
end
end
raise ArgumentError, t('no_pipes', :path => pipedir.to_s) if [@rfile, @wfile].any? {|p| p.nil? }
# We have to open the read pipe AFTER we have sent some data
# to the write pipe, otherwise JRuby hangs.
begin
debug "Opening write pipe."
@w = open_write_pipe
@w.sync = true
debug "Opening read pipe."
@r = open_read_pipe
command 'ok.'
debug "Initialized console: #{@r.inspect} #{@w.inspect}"
rescue => e
debug e.message
close
raise t('no_pipes', :path => pipedir.to_s) + "[ #{e.message} ]"
end
end
# Sends an Erlang command to the console
# @param [String] cmd an Erlang expression to send to the node
def command(cmd)
@mutex.synchronize do
begin
debug "Sending command #{cmd.inspect}"
@w.print "#{cmd}\n"
wait_for_erlang_prompt
rescue SystemCallError
close
end
end
end
# Detects whether the console connection is still open, that is,
# if the node hasn't disconnected from the other side of the
# pipe.
def open?
(@r && !@r.closed?) && (@w && !@w.closed?)
end
# Scans the output of the console until an Erlang shell prompt
# is found. Called by {#command} to ensure that the submitted
# command succeeds.
def wait_for_erlang_prompt
wait_for @prompt
end
# Scans the output of the console for the given pattern.
# @param [String, Regexp] pattern the pattern to scan for
def wait_for(pattern)
debug "Scanning for #{pattern.inspect}"
@r.expect(pattern)
end
# Closes the console by detaching from the pipes.
def close
@r.close if @r && !@r.closed?
@w.close if @w && !@w.closed?
Signal.trap("WINCH", @winch)
freeze
end
protected
# Handles the "window change" signal by faking it.
def handle_winch
debug "WINCHED!"
@w.print "\033_winsize=80,26\033\\"
Signal.trap("WINCH", &method(:handle_winch))
end
def debug(msg)
$stderr.puts msg if ENV["DEBUG_RIAK_CONSOLE"]
end
def open_write_pipe
if defined?(::Java)
java.io.FileOutputStream.new(@wfile.to_s).to_io
else
@wfile.open(File::WRONLY|File::NONBLOCK)
end
end
def open_read_pipe
if defined?(::Java)
IO.popen("cat #{@rfile}", "rb")
else
@rfile.open(File::RDONLY|File::NONBLOCK)
end
end
end
end
end