Class: Cute::Bash::Bash

Inherits:
Object
  • Object
show all
Defined in:
lib/cute/bash.rb

Instance Method Summary collapse

Constructor Details

#initialize(stdin, stdout, debug = false) ⇒ Bash

Returns a new instance of Bash.



31
32
33
34
35
36
# File 'lib/cute/bash.rb', line 31

def initialize(stdin, stdout, debug = false)
    @stdin = stdin
    @stdout = stdout
    @debug = debug
    @buff = ''
end

Instance Method Details

#_escape(args) ⇒ Object



90
91
92
# File 'lib/cute/bash.rb', line 90

def _escape(args)
    return args.map { |x| "'#{x}'" }.join(' ')
end

#_extend(path, suffix) ⇒ Object



81
82
83
84
# File 'lib/cute/bash.rb', line 81

def _extend(path, suffix)
    path = path.chomp('/') if path.end_with?('/')
    return path + suffix
end

#_loopObject



42
43
44
45
46
47
48
49
50
51
# File 'lib/cute/bash.rb', line 42

def _loop
    while true do
        x = IO::select([@stdout], [], [], 120.0)
        raise BashTimeout.new if x.nil?
        bytes = @stdout.sysread(1024)
        $stderr.write("\nBASH IN: #{bytes}\n") if @debug
        @buff << bytes
        break if yield
    end
end

#_nonceObject



53
54
55
56
# File 'lib/cute/bash.rb', line 53

def _nonce
    randee = 4.times.map { rand().to_s }.join('|')
    return Digest::SHA512.hexdigest(randee).to_s
end

#_run(cmd, _opts) ⇒ Object

Raises:



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/cute/bash.rb', line 58

def _run(cmd, _opts)
    # it's a kind of magic
    $stderr.write("\nBASH CMD: #{cmd}\n") if @debug
    nonce = _nonce()
    @stdin.write("#{cmd}; printf '%04d#{nonce}' $?\n")
    @stdin.flush
    _loop do
        @buff.include?(nonce)
    end
    raise BashPaddingError.new if !@buff.end_with?(nonce)
    output = @buff
    @buff = ''
    # treat the output
    output.slice!(-nonce.length..-1)
    status = output.slice!(-4..-1)
    raise "Status #{status} > 255?" if status.slice(0..0) != '0'
    return output, status.to_i
end

#_run_block(cmd, _opts) ⇒ Object



77
78
79
# File 'lib/cute/bash.rb', line 77

def _run_block(cmd, _opts)
    @stdin.write("#{cmd}; printf '%04d#{nonce}' $?\n")
end

#_unlines(s) ⇒ Object



86
87
88
# File 'lib/cute/bash.rb', line 86

def _unlines(s)
    return s.lines.map { |l| l.chomp("\n") }
end

#abspath(path) ⇒ Object



167
168
169
# File 'lib/cute/bash.rb', line 167

def abspath(path)
    run("readlink -f #{path}").strip
end

#append_line(path, line) ⇒ Object



252
253
254
# File 'lib/cute/bash.rb', line 252

def append_line(path, line)
    return run("echo '#{line}' >> #{path}")
end

#append_lines(path, lines) ⇒ Object



256
257
258
259
260
# File 'lib/cute/bash.rb', line 256

def append_lines(path, lines)
    lines.each { |line|
        append_line(path, line)
    }
end

#aptget(*args) ⇒ Object

Raises:



287
288
289
290
291
292
293
294
295
296
# File 'lib/cute/bash.rb', line 287

def aptget(*args)
    raise 'Command not given' if args.length == 0
    cmd = args.first.to_sym
    args = args.map { |x| x.to_s }.join(' ')
    status = run_status("DEBIAN_FRONTEND=noninteractive apt-get -y #{args}")
    if cmd == :purge and status == 100
        return # ugly hack for the case when the package is not installed
    end
    raise StatusError.new('aptget', status, 'none') if status != 0
end

#assert(condition, msg = 'Assertion error') ⇒ Object

TESTING METHODS



96
97
98
# File 'lib/cute/bash.rb', line 96

def assert(condition, msg = 'Assertion error')
    raise msg if condition != true
end

#bc(text) ⇒ Object



141
142
143
# File 'lib/cute/bash.rb', line 141

def bc(text)
    run("echo '#{text}' | bc").strip
end

#buildObject



161
162
163
164
165
# File 'lib/cute/bash.rb', line 161

def build
    # builds a standard Unix software
    run("./configure")
    run("make")
end

#build_tarball(tarball, path) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/cute/bash.rb', line 171

def build_tarball(tarball, path)
    # builds a tarball containing a std Unix software
    tarball = abspath(tarball)
    path = abspath(path)
    remove_dirs(path)
    tmp = _extend(path, '-tmp')
    remove_dirs(tmp)
    make_dirs(tmp)
    untar(tarball, tmp)
    cd tmp
    # we are in the temp dir
    if exists('./configure')
        cd '/'
        mv tmp, path
    else
        ds = dirs()
        raise 'Too many dirs?' if ds.length != 1
        mv ds.first, path
        cd '/'
        remove_dirs(tmp)
    end
    cd path
    build
end

#cd(path) ⇒ Object



117
118
119
# File 'lib/cute/bash.rb', line 117

def cd(path)
    run("cd #{path}")
end

#contents(name) ⇒ Object Also known as: cat



262
263
264
# File 'lib/cute/bash.rb', line 262

def contents(name)
    run("cat #{name}")
end

#cp(a, b) ⇒ Object



145
146
147
# File 'lib/cute/bash.rb', line 145

def cp(a, b)
    run("cp #{a} #{b}")
end

#dirs(ignore = true) ⇒ Object



238
239
240
# File 'lib/cute/bash.rb', line 238

def dirs(ignore = true)
    return files(ignore, 'd')
end

#distribute(path, dest, *nodes) ⇒ Object



298
299
300
301
302
303
# File 'lib/cute/bash.rb', line 298

def distribute(path, dest, *nodes)
    # distributes a file to the given nodes
    nodes.flatten.each { |node|
        run("scp -o 'StrictHostKeyChecking no' #{path} #{node}:#{dest}")
    }
end

#echo(text) ⇒ Object



137
138
139
# File 'lib/cute/bash.rb', line 137

def echo(text)
    run("echo #{text}")
end

#exists(path) ⇒ Object



215
216
217
# File 'lib/cute/bash.rb', line 215

def exists(path)
    run_status("[[ -e #{path} ]]") == 0
end

#expand_path(path) ⇒ Object



248
249
250
# File 'lib/cute/bash.rb', line 248

def expand_path(path)
    return run("echo #{path}").strip
end

#export(name, value) ⇒ Object

PUBLIC METHODS



102
103
104
# File 'lib/cute/bash.rb', line 102

def export(name, value)
    run("export #{name}=#{value}")
end

#files(ignore = true, type = 'f') ⇒ Object



227
228
229
230
231
232
# File 'lib/cute/bash.rb', line 227

def files(ignore = true, type = 'f')
    fs = run("find . -maxdepth 1 -type #{type}")
    fs = _unlines(fs).reject { |f| f == '.' }.map { |f| f[2..-1] }
    fs = fs.reject { |f| f.end_with?('~') or f.start_with?('.') } if ignore
    return fs
end

#get_type(name) ⇒ Object



242
243
244
245
246
# File 'lib/cute/bash.rb', line 242

def get_type(name)
    return :dir if run_status("[[ -d #{name} ]]") == 0
    return :file if run_status("[[ -f #{name} ]]") == 0
    raise "'#{name}' is neither file nor directory"
end

#glob(pattern) ⇒ Object



305
306
307
308
309
# File 'lib/cute/bash.rb', line 305

def glob(pattern)
    out, status = _run("ls -1 #{pattern}", {})
    return [] if status != 0
    return out.strip.lines.map { |x| x.strip }
end

#hostnameObject



268
269
270
# File 'lib/cute/bash.rb', line 268

def hostname
    return run('hostname').strip
end

#join(*args) ⇒ Object



211
212
213
# File 'lib/cute/bash.rb', line 211

def join(*args)
    return File.join(*args)
end

#lsObject



121
122
123
# File 'lib/cute/bash.rb', line 121

def ls
    run("ls -1").lines.map { |line| line.chomp("\n") }
end

#make_dirs(path) ⇒ Object



219
220
221
# File 'lib/cute/bash.rb', line 219

def make_dirs(path)
    run("mkdir -p #{path}")
end

#mkdir(path) ⇒ Object



223
224
225
# File 'lib/cute/bash.rb', line 223

def mkdir(path)
    run("mkdir #{path}") unless exists(path)  # TODO: this changes semantics of mkdir...
end

#mpirun(machines, params) ⇒ Object



206
207
208
209
# File 'lib/cute/bash.rb', line 206

def mpirun(machines, params)
    machines = save_machines(machines) if machines.is_a?(Array)
    return run("mpirun --mca btl ^openib -machinefile #{machines} #{params}")
end

#mv(a, b) ⇒ Object



149
150
151
# File 'lib/cute/bash.rb', line 149

def mv(a, b)
    run("mv #{a} #{b}")
end

#packagesObject



279
280
281
282
283
284
285
# File 'lib/cute/bash.rb', line 279

def packages
    list = run("dpkg -l")
    _unlines(list).map do |p|
        s, n, v = p.split
        { :status => s, :name => n, :version => v }
    end
end

#parse(&block) ⇒ Object



38
39
40
# File 'lib/cute/bash.rb', line 38

def parse(&block)
    return self.instance_exec(&block)
end

#pwdObject



125
126
127
# File 'lib/cute/bash.rb', line 125

def pwd
    run("pwd").strip
end

#remove_dirs(path) ⇒ Object



157
158
159
# File 'lib/cute/bash.rb', line 157

def remove_dirs(path)
    rm "-rf", path
end

#rm(*args) ⇒ Object



153
154
155
# File 'lib/cute/bash.rb', line 153

def rm(*args)
    run("rm #{_escape(args)}")
end

#run(cmd, opts = {}) ⇒ Object

Raises:



106
107
108
109
110
# File 'lib/cute/bash.rb', line 106

def run(cmd, opts = {})
    out, status = _run(cmd, opts)
    raise StatusError.new(cmd, status, out) if status != 0
    return out
end

#run_status(cmd, opts = {}) ⇒ Object



112
113
114
115
# File 'lib/cute/bash.rb', line 112

def run_status(cmd, opts = {})
    _out, status = _run(cmd, opts)
    return status
end

#save_machines(machines, path = nil) ⇒ Object



200
201
202
203
204
# File 'lib/cute/bash.rb', line 200

def save_machines(machines, path = nil)
    path = tmp_file if path.nil?
    append_lines(path, machines.map { |m| m.to_s })
    return path
end

#tmp_fileObject



196
197
198
# File 'lib/cute/bash.rb', line 196

def tmp_file
    return run('mktemp').strip
end

#touch(path) ⇒ Object



272
273
274
# File 'lib/cute/bash.rb', line 272

def touch(path)
    run("touch #{path}")
end

#untar(name, where = nil) ⇒ Object



129
130
131
132
133
134
135
# File 'lib/cute/bash.rb', line 129

def untar(name, where = nil)
    if where.nil?
        run("tar xvf #{name}")
    else
        run("tar -C #{where} -xvf #{name}")
    end
end

#which(prog) ⇒ Object



234
235
236
# File 'lib/cute/bash.rb', line 234

def which(prog)
    return run("which #{prog}").strip
end