6 $stderr.sync
= $stdout.sync
= true
8 trap('INT') { exit
130 }
9 trap('PIPE') { exit
0 }
10 if md5_trailer_nodes
= ENV["MD5_TRAILER_NODES"]
11 md5_trailer_nodes
.split(/\s*,\s*/).each
do |host
|
12 MogileFS
::HTTPFile::MD5_TRAILER_NODES[host
] = true
16 # this is to be compatible with config files used by the Perl tools
17 def parse_config_file
!(path
, dest
= {})
18 File
.open(path
).each_line
do |line
|
20 when /^(domain|class)\s*=\s*(\S+)/
22 when /^(?:trackers|hosts)\s*=\s*(.*)/
23 dest
[:hosts] = $1.split(/\s*,\s*/)
24 when /^timeout\s*=\s*(.*)/
25 dest
[:timeout] = $1.to_f
26 when /^noclobber\s*=\s*true\s*/
27 dest
[:noclobber] = true
29 warn
"Ignored configuration line: #{line}" unless /^#/.match(line
)
35 # parse the default config file if one exists
36 def_file
= File
.expand_path("~/.mogilefs-client.conf")
37 def_cfg
= File
.exist
?(def_file
) ? parse_config_file
!(def_file
) : {}
39 # parse the command-line first, these options take precedence over all else
47 cat
= { :raw => false }
50 x
.banner
= "Usage: #{$0} [options] <command> [<arguments>]"
53 x
.on('-c', '--config=/path/to/config',
54 'config file to load') { |file
| config_file
= file
}
56 x
.on('-t', '--trackers=host1[,host2]', '--hosts=host1[,host2]', Array
,
57 'hostnames/IP addresses of trackers') do |trackers
|
58 cli_cfg
[:hosts] = trackers
61 x
.on('-e', 'True if key exists') { test
[:e] = true }
62 x
.on('-r', '--raw', 'show raw big_info file information') { cat
[:raw] = true }
63 x
.on('-n', '--no-clobber', 'do not clobber existing key') do
64 cli_cfg
[:noclobber] = true
67 x
.on('-C', '--class=s', 'class') { |klass
| cli_cfg
[:class] = klass
}
68 x
.on('-d', '--domain=s', 'domain') { |domain
| cli_cfg
[:domain] = domain
}
69 x
.on('-l', "long listing format (`ls' command)") { ls_l
= true }
70 x
.on('-h', '--human-readable',
71 "print sizes in human-readable format (`ls' command)") { ls_h
= true }
72 x
.on('--chunk', "chunk uploads (`tee' command)") { chunk
= true }
73 x
.on('--range', "stream partial uploads (`tee' command)") { range
= true }
75 x
.on('--help', 'Show this help message.') { puts x
; exit
}
76 x
.on('--version', 'Show --version') { puts
"#$0 #{MogileFS::VERSION}"; exit
}
80 # parse the config file specified at the command-line
81 file_cfg
= config_file
? parse_config_file
!(config_file
) : {}
83 # read environment variables, too. This Ruby API favors the term
84 # "hosts", however upstream MogileFS teminology favors "trackers" instead.
85 # Favor the term more consistent with what the MogileFS inventors used.
87 if ENV["MOG_TRACKERS"]
88 env_cfg
[:hosts] = ENV["MOG_TRACKERS"].split(/\s*,\s*/)
90 if ENV["MOG_HOSTS"] && (env_cfg
[:hosts] || []).empty
?
91 env_cfg
[:hosts] = ENV["MOG_HOSTS"].split(/\s*,\s*/)
93 env_cfg
[:domain] = ENV["MOG_DOMAIN"] if ENV["MOG_DOMAIN"]
94 env_cfg
[:class] = ENV["MOG_CLASS"] if ENV["MOG_CLASS"]
96 # merge the configs, favoring them in order specified:
97 cfg
= {}.merge(def_cfg
).merge(env_cfg
).merge(file_cfg
).merge(cli_cfg
)
101 err
<< "trackers must be specified" if cfg
[:hosts].nil? || cfg
[:hosts].empty
?
102 err
<< "domain must be specified" unless cfg
[:domain]
104 warn
"Errors:\n #{err.join("\n ")}"
109 unless cmd
= ARGV.shift
114 cfg
[:timeout] ||= 30 # longer timeout for interactive use
115 mg
= MogileFS
::MogileFS.new(cfg
)
117 def store_file_retry(mg
, key
, storage_class
, filepath
)
120 mg
.store_file(key
, storage_class
, filepath
)
121 rescue MogileFS
::UnreadableSocketError,
122 MogileFS
::Backend::NoDevicesError => err
123 if ((tries
+= 1) < 10)
124 warn
"Retrying on error: #{err}: #{err.message} tries: #{tries}"
127 warn
"FATAL: #{err}: #{err.message} tries: #{tries}"
135 %w(K M G
).each
do |s
|
142 sprintf("%.1f%s", size
, suff
)
148 filename
= ARGV.shift
or raise ArgumentError
, '<filename> <key>'
149 dkey
= ARGV.shift
or raise ArgumentError
, '<filename> <key>'
150 ARGV.shift
and raise ArgumentError
, '<filename> <key>'
151 cfg
[:noclobber] && mg
.exist
?(dkey
) and
152 abort
"`#{dkey}' already exists and -n/--no-clobber was specified"
153 store_file_retry(mg
, dkey
, cfg
[:class], filename
)
155 ARGV.empty
? and raise ArgumentError
, '<key1> [<key2> ...]'
157 if (!cat
[:raw] && key
=~
/^_big_info:/)
158 mg
.bigfile_write(key
, $stdout, {:verify => true})
160 mg
.get_file_data(key
, $stdout)
164 newclass
= cfg
[:class] or abort
'-C/--class not specified'
166 mg
.updateclass(key
, newclass
)
169 prefixes
= ARGV.empty
? ? [ nil ] : ARGV
171 each_key
= lambda
do |key
, size
, devcount
|
172 size
= ls_h
&& size
> 1024 ? human_size(size
) : size
.to_s
173 size
= (' ' * (12 - size
.length
)) << size
# right justify
174 puts
[ sprintf("% 2d", devcount
), size
, key
].pack("A4 A16 A*")
177 each_key
= lambda
{ |key
| puts key
}
179 prefixes
.each
{ |prefix
| mg
.each_key(prefix
, &each_key
) }
181 ARGV.empty
? and raise ArgumentError
, '<key1> [<key2>]'
182 ARGV.each
{ |key
| mg
.delete(key
) }
184 from
= ARGV.shift
or raise ArgumentError
, '<from> <to>'
185 to
= ARGV.shift
or raise ArgumentError
, '<from> <to>'
186 ARGV.shift
and raise ArgumentError
, '<from> <to>'
188 when 'stat' # this outputs a RFC822-like format
189 ARGV.empty
? and raise ArgumentError
, '<key1> [<key2>]'
191 ARGV.each_with_index
do |key
,j
|
193 info
= mg
.file_info(key
)
195 puts
"Size: #{info['length']}"
196 puts
"Class: #{info['class']}"
197 checksum
= info
['checksum'] and puts
"Checksum: #{checksum}"
198 o
= { :pathcount => info
["devcount"] }
199 mg
.get_paths(key
, o
).each_with_index
do |path
,i
|
200 puts
"URL-#{i}: #{path}"
202 puts
"" if ARGV.size
!= (j
+ 1)
203 rescue MogileFS
::Backend::UnknownKeyError
204 warn
"No such key: #{key}"
210 abort
"--range and --chunk are incompatible" if range
&& chunk
211 dkey
= ARGV.shift
or raise ArgumentError
, '<key>'
212 ARGV.shift
and raise ArgumentError
, '<key>'
213 cfg
[:noclobber] && mg
.exist
?(dkey
) and
214 abort
"`#{dkey}' already exists and -n/--no-clobber was specified"
215 skip_tee
= File
.stat('/dev/null') == $stdout.stat
216 largefile
= :tempfile
217 largefile
= :content_range if range
218 largefile
= :stream if chunk
220 io
= mg
.new_file(dkey
, :class => cfg
[:class], :largefile => largefile
)
222 buf
= $stdin.readpartial(16384)
225 $stdout.write(buf
) unless skip_tee
226 $stdin.readpartial(16384, buf
)
232 truth
, ok
= true, nil
233 raise ArgumentError
, "-e must be specified" unless (test
.size
== 1)
235 truth
, key
= case ARGV.size
240 raise ArgumentError
, "#{ARGV[0]}: binary operator expected"
244 raise ArgumentError
, "Too many arguments"
247 test
[:e] or raise ArgumentError
, "Unknown flag: -#{test.keys.first}"
252 raise ArgumentError
, "Unknown command: #{cmd}"
254 rescue ArgumentError
=> err
255 warn
"Usage: #{$0} #{cmd} #{err.message}"