sshfsmount 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/exe/sshfsmount +2 -150
- data/lib/sshfsmount.rb +158 -1
- data/lib/sshfsmount/cli.rb +48 -0
- data/lib/sshfsmount/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f9330ebd590ac3a49fb68f3166a18f1e8666ca5
|
4
|
+
data.tar.gz: 6ad40fe249ba8b6a82f365fae9518e9b351972d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31eac3dbd23722dbacb13dc2d15e0e5495870d758c0a87eaab7e2eadba5b382b83b336f248f6eb04afa5582120e55da270e217eb90193cc9ea15dcfb65c834a9
|
7
|
+
data.tar.gz: d42f434f5a1d99b9099b9e9ec6a97737c5cd83089381fe2ca40429d6ea6fa4678fea67e66c72b517463a6558da83444ffca9dfd5dc3ca3db86d8bafe0b8a00d2
|
data/README.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
[](https://travis-ci.org/nomoon/sshfsmount)
|
4
4
|
[](https://coveralls.io/github/nomoon/sshfsmount?branch=master)
|
5
5
|
[](https://gemnasium.com/github.com/nomoon/sshfsmount)
|
6
|
+
[](https://badge.fury.io/rb/sshfsmount)
|
6
7
|
|
7
8
|
A simple front-end CLI to SSHFS.
|
8
9
|
|
data/exe/sshfsmount
CHANGED
@@ -1,156 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "gli"
|
5
|
-
require "json"
|
6
|
-
require "shellwords"
|
7
|
-
require "fileutils"
|
8
|
-
|
9
4
|
require "sshfsmount"
|
10
|
-
|
11
|
-
#
|
12
|
-
# Basic SSHFS flags
|
13
|
-
#
|
14
|
-
SSHFS_FLAGS = [
|
15
|
-
"-o local",
|
16
|
-
"-o workaround=nonodelaysrv",
|
17
|
-
"-o transform_symlinks",
|
18
|
-
"-o idmap=user",
|
19
|
-
"-C",
|
20
|
-
].freeze
|
21
|
-
|
22
|
-
#
|
23
|
-
# Locate config file.
|
24
|
-
#
|
25
|
-
CONFIG_FILE = [
|
26
|
-
File.join(Dir.home, ".sshfsmount.json"),
|
27
|
-
File.join(Dir.home, ".config", "sshfsmount.json"),
|
28
|
-
File.join(Dir.home, ".config", "sshfsmount", "sshfsmount.json"),
|
29
|
-
].select { |f| File.exist?(f) }.first.freeze
|
30
|
-
|
31
|
-
#
|
32
|
-
# Parse config file
|
33
|
-
#
|
34
|
-
CONFIG = (begin
|
35
|
-
if CONFIG_FILE.nil?
|
36
|
-
STDERR.puts "No config file found"
|
37
|
-
{}
|
38
|
-
else
|
39
|
-
JSON.parse(File.read(CONFIG_FILE), symbolize_names: true)
|
40
|
-
end
|
41
|
-
rescue JSON::ParserError => e
|
42
|
-
STDERR.puts "Parse error in config file `#{CONFIG_FILE}`: #{e}"
|
43
|
-
{}
|
44
|
-
end).freeze
|
45
|
-
|
46
|
-
#
|
47
|
-
# Create Mount-point Directory
|
48
|
-
#
|
49
|
-
def mkmountpoint(name)
|
50
|
-
local = File.expand_path(name)
|
51
|
-
if !Dir.exist?(local)
|
52
|
-
STDERR.puts "Creating mount-point directory #{local}"
|
53
|
-
FileUtils.mkdir_p(local)
|
54
|
-
elsif !Dir.empty?(local)
|
55
|
-
raise "Mount point #{local} already exists and is not empty"
|
56
|
-
elsif VERBOSE
|
57
|
-
STDERR.puts "Mount-point directory #{local} already exists and is empty"
|
58
|
-
end
|
59
|
-
local
|
60
|
-
end
|
61
|
-
|
62
|
-
#
|
63
|
-
# Delete mount-point directory
|
64
|
-
#
|
65
|
-
def rmmountpoint(name)
|
66
|
-
local = File.expand_path(name)
|
67
|
-
if !Dir.exist?(local)
|
68
|
-
STDERR.puts "Mount-point directory not found" if VERBOSE
|
69
|
-
elsif !Dir.empty?(local)
|
70
|
-
raise "Mount-point directory #{local} is not empty"
|
71
|
-
else
|
72
|
-
STDERR.puts "Deleting mount-point directory #{local}"
|
73
|
-
FileUtils.rmdir(local)
|
74
|
-
end
|
75
|
-
local
|
76
|
-
end
|
77
|
-
|
78
|
-
#
|
79
|
-
# Mount an SSHFS volume
|
80
|
-
#
|
81
|
-
def mount(mount_name, params)
|
82
|
-
local = mkmountpoint(params[:local])
|
83
|
-
volname = params[:volname] || mount_name
|
84
|
-
p_remote = Shellwords.escape(params[:remote])
|
85
|
-
p_local = Shellwords.escape(local)
|
86
|
-
p_volname = Shellwords.escape(volname)
|
87
|
-
port = (params[:port] || 22).to_i
|
88
|
-
cmd = "/usr/local/bin/sshfs #{p_remote} #{p_local} " \
|
89
|
-
"-o volname=\"#{p_volname}\" #{SSHFS_FLAGS.join(' ')} -p #{port}"
|
90
|
-
pgrep = `pgrep -f \"#{cmd}\"`
|
91
|
-
unless pgrep.empty?
|
92
|
-
raise "SSHFS process for #{mount_name} running already (PID: #{pgrep.strip}, " \
|
93
|
-
"Mount-point: #{p_local})"
|
94
|
-
end
|
95
|
-
puts "Mounting #{params[:remote]} to #{params[:local]} as \"#{volname}\""
|
96
|
-
STDERR.puts "> #{cmd}" if VERBOSE
|
97
|
-
system(cmd)
|
98
|
-
end
|
99
|
-
|
100
|
-
#
|
101
|
-
# Unmount an SSHFS volume
|
102
|
-
#
|
103
|
-
def unmount(mount_name, params)
|
104
|
-
local = File.expand_path(params[:local])
|
105
|
-
p_local = Shellwords.escape(local)
|
106
|
-
cmd = "diskutil unmount #{p_local}"
|
107
|
-
pgrep = `pgrep -f \"#{p_local}\"`
|
108
|
-
if pgrep.empty?
|
109
|
-
raise "No SSHFS process found with the mount-point for #{mount_name} (#{p_local})"
|
110
|
-
end
|
111
|
-
puts "Unmounting #{local}"
|
112
|
-
STDERR.puts "> #{cmd}" if VERBOSE
|
113
|
-
system(cmd)
|
114
|
-
rmmountpoint(local)
|
115
|
-
end
|
116
|
-
|
117
|
-
#
|
118
|
-
# GLI command-line app definition
|
119
|
-
#
|
120
|
-
include GLI::App
|
121
|
-
|
122
|
-
program_desc "A simple front-end CLI to SSHFS"
|
123
|
-
version Sshfsmount::VERSION
|
124
|
-
|
125
|
-
switch %i[u unmount], desc: "Unmount the volume", negatable: false
|
126
|
-
switch %i[v verbose], desc: "Show verbose output", negatable: false
|
127
|
-
|
128
|
-
desc "List active SSHFS processes"
|
129
|
-
command :active do |c|
|
130
|
-
c.action do
|
131
|
-
system("pgrep -fl sshfs")
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
#
|
136
|
-
# Define commands from data-file
|
137
|
-
#
|
138
|
-
extant_commands = commands.keys
|
139
|
-
CONFIG.reject { |name, _| extant_commands.include?(name) }.each do |mount_name, params|
|
140
|
-
desc "mount #{params[:remote]} to #{params[:local]}"
|
141
|
-
command mount_name do |c|
|
142
|
-
c.switch %i[u unmount], desc: "Unmount the volume", negatable: false
|
143
|
-
c.switch %i[v verbose], desc: "Show verbose output", negatable: false
|
144
|
-
c.action do |global_options, cmd_options|
|
145
|
-
VERBOSE = global_options[:v] || cmd_options[:v]
|
146
|
-
if global_options[:u] || cmd_options[:u]
|
147
|
-
unmount(mount_name, params)
|
148
|
-
else
|
149
|
-
mount(mount_name, params)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
5
|
+
require "sshfsmount/cli"
|
154
6
|
|
155
7
|
# Run the GLI app
|
156
|
-
exit run(ARGV)
|
8
|
+
exit Sshfsmount::CLI.run(ARGV)
|
data/lib/sshfsmount.rb
CHANGED
@@ -1,7 +1,164 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "json"
|
4
|
+
require "shellwords"
|
5
|
+
require "pathname"
|
6
|
+
|
3
7
|
require "sshfsmount/version"
|
4
8
|
|
5
9
|
module Sshfsmount
|
6
|
-
#
|
10
|
+
#
|
11
|
+
# Basic SSHFS flags
|
12
|
+
#
|
13
|
+
SSHFS_FLAGS = [
|
14
|
+
"-o local",
|
15
|
+
"-o workaround=nonodelaysrv",
|
16
|
+
"-o transform_symlinks",
|
17
|
+
"-o idmap=user",
|
18
|
+
"-C",
|
19
|
+
].freeze
|
20
|
+
|
21
|
+
module_function
|
22
|
+
|
23
|
+
def verbose
|
24
|
+
@verbose
|
25
|
+
end
|
26
|
+
|
27
|
+
def verbose=(val)
|
28
|
+
@verbose = val ? true : false
|
29
|
+
end
|
30
|
+
|
31
|
+
def sshfs_cmd
|
32
|
+
sshfs_cmd = `command -v sshfs`.strip
|
33
|
+
raise "sshfs command not found in path" if sshfs_cmd.empty?
|
34
|
+
sshfs_cmd
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Locate config file.
|
39
|
+
#
|
40
|
+
def find_config_file
|
41
|
+
@config_file ||= begin
|
42
|
+
[
|
43
|
+
Pathname.new(File.join(Dir.home, ".sshfsmount.json")),
|
44
|
+
Pathname.new(File.join(Dir.home, ".config", "sshfsmount.json")),
|
45
|
+
Pathname.new(File.join(Dir.home, ".config", "sshfsmount", "sshfsmount.json")),
|
46
|
+
].select(&:file?).first
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Parse config file
|
52
|
+
#
|
53
|
+
def config
|
54
|
+
@config ||= begin
|
55
|
+
config_file = find_config_file
|
56
|
+
if config_file.nil?
|
57
|
+
STDERR.puts "No config file found"
|
58
|
+
{}
|
59
|
+
else
|
60
|
+
JSON.parse(config_file.read, symbolize_names: true)
|
61
|
+
end
|
62
|
+
rescue JSON::ParserError => e
|
63
|
+
STDERR.puts "Parse error in config file `#{config_file}`: #{e}"
|
64
|
+
{}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def active_mounts
|
69
|
+
@active_mounts ||= begin
|
70
|
+
`mount`.lines.each_with_object({}) do |line, mounts|
|
71
|
+
regex = %r{^(?<remote>.+@[^:]+\:/.*?) on (?<local>/.*?) (?<options>\(\b(?:fuse|osxfuse)\b.*?\))$}
|
72
|
+
info = line.match(regex)
|
73
|
+
next if info.nil?
|
74
|
+
pid = `pgrep -f "/sshfs #{Shellwords.escape(info[:remote])} #{Shellwords.escape(info[:local])} "`.strip
|
75
|
+
pid = "None found" if pid.empty?
|
76
|
+
mounts[Pathname.new(info[:local]).expand_path] = {
|
77
|
+
local: info[:local],
|
78
|
+
remote: info[:remote],
|
79
|
+
options: info[:options],
|
80
|
+
pid: pid,
|
81
|
+
str: "#{info[:remote]} on #{info[:local]} (PID: #{pid})",
|
82
|
+
}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Fails if the mount-path is already in use.
|
88
|
+
def dupe_check!(mount_name, params)
|
89
|
+
local = Pathname.new(params[:local]).expand_path
|
90
|
+
dupe = active_mounts[local]
|
91
|
+
return if dupe.nil?
|
92
|
+
raise "\"#{mount_name}\" already mounted?\n* #{dupe[:str]}"
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
# Create Mount-point Directory
|
97
|
+
#
|
98
|
+
def mkmountpoint(name)
|
99
|
+
local = Pathname.new(name).expand_path
|
100
|
+
if !local.exist?
|
101
|
+
STDERR.puts "Creating mount-point directory #{local}"
|
102
|
+
local.mkpath
|
103
|
+
elsif !local.directory
|
104
|
+
raise "Mount point #{local} exists and is not a directory"
|
105
|
+
elsif !local.empty?
|
106
|
+
raise "Mount point #{local} already exists and is not empty"
|
107
|
+
elsif @verbose
|
108
|
+
STDERR.puts "Mount-point directory #{local} already exists and is empty"
|
109
|
+
end
|
110
|
+
local
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# Delete mount-point directory
|
115
|
+
#
|
116
|
+
def rmmountpoint(name)
|
117
|
+
local = Pathname.new(name).expand_path
|
118
|
+
if !local.exist?
|
119
|
+
STDERR.puts "Mount-point directory not found" if @verbose
|
120
|
+
elsif !local.directory?
|
121
|
+
raise "Mount point #{local} is not a directory"
|
122
|
+
elsif !local.empty?
|
123
|
+
raise "Mount-point directory #{local} is not empty"
|
124
|
+
else
|
125
|
+
STDERR.puts "Deleting mount-point directory #{local}"
|
126
|
+
local.rmdir
|
127
|
+
return true
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
#
|
132
|
+
# Mount an SSHFS volume
|
133
|
+
#
|
134
|
+
def mount(mount_name, params)
|
135
|
+
dupe_check!(mount_name, params)
|
136
|
+
local = mkmountpoint(params[:local])
|
137
|
+
volname = params[:volname] || mount_name
|
138
|
+
cmd = "#{sshfs_cmd} " \
|
139
|
+
"#{Shellwords.escape(params[:remote])} " \
|
140
|
+
"#{Shellwords.escape(local)} " \
|
141
|
+
"-o volname=\"#{Shellwords.escape(volname)}\" " \
|
142
|
+
"#{SSHFS_FLAGS.join(' ')} " \
|
143
|
+
"-p #{(params[:port] || 22).to_i}"
|
144
|
+
puts "Mounting \"#{mount_name}\"\n" \
|
145
|
+
" * #{params[:remote]} on #{local} as \"#{volname}\""
|
146
|
+
STDERR.puts "> #{cmd}" if @verbose
|
147
|
+
system(cmd)
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# Unmount an SSHFS volume
|
152
|
+
#
|
153
|
+
def unmount(mount_name, params)
|
154
|
+
local = Pathname.new(params[:local]).expand_path
|
155
|
+
info = active_mounts[local]
|
156
|
+
raise "No active mount-point found for #{local}" if info.nil?
|
157
|
+
STDERR.puts "No active SSHFS process found for #{local}" if info[:pid] == "None found"
|
158
|
+
cmd = "umount #{Shellwords.escape(local)}"
|
159
|
+
puts "Unmounting \"#{mount_name}\" (#{local})"
|
160
|
+
STDERR.puts "> #{cmd}" if @verbose
|
161
|
+
raise "Unmount error, skipping removal of mount-point." unless system(cmd)
|
162
|
+
rmmountpoint(local)
|
163
|
+
end
|
7
164
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "gli"
|
4
|
+
|
5
|
+
module Sshfsmount
|
6
|
+
module CLI
|
7
|
+
extend GLI::App
|
8
|
+
#
|
9
|
+
# GLI command-line app definition
|
10
|
+
#
|
11
|
+
program_desc "A simple front-end CLI to SSHFS"
|
12
|
+
version Sshfsmount::VERSION
|
13
|
+
|
14
|
+
switch %i[u unmount], desc: "Unmount the volume", negatable: false
|
15
|
+
switch %i[v verbose], desc: "Show verbose output", negatable: false
|
16
|
+
|
17
|
+
desc "List active SSHFS processes"
|
18
|
+
command :active do |c|
|
19
|
+
c.action do
|
20
|
+
Sshfsmount.active_mounts.each_value do |data|
|
21
|
+
puts "* #{data[:str]}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
config = Sshfsmount.config
|
27
|
+
|
28
|
+
#
|
29
|
+
# Define commands from data-file
|
30
|
+
#
|
31
|
+
extant_commands = commands.keys
|
32
|
+
config.reject { |name, _| extant_commands.include?(name) }.each do |mount_name, params|
|
33
|
+
desc "mount #{params[:remote]} to #{params[:local]}"
|
34
|
+
command mount_name do |c|
|
35
|
+
c.switch %i[u unmount], desc: "Unmount the volume", negatable: false
|
36
|
+
c.switch %i[v verbose], desc: "Show verbose output", negatable: false
|
37
|
+
c.action do |global_options, cmd_options|
|
38
|
+
Sshfsmount.verbose = global_options[:v] || cmd_options[:v]
|
39
|
+
if global_options[:u] || cmd_options[:u]
|
40
|
+
Sshfsmount.unmount(mount_name, params)
|
41
|
+
else
|
42
|
+
Sshfsmount.mount(mount_name, params)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/sshfsmount/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sshfsmount
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Bellefleur
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-07-
|
11
|
+
date: 2017-07-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -128,6 +128,7 @@ files:
|
|
128
128
|
- bin/setup
|
129
129
|
- exe/sshfsmount
|
130
130
|
- lib/sshfsmount.rb
|
131
|
+
- lib/sshfsmount/cli.rb
|
131
132
|
- lib/sshfsmount/version.rb
|
132
133
|
- sshfsmount.gemspec
|
133
134
|
homepage: https://github.com/nomoon/sshfsmount
|