bin/shaft in shaft-0.6 vs bin/shaft in shaft-0.7
- old
+ new
@@ -1,22 +1,23 @@
#!/usr/bin/env ruby
-SHAFT_DIR = File.join(Dir.home, ".shaft")
+SHAFT_CONFIG = File.join(Dir.home, ".shaft")
+SHAFT_ACTIVE = File.join(Dir.home, ".shaft.active")
require 'rubygems'
require 'thor'
require 'yaml'
class ShaftCLI < Thor
include Thor::Actions
map "-h" => :help
- map "-l" => :list
+ map "-l" => :active
- desc "list", "Lists active tunnels"
+ desc "active", "Lists active tunnels"
method_options %w( short -s ) => :boolean
- def list
+ def active
active = get_active
unless active.empty?
if options[:short]
say active.keys.join(",")
else
@@ -29,62 +30,74 @@
end
desc "all", "Lists all available tunnels"
def all
say "Listing all available tunnels:"
- tunnels = Dir["#{SHAFT_DIR}/*.yml"].map { |f| File.basename(f, ".yml") }
+ tunnels = get_config.keys
print_in_columns tunnels
end
desc "start [NAME]", "Starts a tunnel"
method_options :name => :string
def start(name)
active = get_active
if active.has_key? name
say "Error: tunnel '#{name}' already active!"
else
- c = read_yaml("#{name}.yml")
+ c = get_tunnel(name)
unless c.nil?
begin
port = c['port']
bind = "#{c['bind']['client-port']}:#{c['bind']['host']}:#{c['bind']['host-port']}"
host = "#{c['username']}@#{c['host']}"
rescue NoMethodError
- error "Tunnel file for '#{name}' appears to be invalid!"
+ error "Tunnel configuration for '#{name}' appears to be invalid!"
return
end
+ used = local_port_used?(c['bind']['client-port'])
+ if used
+ error "Local port #{port} is used by:\n#{used}"
+ return
+ end
+
say "Starting tunnel '#{name}'..."
pid = Process.spawn("ssh -N -p #{port} #{host} -L #{bind}")
Process.detach pid
say "Started with pid #{pid}."
active[name] = pid
set_active(active)
else
- error "Tunnel '#{name}' file not found!"
+ error "Tunnel '#{name}' not found!"
end
end
end
desc "stop [NAME]", "Stops a tunnel"
method_options :name => :string
def stop(name)
active = get_active
- if active.has_key? name
+ if active.has_key?(name)
say "Stopping tunnel '#{name}' at pid #{active[name]}..."
begin
Process.kill "INT", active[name]
- say "Stopped."
rescue Errno::ESRCH
say "Tunnel wasn't active (zombie shaft item)."
end
- #TODO verify killing?
+ # verify killing
+ tunnel = get_tunnel(name)
+ if local_port_used?(tunnel['bind']['client-port'])
+ error "Could not stop tunnel process!"
+ else
+ say "Stopped."
- active.delete(name)
- set_active(active)
+ # set as inactive
+ active.delete(name)
+ set_active(active)
+ end
else
error "Tunnel '#{name}' does not seem to be active!"
end
end
@@ -93,31 +106,45 @@
def restart(name)
stop(name) && start(name)
end
private
- def read_yaml(filename)
- path = File.join(SHAFT_DIR, filename)
- YAML::load(File.open(path)) if File.exist?(path)
+ def get_config
+ @config ||= read_yaml(SHAFT_CONFIG)
end
def get_active
- active = read_yaml(".active")
- if active.nil?
- active = {}
- set_active(active)
- end
- active
+ @active ||= read_yaml(SHAFT_ACTIVE)
end
+ def get_tunnel(name)
+ get_config[name] || nil
+ end
+
def set_active(active)
- unless File.exist?(SHAFT_DIR) and File.directory?(SHAFT_DIR)
- Dir.mkdir(SHAFT_DIR)
+ File.open(SHAFT_ACTIVE, 'w') { |out|
+ YAML.dump(active, out)
+ }
+ end
+
+ def read_yaml(file)
+ if File.directory?(SHAFT_CONFIG)
+ error "Shaft v0.7 and up uses a single-file config.\nConsult the Readme at http://git.io/Zs3viQ ."
+ {}
+ elsif File.exists?(file)
+ YAML::load(File.open(file))
+ else
+ {}
end
+ end
- File.open(File.join(SHAFT_DIR, '.active'), 'w') { |f|
- f.write(active.to_yaml)
- }
+ def local_port_used?(port)
+ usage = `lsof -n -i4TCP:#{port} | grep LISTEN`
+ if usage.length > 0
+ usage
+ else
+ false
+ end
end
end
ShaftCLI.start