lib/shexy.rb in shexy-0.2 vs lib/shexy.rb in shexy-0.3

- old
+ new

@@ -28,44 +28,80 @@ # # Copying files (local -> remote): # # Shexy.copy_to 'test@test-host', '/home/rubiojr/my-uber-file', '/tmp/' # + module Shexy + + VERSION = '0.3' - VERSION = '0.2' + [:user, :password, :key, :cmd, :host].each do |n| + instance_eval %{ + def #{n}; Thread.current[:shexy_#{n}]; end + def #{n}=(v); Thread.current[:shexy_#{n}] = v; end + } + end - @flags = {} + def self.flags=(f);Thread.current[:shexy_flags] = f;end + def self.flags; Thread.current[:shexy_flags] ||= {} ; end + def self.sudo?;Thread.current[:shexy_use_sudo]; end + def self.use_sudo(v = true); Thread.current[:shexy_use_sudo] = v; end - def self.password=(password); flags[:password] = password; end - def self.password; flags[:password]; end - def self.key=(key); flags[:keys] = [File.expand_path(key)]; end - def self.key; flags[:keys]; end - def self.use_sudo(v=true); @sudo = v; end - def self.sudo?; @sudo ||= false; end + def self.wait_for_ssh(timeout = 60) + Timeout.timeout(timeout) do + begin + sleep(1) until tcp_test_ssh(host) do + end + rescue Errno::ECONNRESET + # safe to ignore, we need to retry all the time. + end + end + true + rescue Exception => e + $stderr.puts e.message + false + end - class << self - attr_accessor :host, :user, :flags - attr_reader :cmd + def self.tcp_test_ssh + tcp_socket = TCPSocket.new(host, 22) + readable = IO.select([tcp_socket], nil, nil, 5) + if readable + yield + true + else + false + end + rescue Errno::ETIMEDOUT, Errno::EPERM + false + rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH + sleep 2 + false + ensure + tcp_socket && tcp_socket.close end def self.exe(*args) args.flatten! if args.size > 1 - @host = args[0] - if @host =~ /@/ - @user, @host = @host.split '@' - end - @cmd = args[1] + self.host = args[0] + self.user, self.host = self.host.split '@' if self.host =~ /@/ + self.cmd = args[1] else - @cmd = args[0] + self.cmd = args[0] end - Net::SSH.start(host, user, flags) do |sh| + self.flags[:password] = self.password if self.password + self.flags[:keys] = [self.key] if self.key + Net::SSH.start(self.host, self.user, self.flags) do |sh| sh.open_channel do |ch| if sudo? ch.request_pty - @cmd = "sudo #{@cmd}" + if cmd =~ /(&&|\|\||&|\|)/ + self.cmd = cmd.gsub(/(&&|\|\||&|\|)/, $1 + 'sudo') + end + self.cmd = "sudo #{cmd}" + puts "SHEXY: #{cmd}" if $DEBUG end ch.exec cmd do # FIXME: I don't think it's a good idea # to implement access to stdout,stderr this way stdout = "" @@ -93,10 +129,29 @@ end end end end + def self.batch(&block) + require 'tempfile' + out, err = nil,nil + def self.script(script) + f = Tempfile.new 'shexy' + begin + f.puts script + f.flush + copy_to f.path, "#{f.path}.remote" + out, err = exe "/bin/bash #{f.path}.remote" + ensure + f.close + f.unlink + end + return out, err + end + instance_eval &block + end + # # Shexy.copy_to 'root@foobar.com', 'source_file', 'dest_file' # # or # @@ -111,22 +166,23 @@ args.delete :recursive end if args.size > 2 # First arg assumed to be foo@host.net - @host = args[0] - if @host =~ /@/ - @user, @host = @host.split '@' + self.host = args[0] + if self.host =~ /@/ + self.user, self.host = self.host.split '@' end from = args[1] to = args[2] else # user, host already set via Shexy.host and # Shexy.user from = args[0] to = args[1] end + self.flags[:password] = self.password if self.password from = File.expand_path from Net::SCP.start(host, user, flags) do |scp| scp.upload! from, to, opts end end @@ -161,8 +217,42 @@ # def self.issue issue, err = ((exe 'cat /etc/issue')[0].strip.chomp || Shexy.exe('lsb_release -i')[0].strip.chomp).lines.first raise Exception.new "Error reading release info." unless (err.nil? or err.empty?) issue + end + + def self.copy_ssh_pubkey(path, dest_dir = '~/.ssh') + path = File.expand_path path + raise ArgumentError.new("Invalid key file") unless File.exist?(path) + key = File.read path + batch do + script <<-EOH + mkdir -p #{dest_dir} && chmod 700 #{dest_dir} + echo '#{key}' >> #{dest_dir}/authorized_keys + EOH + end + end + + # + # Set PermitRootLogin to value in /etc/ssh/sshd_config + # value accepted: yes,now, without-password + # + def self.permit_root_login(value) + value = value.to_s + unless value =~ /^(yes|no|without-password)$/ + raise ArgumentError.new "Argument should be yes|no|without-password" + end + using_sudo = Shexy.sudo? + Shexy.use_sudo + out, err = batch do + script <<-EOH + sed -i 's/^#\?PermitRootLogin.*$/PermitRootLogin\ #{value}/' /etc/ssh/sshd_config + test -f /etc/init.d/ssh && /etc/init.d/ssh restart + test -f /etc/init.d/sshd && /etc/init.d/sshd restart + EOH + end + Shexy.use_sudo(false) unless using_sudo + return out, err end end