sup 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sup might be problematic. Click here for more details.
- data/History.txt +14 -1
- data/Manifest.txt +4 -1
- data/README.txt +13 -7
- data/Rakefile +3 -3
- data/bin/sup +17 -8
- data/bin/sup-add +20 -14
- data/bin/sup-config +222 -0
- data/bin/sup-dump +31 -0
- data/bin/sup-sync +235 -0
- data/doc/FAQ.txt +52 -25
- data/doc/Philosophy.txt +19 -19
- data/doc/TODO +37 -11
- data/doc/UserGuide.txt +42 -41
- data/lib/sup.rb +48 -4
- data/lib/sup/buffer.rb +22 -5
- data/lib/sup/draft.rb +2 -2
- data/lib/sup/imap.rb +20 -30
- data/lib/sup/index.rb +42 -54
- data/lib/sup/logger.rb +1 -1
- data/lib/sup/maildir.rb +127 -0
- data/lib/sup/mbox/loader.rb +40 -38
- data/lib/sup/mbox/ssh-file.rb +16 -25
- data/lib/sup/mbox/ssh-loader.rb +2 -11
- data/lib/sup/message.rb +14 -8
- data/lib/sup/mode.rb +1 -1
- data/lib/sup/modes/edit-message-mode.rb +1 -1
- data/lib/sup/modes/inbox-mode.rb +4 -1
- data/lib/sup/modes/line-cursor-mode.rb +3 -2
- data/lib/sup/modes/reply-mode.rb +1 -1
- data/lib/sup/modes/thread-index-mode.rb +6 -6
- data/lib/sup/modes/thread-view-mode.rb +24 -11
- data/lib/sup/poll.rb +45 -36
- data/lib/sup/sent.rb +3 -3
- data/lib/sup/source.rb +29 -47
- data/lib/sup/thread.rb +1 -1
- data/lib/sup/util.rb +30 -5
- metadata +12 -7
- data/bin/sup-import +0 -159
data/lib/sup/index.rb
CHANGED
@@ -30,6 +30,7 @@ class Index
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def save
|
33
|
+
Redwood::log "saving index and sources..."
|
33
34
|
FileUtils.mkdir_p @dir unless File.exists? @dir
|
34
35
|
save_sources
|
35
36
|
save_index
|
@@ -72,25 +73,46 @@ class Index
|
|
72
73
|
end
|
73
74
|
end
|
74
75
|
|
75
|
-
##
|
76
|
-
##
|
77
|
-
## unless given.
|
76
|
+
## Syncs the message to the index: deleting if it's already there,
|
77
|
+
## and adding either way. Index state will be determined by m.labels.
|
78
78
|
##
|
79
|
-
##
|
80
|
-
|
81
|
-
|
82
|
-
unless docid && entry
|
83
|
-
docid, entry = load_entry_for_id m.id
|
84
|
-
raise ArgumentError, "cannot find #{m.id} in the index" unless entry
|
85
|
-
end
|
79
|
+
## docid and entry can be specified if they're already known.
|
80
|
+
def sync_message m, docid=nil, entry=nil
|
81
|
+
docid, entry = load_entry_for_id m.id unless docid && entry
|
86
82
|
|
87
|
-
raise "no
|
83
|
+
raise "no source info for message #{m.id}" unless m.source && m.source_info
|
84
|
+
raise "trying deleting non-corresponding entry #{docid}" if docid && @index[docid][:message_id] != m.id
|
88
85
|
|
89
|
-
|
86
|
+
source_id =
|
87
|
+
if m.source.is_a? Integer
|
88
|
+
raise "Debugging: integer source set"
|
89
|
+
m.source
|
90
|
+
else
|
91
|
+
m.source.id or raise "unregistered source #{m.source} (id #{m.source.id.inspect})"
|
92
|
+
end
|
90
93
|
|
91
|
-
|
92
|
-
|
94
|
+
to = (m.to + m.cc + m.bcc).map { |x| x.email }.join(" ")
|
95
|
+
d = {
|
96
|
+
:message_id => m.id,
|
97
|
+
:source_id => source_id,
|
98
|
+
:source_info => m.source_info,
|
99
|
+
:date => m.date.to_indexable_s,
|
100
|
+
:body => m.content,
|
101
|
+
:snippet => m.snippet,
|
102
|
+
:label => m.labels.join(" "),
|
103
|
+
:from => m.from ? m.from.email : "",
|
104
|
+
:to => (m.to + m.cc + m.bcc).map { |x| x.email }.join(" "),
|
105
|
+
:subject => wrap_subj(Message.normalize_subj(m.subj)),
|
106
|
+
:refs => (m.refs + m.replytos).uniq.join(" "),
|
107
|
+
}
|
108
|
+
|
109
|
+
@index.delete docid if docid
|
110
|
+
@index.add_document d
|
111
|
+
|
93
112
|
docid, entry = load_entry_for_id m.id
|
113
|
+
## this hasn't been triggered in a long time. TODO: decide whether it's still a problem.
|
114
|
+
raise "just added message #{m.id} but couldn't find it in a search" unless docid
|
115
|
+
true
|
94
116
|
end
|
95
117
|
|
96
118
|
def save_index fn=File.join(@dir, "ferret")
|
@@ -210,41 +232,6 @@ class Index
|
|
210
232
|
def wrap_subj subj; "__START_SUBJECT__ #{subj} __END_SUBJECT__"; end
|
211
233
|
def unwrap_subj subj; subj =~ /__START_SUBJECT__ (.*?) __END_SUBJECT__/ && $1; end
|
212
234
|
|
213
|
-
## Adds a message to the index. The message cannot already exist in
|
214
|
-
## the index.
|
215
|
-
def add_message m
|
216
|
-
raise ArgumentError, "index already contains #{m.id}" if contains? m
|
217
|
-
|
218
|
-
source_id =
|
219
|
-
if m.source.is_a? Integer
|
220
|
-
m.source
|
221
|
-
else
|
222
|
-
m.source.id or raise "unregistered source #{m.source} (id #{m.source.id.inspect})"
|
223
|
-
end
|
224
|
-
|
225
|
-
to = (m.to + m.cc + m.bcc).map { |x| x.email }.join(" ")
|
226
|
-
d = {
|
227
|
-
:message_id => m.id,
|
228
|
-
:source_id => source_id,
|
229
|
-
:source_info => m.source_info,
|
230
|
-
:date => m.date.to_indexable_s,
|
231
|
-
:body => m.content,
|
232
|
-
:snippet => m.snippet,
|
233
|
-
:label => m.labels.join(" "),
|
234
|
-
:from => m.from ? m.from.email : "",
|
235
|
-
:to => (m.to + m.cc + m.bcc).map { |x| x.email }.join(" "),
|
236
|
-
:subject => wrap_subj(Message.normalize_subj(m.subj)),
|
237
|
-
:refs => (m.refs + m.replytos).uniq.join(" "),
|
238
|
-
}
|
239
|
-
|
240
|
-
@index.add_document d
|
241
|
-
|
242
|
-
docid, entry = load_entry_for_id m.id
|
243
|
-
## this hasn't been triggered in a long time. TODO: decide whether it's still a problem.
|
244
|
-
raise "just added message #{m.id} but couldn't find it in a search" unless docid
|
245
|
-
true
|
246
|
-
end
|
247
|
-
|
248
235
|
def drop_entry docno; @index.delete docno; end
|
249
236
|
|
250
237
|
def load_entry_for_id mid
|
@@ -285,6 +272,12 @@ class Index
|
|
285
272
|
contacts.keys.compact
|
286
273
|
end
|
287
274
|
|
275
|
+
def load_sources fn=Redwood::SOURCE_FN
|
276
|
+
source_array = (Redwood::load_yaml_obj(fn) || []).map { |o| Recoverable.new o }
|
277
|
+
@sources = Hash[*(source_array).map { |s| [s.id, s] }.flatten]
|
278
|
+
@sources_dirty = false
|
279
|
+
end
|
280
|
+
|
288
281
|
protected
|
289
282
|
|
290
283
|
def parse_user_query_string str; @qparser.parse str; end
|
@@ -308,11 +301,6 @@ protected
|
|
308
301
|
query
|
309
302
|
end
|
310
303
|
|
311
|
-
def load_sources fn=Redwood::SOURCE_FN
|
312
|
-
@sources = Hash[*(Redwood::load_yaml_obj(fn) || []).map { |s| [s.id, s] }.flatten]
|
313
|
-
@sources_dirty = false
|
314
|
-
end
|
315
|
-
|
316
304
|
def save_sources fn=Redwood::SOURCE_FN
|
317
305
|
if @sources_dirty || @sources.any? { |id, s| s.dirty? }
|
318
306
|
bakfn = fn + ".bak"
|
data/lib/sup/logger.rb
CHANGED
@@ -26,7 +26,7 @@ class Logger
|
|
26
26
|
# $stderr.puts s
|
27
27
|
make_buf
|
28
28
|
@mode << "#{Time.now}: #{s.chomp}\n"
|
29
|
-
$stderr.puts "[#{Time.now}] #{s.chomp}" unless @mode.buffer
|
29
|
+
$stderr.puts "[#{Time.now}] #{s.chomp}" unless BufferManager.instantiated? && @mode.buffer
|
30
30
|
end
|
31
31
|
|
32
32
|
def self.method_missing m, *a
|
data/lib/sup/maildir.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'rmail'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Redwood
|
5
|
+
|
6
|
+
## Maildir doesn't provide an ordered unique id, which is what Sup
|
7
|
+
## requires to be really useful. So we must maintain, in memory, a
|
8
|
+
## mapping between Sup "ids" (timestamps, essentially) and the
|
9
|
+
## pathnames on disk.
|
10
|
+
|
11
|
+
class Maildir < Source
|
12
|
+
SCAN_INTERVAL = 30 # seconds
|
13
|
+
|
14
|
+
def initialize uri, last_date=nil, usual=true, archived=false, id=nil
|
15
|
+
super
|
16
|
+
uri = URI(uri)
|
17
|
+
|
18
|
+
raise ArgumentError, "not a maildir URI" unless uri.scheme == "maildir"
|
19
|
+
raise ArgumentError, "maildir URI cannot have a host: #{uri.host}" if uri.host
|
20
|
+
|
21
|
+
@dir = uri.path
|
22
|
+
@ids = []
|
23
|
+
@ids_to_fns = {}
|
24
|
+
@last_scan = nil
|
25
|
+
@mutex = Mutex.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def check
|
29
|
+
scan_mailbox
|
30
|
+
start = @ids.index(cur_offset || start_offset) or raise OutOfSyncSourceError, "Unknown message id #{cur_offset || start_offset}." # couldn't find the most recent email
|
31
|
+
end
|
32
|
+
|
33
|
+
def load_header id
|
34
|
+
scan_mailbox
|
35
|
+
with_file_for(id) { |f| MBox::read_header f }
|
36
|
+
end
|
37
|
+
|
38
|
+
def load_message id
|
39
|
+
scan_mailbox
|
40
|
+
with_file_for(id) { |f| RMail::Parser.read f }
|
41
|
+
end
|
42
|
+
|
43
|
+
def raw_header id
|
44
|
+
scan_mailbox
|
45
|
+
ret = ""
|
46
|
+
with_file_for(id) do |f|
|
47
|
+
until f.eof? || (l = f.gets) =~ /^$/
|
48
|
+
ret += l
|
49
|
+
end
|
50
|
+
end
|
51
|
+
ret
|
52
|
+
end
|
53
|
+
|
54
|
+
def raw_full_message id
|
55
|
+
scan_mailbox
|
56
|
+
with_file_for(id) { |f| f.readlines.join }
|
57
|
+
end
|
58
|
+
|
59
|
+
def scan_mailbox
|
60
|
+
return if @last_scan && (Time.now - @last_scan) < SCAN_INTERVAL
|
61
|
+
|
62
|
+
cdir = File.join(@dir, 'cur')
|
63
|
+
ndir = File.join(@dir, 'new')
|
64
|
+
|
65
|
+
raise FatalSourceError, "#{cdir} not a directory" unless File.directory? cdir
|
66
|
+
raise FatalSourceError, "#{ndir} not a directory" unless File.directory? ndir
|
67
|
+
|
68
|
+
begin
|
69
|
+
@ids, @ids_to_fns = @mutex.synchronize do
|
70
|
+
ids, ids_to_fns = [], {}
|
71
|
+
(Dir[File.join(cdir, "*")] + Dir[File.join(ndir, "*")]).map do |fn|
|
72
|
+
id = make_id fn
|
73
|
+
ids << id
|
74
|
+
ids_to_fns[id] = fn
|
75
|
+
end
|
76
|
+
[ids.sort, ids_to_fns]
|
77
|
+
end
|
78
|
+
rescue SystemCallError, IOError => e
|
79
|
+
raise FatalSourceError, "Problem scanning Maildir directories: #{e.message}."
|
80
|
+
end
|
81
|
+
|
82
|
+
@last_scan = Time.now
|
83
|
+
end
|
84
|
+
|
85
|
+
def each
|
86
|
+
scan_mailbox
|
87
|
+
start = @ids.index(cur_offset || start_offset) or raise OutOfSyncSourceError, "Unknown message id #{cur_offset || start_offset}." # couldn't find the most recent email
|
88
|
+
|
89
|
+
start.upto(@ids.length - 1) do |i|
|
90
|
+
id = @ids[i]
|
91
|
+
self.cur_offset = id
|
92
|
+
yield id, (@ids_to_fns[id] =~ /,.*R.*$/ ? [] : [:unread])
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def start_offset
|
97
|
+
scan_mailbox
|
98
|
+
@ids.first
|
99
|
+
end
|
100
|
+
|
101
|
+
def end_offset
|
102
|
+
scan_mailbox
|
103
|
+
@ids.last
|
104
|
+
end
|
105
|
+
|
106
|
+
def pct_done; 100.0 * (@ids.index(cur_offset) || 0).to_f / (@ids.length - 1).to_f; end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def make_id fn
|
111
|
+
# use 7 digits for the size. why 7? seems nice.
|
112
|
+
sprintf("%d%07d", File.mtime(fn), File.size(fn)).to_i
|
113
|
+
end
|
114
|
+
|
115
|
+
def with_file_for id
|
116
|
+
fn = @ids_to_fns[id] or raise OutOfSyncSourceError, "No such id: #{id.inspect}."
|
117
|
+
begin
|
118
|
+
File.open(fn) { |f| yield f }
|
119
|
+
rescue SystemCallError, IOError => e
|
120
|
+
raise FatalSourceError, "Problem reading file for id #{id.inspect}: #{fn.inspect}: #{e.message}."
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
Redwood::register_yaml(Maildir, %w(uri cur_offset usual archived id))
|
126
|
+
|
127
|
+
end
|
data/lib/sup/mbox/loader.rb
CHANGED
@@ -1,32 +1,36 @@
|
|
1
1
|
require 'rmail'
|
2
|
+
require 'uri'
|
2
3
|
|
3
4
|
module Redwood
|
4
5
|
module MBox
|
5
6
|
|
6
7
|
class Loader < Source
|
7
|
-
attr_reader_cloned :labels
|
8
|
-
|
9
8
|
def initialize uri_or_fp, start_offset=nil, usual=true, archived=false, id=nil
|
10
9
|
super
|
11
10
|
|
12
11
|
@mutex = Mutex.new
|
13
12
|
@labels = [:unread]
|
14
|
-
@labels << :inbox unless archived?
|
15
13
|
|
16
14
|
case uri_or_fp
|
17
15
|
when String
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
uri = URI(uri_or_fp)
|
17
|
+
raise ArgumentError, "not an mbox uri" unless uri.scheme == "mbox"
|
18
|
+
raise ArgumentError, "mbox uri ('#{uri}') cannot have a host: #{uri.host}" if uri.host
|
21
19
|
## heuristic: use the filename as a label, unless the file
|
22
20
|
## has a path that probably represents an inbox.
|
23
|
-
@labels << File.basename(
|
24
|
-
@f = File.open
|
21
|
+
@labels << File.basename(uri.path).intern unless File.dirname(uri.path) =~ /\b(var|usr|spool)\b/
|
22
|
+
@f = File.open uri.path
|
25
23
|
else
|
26
24
|
@f = uri_or_fp
|
27
25
|
end
|
28
26
|
end
|
29
27
|
|
28
|
+
def check
|
29
|
+
if (cur_offset ||= start_offset) > end_offset
|
30
|
+
raise OutOfSyncSourceError, "mbox file is smaller than last recorded message offset. Messages have probably been deleted by another client."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
30
34
|
def start_offset; 0; end
|
31
35
|
def end_offset; File.size @f; end
|
32
36
|
|
@@ -36,9 +40,7 @@ class Loader < Source
|
|
36
40
|
@f.seek offset
|
37
41
|
l = @f.gets
|
38
42
|
unless l =~ BREAK_RE
|
39
|
-
|
40
|
-
self.broken_msg = "offset mismatch in mbox file offset #{offset.inspect}: #{l.inspect}. Run 'sup-import --rebuild #{to_s}' to correct this."
|
41
|
-
raise SourceError, self.broken_msg
|
43
|
+
raise OutOfSyncSourceError, "mismatch in mbox file offset #{offset.inspect}: #{l.inspect}."
|
42
44
|
end
|
43
45
|
header = MBox::read_header @f
|
44
46
|
end
|
@@ -46,7 +48,6 @@ class Loader < Source
|
|
46
48
|
end
|
47
49
|
|
48
50
|
def load_message offset
|
49
|
-
raise SourceError, self.broken_msg if broken?
|
50
51
|
@mutex.synchronize do
|
51
52
|
@f.seek offset
|
52
53
|
begin
|
@@ -54,13 +55,12 @@ class Loader < Source
|
|
54
55
|
return RMail::Parser.read(input)
|
55
56
|
end
|
56
57
|
rescue RMail::Parser::Error => e
|
57
|
-
raise
|
58
|
+
raise FatalSourceError, "error parsing mbox file: #{e.message}"
|
58
59
|
end
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
62
63
|
def raw_header offset
|
63
|
-
raise SourceError, self.broken_msg if broken?
|
64
64
|
ret = ""
|
65
65
|
@mutex.synchronize do
|
66
66
|
@f.seek offset
|
@@ -72,7 +72,6 @@ class Loader < Source
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def raw_full_message offset
|
75
|
-
raise SourceError, self.broken_msg if broken?
|
76
75
|
ret = ""
|
77
76
|
@mutex.synchronize do
|
78
77
|
@f.seek offset
|
@@ -85,37 +84,40 @@ class Loader < Source
|
|
85
84
|
end
|
86
85
|
|
87
86
|
def next
|
88
|
-
raise SourceError, self.broken_msg if broken?
|
89
87
|
returned_offset = nil
|
90
88
|
next_offset = cur_offset
|
91
89
|
|
92
|
-
|
93
|
-
@
|
94
|
-
|
95
|
-
## cur_offset could be at one of two places here:
|
96
|
-
|
97
|
-
## 1. before a \n and a mbox separator, if it was previously at
|
98
|
-
## EOF and a new message was added; or,
|
99
|
-
## 2. at the beginning of an mbox separator (in all other
|
100
|
-
## cases).
|
101
|
-
|
102
|
-
l = @f.gets or raise "next while at EOF"
|
103
|
-
if l =~ /^\s*$/ # case 1
|
104
|
-
returned_offset = @f.tell
|
105
|
-
@f.gets # now we're at a BREAK_RE, so skip past it
|
106
|
-
else # case 2
|
107
|
-
returned_offset = cur_offset
|
108
|
-
## we've already skipped past the BREAK_RE, so just go
|
109
|
-
end
|
90
|
+
begin
|
91
|
+
@mutex.synchronize do
|
92
|
+
@f.seek cur_offset
|
110
93
|
|
111
|
-
|
112
|
-
|
113
|
-
|
94
|
+
## cur_offset could be at one of two places here:
|
95
|
+
|
96
|
+
## 1. before a \n and a mbox separator, if it was previously at
|
97
|
+
## EOF and a new message was added; or,
|
98
|
+
## 2. at the beginning of an mbox separator (in all other
|
99
|
+
## cases).
|
100
|
+
|
101
|
+
l = @f.gets or raise "next while at EOF"
|
102
|
+
if l =~ /^\s*$/ # case 1
|
103
|
+
returned_offset = @f.tell
|
104
|
+
@f.gets # now we're at a BREAK_RE, so skip past it
|
105
|
+
else # case 2
|
106
|
+
returned_offset = cur_offset
|
107
|
+
## we've already skipped past the BREAK_RE, so just go
|
108
|
+
end
|
109
|
+
|
110
|
+
while(line = @f.gets)
|
111
|
+
break if line =~ BREAK_RE
|
112
|
+
next_offset = @f.tell
|
113
|
+
end
|
114
114
|
end
|
115
|
+
rescue SystemCallError, IOError => e
|
116
|
+
raise FatalSourceError, "Error reading #{@f.path}: #{e.message}"
|
115
117
|
end
|
116
118
|
|
117
119
|
self.cur_offset = next_offset
|
118
|
-
[returned_offset, labels]
|
120
|
+
[returned_offset, @labels.clone]
|
119
121
|
end
|
120
122
|
end
|
121
123
|
|
data/lib/sup/mbox/ssh-file.rb
CHANGED
@@ -106,14 +106,12 @@ class SSHFile
|
|
106
106
|
@file_size = nil
|
107
107
|
@offset = 0
|
108
108
|
@say_id = nil
|
109
|
-
@broken_msg = nil
|
110
109
|
@shell = nil
|
111
110
|
@shell_mutex = nil
|
112
111
|
@buf_mutex = Mutex.new
|
113
112
|
end
|
114
113
|
|
115
114
|
def to_s; "mbox+ssh://#@host/#@fn"; end ## TODO: remove this EVILness
|
116
|
-
def broken?; !@broken_msg.nil?; end
|
117
115
|
|
118
116
|
def connect
|
119
117
|
do_remote nil
|
@@ -164,7 +162,6 @@ private
|
|
164
162
|
end
|
165
163
|
|
166
164
|
def unsafe_connect
|
167
|
-
raise SSHFileError, @broken_msg if broken?
|
168
165
|
return if @shell
|
169
166
|
|
170
167
|
@key = [@host, @ssh_opts[:username]]
|
@@ -172,14 +169,13 @@ private
|
|
172
169
|
@shell, @shell_mutex = @@shells_mutex.synchronize do
|
173
170
|
unless @@shells.member? @key
|
174
171
|
say "Opening SSH connection to #{@host} for #@fn..."
|
175
|
-
#raise SSHFileError, "simulated SSH file error"
|
176
172
|
session = Net::SSH.start @host, @ssh_opts
|
177
173
|
say "Starting SSH shell..."
|
178
174
|
@@shells[@key] = [session.shell.sync, Mutex.new]
|
179
175
|
end
|
180
176
|
@@shells[@key]
|
181
177
|
end
|
182
|
-
|
178
|
+
|
183
179
|
say "Checking for #@fn..."
|
184
180
|
@shell_mutex.synchronize { raise Errno::ENOENT, @fn unless @shell.test("-e #@fn").status == 0 }
|
185
181
|
ensure
|
@@ -190,29 +186,24 @@ private
|
|
190
186
|
def do_remote cmd, expected_size=0
|
191
187
|
retries = 0
|
192
188
|
result = nil
|
193
|
-
begin
|
194
|
-
begin
|
195
|
-
unsafe_connect
|
196
|
-
if cmd
|
197
|
-
# MBox::debug "sending command: #{cmd.inspect}"
|
198
|
-
result = @shell_mutex.synchronize { x = @shell.send_command cmd; sleep 0.25; x }
|
199
|
-
raise SSHFileError, "Failure during remote command #{cmd.inspect}: #{(result.stderr || result.stdout || "")[0 .. 100]}" unless result.status == 0
|
200
|
-
end
|
201
189
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
190
|
+
begin
|
191
|
+
unsafe_connect
|
192
|
+
if cmd
|
193
|
+
# MBox::debug "sending command: #{cmd.inspect}"
|
194
|
+
result = @shell_mutex.synchronize { x = @shell.send_command cmd; sleep 0.25; x }
|
195
|
+
raise SSHFileError, "Failure during remote command #{cmd.inspect}: #{(result.stderr || result.stdout || "")[0 .. 100]}" unless result.status == 0
|
196
|
+
end
|
197
|
+
## Net::SSH::Exceptions seem to happen every once in a while for
|
198
|
+
## no good reason.
|
199
|
+
rescue Net::SSH::Exception, *RECOVERABLE_ERRORS
|
200
|
+
if (retries += 1) <= 3
|
201
|
+
@@shells_mutex.synchronize do
|
202
|
+
@shell = nil
|
203
|
+
@@shells[@key] = nil
|
211
204
|
end
|
212
|
-
|
205
|
+
retry
|
213
206
|
end
|
214
|
-
rescue Net::SSH::Exception, SSHFileError, SystemCallError => e
|
215
|
-
@broken_msg = e.message
|
216
207
|
raise
|
217
208
|
end
|
218
209
|
|