thoughtafter-lockfile 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/rlock ADDED
@@ -0,0 +1,360 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # built-in
4
+ #
5
+ require 'optparse'
6
+ require 'logger'
7
+ #
8
+ # http://raa.ruby-lang.org/project/lockfile/
9
+ #
10
+ require 'lockfile'
11
+
12
+ class Main
13
+ #--{{{
14
+ VERSION = Lockfile::VERSION
15
+
16
+ USAGE =
17
+ #--{{{
18
+ <<-usage
19
+ NAME
20
+ rlock v#{ VERSION }
21
+
22
+ SYNOPSIS
23
+ rlock [options]+ lockfile [program [args]+ | -- program options+ [args]+]
24
+
25
+ DESCRIPTTION
26
+ rlock creates NFS safe lockfiles. it can optionally run a program while
27
+ holding the lock, ensuring lockfile removal on program exit. if a program
28
+ is specified to be run rlock will spawn a background thread to kept the
29
+ lockfile 'fresh' by touching it at a regular interval. in this way a lease
30
+ is maintained on the lockfile and other processes attempting to obtain the
31
+ lock can determine that it is in use. see the '--refresh' option for how to
32
+ control the touch interval. any other process trying to obtain a lock will
33
+ automatically remove a stale lockfile; a stale lockfile is one that is older
34
+ than a certain age. this age be controled via the '--max_age' option.
35
+
36
+ ENVIRONMENT
37
+ LOCKFILE_DEBUG=1
38
+ causes internal actions of the library to be shown on STDERR
39
+
40
+ DIAGNOSTICS
41
+ rlock attempts to exit with the status of 'program' except where it
42
+ cannot due to exceptional conditions. in addition the message
43
+
44
+ 'RLOCK SUBCOMMAND FAILURE'
45
+
46
+ will be printed on STDERR if 'program' exits with non-zero status.
47
+
48
+ success => $? == 0
49
+ failure => $? != 0
50
+
51
+ AUTHOR
52
+ ara.t.howard@gmail.com
53
+
54
+ BUGS
55
+ 1 < bugno && bugno < 42
56
+
57
+ OPTIONS
58
+ usage
59
+ #--}}}
60
+
61
+ EXAMPLES =
62
+ #--{{{
63
+ <<-examples
64
+ EXAMPLES
65
+
66
+ 0) simple usage - create lockfile in an atomic fashion (obtain a lock)
67
+
68
+ ~ > rlock lockfile
69
+
70
+ 1) safe usage - create a lockfile, execute a command, and remove lockfile
71
+
72
+ ~ > rlock lockfile ls lockfile
73
+
74
+ 2) same as above, but logging verbose messages
75
+
76
+ ~ > rlock -v4 lockfile ls lockfile
77
+
78
+ 3) same as above, but logging verbose messages and showing actions internal
79
+ to lockfile library
80
+
81
+ ~ > rlock -v4 -d lockfile ls lockfile
82
+
83
+ 4) same as above
84
+
85
+ ~ > LOCKFILE_DEBUG=1 rlock -v4 lockfile ls lockfile
86
+
87
+ 5) same as above
88
+
89
+ ~ > export LOCKFILE_DEBUG=1
90
+ ~ > rlock -v4 lockfile ls lockfile
91
+
92
+ 6) you need to tell the option parser to stop parsing rlock options if you
93
+ intend to pass options to 'program'
94
+
95
+ ~ > rlock -v4 -d lockfile -- ls -ltar lockfile
96
+
97
+ without the '--' rlock would consume the '-ltar' option as one of
98
+ it's own.
99
+
100
+ 7) lock lockfile and exec 'program' - remove the lockfile if it is older
101
+ than 4242 seconds
102
+
103
+ ~ > rlock --max_age=4242 lockfile program
104
+
105
+ 8) lock lockfile and exec 'program' - remove the lockfile if it is older
106
+ than 4242 seconds, set the refresh rate to be 8 seconds.
107
+
108
+ ~ > rlock --max_age=4242 --refresh=8 lockfile program
109
+
110
+ 9) same as above, but fail if lockfile cannot be obtained within 1 minute
111
+
112
+ ~ > rlock --max_age=4242 --refresh=8 --timeout=60 lockfile program
113
+
114
+ 10) lockfile creation involves making some temporary files. normally these
115
+ are cleaned up unless rlock is killed with 'kill -9'. these temp files are
116
+ normally 'sweeped' - searched for and removed - unless the '--dont_sweep'
117
+ option is given. note that sweeping can remove ONLY old temp files created
118
+ by the same host since there is otherwise no way to tell if the offending
119
+ process is still running.
120
+
121
+ lock lockfile and run program - do not do any sweeping
122
+
123
+ ~ > rlock --dont_sweep lockfile program
124
+
125
+ examples
126
+ #--}}}
127
+
128
+ EXIT_SUCCESS = 0
129
+ EXIT_FAILURE = 1
130
+
131
+ attr :argv
132
+ attr :op
133
+ attr :logger
134
+ attr :config
135
+
136
+ def initialize argv = ARGV
137
+ #--{{{
138
+ @argv = mcp argv
139
+ parse_opts
140
+ if @opt_version
141
+ puts Main::VERSION
142
+ exit EXIT_SUCCESS
143
+ end
144
+ if @opt_help
145
+ usage
146
+ exit EXIT_SUCCESS
147
+ end
148
+ parse_argv
149
+ run
150
+ #--}}}
151
+ end
152
+ def run
153
+ #--{{{
154
+ init_logging
155
+
156
+ debug{ "lockpath <#{ @lockpath }>" }
157
+
158
+ opts = {}
159
+ options =
160
+ %w(retries max_age sleep_inc min_sleep max_sleep suspend timeout refresh poll_retries poll_max_sleep)
161
+ options.each do |opt|
162
+ #if((val = eval("opt_#{ opt }")))
163
+ if(send("opt_#{ opt }?"))
164
+ val = send "opt_#{ opt }"
165
+ begin
166
+ val = (opts[opt] = String === val ? Integer(val) : val)
167
+ logger.debug{ "<#{ opt }> <#{ val.inspect }>" }
168
+ rescue
169
+ logger.fatal{ "illegal value <#{ val.inspect }> for opt <#{ opt }>" }
170
+ exit EXIT_FAILURE
171
+ end
172
+ end
173
+ end
174
+
175
+ opts['debug'] = true if opt_debug
176
+
177
+ begin
178
+ case @argv.size
179
+ when 0
180
+ opts['dont_clean'] = true
181
+ logger.debug{ "opts <#{ opts.inspect }>" }
182
+ logger.debug{ "aquiring lock <#{ @lockpath }>..." }
183
+ #
184
+ # simple usage - just create the lockfile with opts
185
+ #
186
+ lockfile = ::Lockfile.new @lockpath, opts
187
+ lockfile.lock
188
+
189
+ logger.debug{ "aquired lock <#{ @lockpath }>" }
190
+ else
191
+ logger.debug{ "opts <#{ opts.inspect }>" }
192
+ logger.debug{ "aquiring lock <#{ @lockpath }>..." }
193
+ #
194
+ # block usage - create the lockfile with opts, run block, rm lockfile
195
+ #
196
+ status = 1
197
+
198
+ lockfile = ::Lockfile.new @lockpath, opts
199
+
200
+ lockfile.lock do
201
+ logger.debug{ "aquired lock <#{ @lockpath }>" }
202
+ logger.debug{ "cmd <#{ @argv.join ' ' }>" }
203
+ v = nil
204
+ begin
205
+ v = $VERBOSE
206
+ $VERBOSE = nil
207
+ STDOUT.flush
208
+ STDERR.flush
209
+ fork{ exec(*@argv) }
210
+ pid, status = Process::wait2
211
+ ensure
212
+ $VERBOSE = v
213
+ end
214
+ logger.debug{ "status <#{ $? }>" }
215
+ end
216
+
217
+ status = status.exitstatus
218
+ STDERR.puts "RLOCK SUBCOMMAND FAILURE" unless status == 0
219
+ exit status
220
+ end
221
+ rescue => e
222
+ logger.fatal{ e }
223
+ exit EXIT_FAILURE
224
+ end
225
+
226
+ exit EXIT_SUCCESS
227
+ #--}}}
228
+ end
229
+ def parse_opts
230
+ #--{{{
231
+ @op = OptionParser::new
232
+ @op.banner = ''
233
+ define_options
234
+ @op.parse! argv
235
+
236
+ #--}}}
237
+ end
238
+ def parse_argv
239
+ #--{{{
240
+ usage and exit EXIT_FAILURE if @argv.empty?
241
+ @lockpath = @argv.shift
242
+ #--}}}
243
+ end
244
+ def define_options
245
+ #--{{{
246
+ options = [
247
+ ['--retries=n','-r', "default(#{ Lockfile.retries.inspect }) - (nil => forever)"],
248
+ ['--max_age=n','-a', "default(#{ Lockfile.max_age.inspect })"],
249
+ ['--sleep_inc=n','-s', "default(#{ Lockfile.sleep_inc.inspect })"],
250
+ ['--max_sleep=n','-p', "default(#{ Lockfile.max_sleep.inspect })"],
251
+ ['--min_sleep=n','-P', "default(#{ Lockfile.min_sleep.inspect })"],
252
+ ['--suspend=n','-u', "default(#{ Lockfile.suspend.inspect })"],
253
+ ['--timeout=n','-t', "default(#{ Lockfile.timeout.inspect }) - (nil => never)"],
254
+ ['--refresh=n','-f', "default(#{ Lockfile.refresh.inspect })"],
255
+ ['--debug','-d', "default(#{ Lockfile.debug.inspect })"],
256
+ ['--poll_retries=n','-R', "default(#{ Lockfile.poll_retries.inspect })"],
257
+ ['--poll_max_sleep=n','-S', "default(#{ Lockfile.poll_max_sleep.inspect })"],
258
+ ['--dont_sweep','-w', "default(#{ Lockfile.dont_sweep.inspect })"],
259
+
260
+ ['--version'],
261
+ ['--verbosity=0-4|debug|info|warn|error|fatal','-v'],
262
+ ['--log=path','-l'],
263
+ ['--log_age=log_age'],
264
+ ['--log_size=log_size'],
265
+ ['--help','-h'],
266
+ ]
267
+ options.each do |option|
268
+ opt = option.first.gsub(%r/(?:--)|(?:=.*$)/o,'').strip
269
+ get, set = opt_attr opt
270
+ value4 = lambda do |v|
271
+ case v
272
+ when NilClass, %r/^t|true$/i
273
+ true
274
+ when %r/^f|false$/i
275
+ false
276
+ when %r/^nil|nul|null$/i
277
+ nil
278
+ else
279
+ v
280
+ end
281
+ end
282
+ @op.def_option(*option) do |v|
283
+ send set, value4[v]
284
+ end
285
+ end
286
+ #--}}}
287
+ end
288
+ %w(debug info warn error fatal).each do |m|
289
+ eval "def #{ m }(*args,&block);@logger.#{ m }(*args,&block);end"
290
+ end
291
+ def init_logging
292
+ #--{{{
293
+ if @opt_log_age
294
+ @opt_log_age = @opt_log_age.to_i if @opt_log_age =~ /\d/
295
+ end
296
+ if @opt_log_size
297
+ @opt_log_size = @opt_log_size.to_i if @opt_log_size =~ /\d/
298
+ end
299
+ $logger = @logger = Logger::new(@opt_log || STDERR, @opt_log_age, @opt_log_size)
300
+
301
+ level = nil
302
+ @opt_verbosity ||= 'info'
303
+ @opt_verbosity =
304
+ case @opt_verbosity
305
+ when /^\s*(?:4|d|debug)\s*$/io
306
+ level = 'Logging::DEBUG'
307
+ 4
308
+ when /^\s*(?:3|i|info)\s*$/io
309
+ level = 'Logging::INFO'
310
+ 3
311
+ when /^\s*(?:2|w|warn)\s*$/io
312
+ level = 'Logging::WARN'
313
+ 2
314
+ when /^\s*(?:1|e|error)\s*$/io
315
+ level = 'Logging::ERROR'
316
+ 1
317
+ when /^\s*(?:0|f|fatal)\s*$/io
318
+ level = 'Logging::FATAL'
319
+ 0
320
+ else
321
+ abort "illegal verbosity setting <#{ @opt_verbosity }>"
322
+ end
323
+ @logger.level = 2 - ((@opt_verbosity % 5) - 2)
324
+ #--}}}
325
+ end
326
+ def usage io = STDOUT
327
+ #--{{{
328
+ io << USAGE
329
+ io << "\n"
330
+ io << @op
331
+ io << "\n"
332
+ io << EXAMPLES if defined? EXAMPLES
333
+ self
334
+ #--}}}
335
+ end
336
+ def opt_attr opt
337
+ #--{{{
338
+ query = "opt_#{ opt }?"
339
+ get = "opt_#{ opt }"
340
+ set = "#{ get }="
341
+ code = <<-code
342
+ class << self
343
+ def #{ query }; defined? @#{ get }; end
344
+ def #{ get }; defined?(@#{ get }) ? @#{ get } : nil; end
345
+ def #{ set } value; @#{ get } = value; end
346
+ end
347
+ code
348
+ instance_eval code
349
+ [get, set]
350
+ #--}}}
351
+ end
352
+ def mcp obj
353
+ #--{{{
354
+ Marshal::load(Marshal::dump(obj))
355
+ #--}}}
356
+ end
357
+ #--}}}
358
+ end
359
+
360
+ Main::new
data/doc/rlock.help ADDED
@@ -0,0 +1,95 @@
1
+ NAME
2
+ rlock v1.3.0
3
+
4
+ SYNOPSIS
5
+ rlock [options]+ file.lock [program [-- [options]+] [args]+]
6
+
7
+ DESCRIPTTION
8
+ rlock creates NFS resistent lockfiles
9
+
10
+ ENVIRONMENT
11
+ LOCKFILE_DEBUG=1 will show internal actions of the library
12
+
13
+ DIAGNOSTICS
14
+ success => $? == 0
15
+ failure => $? != 0
16
+
17
+ AUTHOR
18
+ ara.t.howard@noaa.gov
19
+
20
+ BUGS
21
+ > 1
22
+
23
+ OPTIONS
24
+
25
+
26
+ -r, --retries=n default(nil) - (nil => forever)
27
+ -a, --max_age=n default(1024)
28
+ -s, --sleep_inc=n default(2)
29
+ -p, --max_sleep=n default(32)
30
+ -P, --min_sleep=n default(2)
31
+ -u, --suspend=n default(64)
32
+ -t, --timeout=n default(nil) - (nil => never)
33
+ -f, --refresh=n default(8)
34
+ -d, --debug default(false)
35
+ -R, --poll_retries=n default(16)
36
+ -S, --poll_max_sleep=n default(0.08)
37
+ -w, --dont_sweep default(false)
38
+ -v=0-4|debug|info|warn|error|fatal
39
+ --verbosity
40
+ -l, --log=path
41
+ --log_age=log_age
42
+ --log_size=log_size
43
+ -h, --help
44
+
45
+ EXAMPLES
46
+
47
+ 0) simple usage - just create a file.lock in an atomic fashion
48
+
49
+ ~ > rlock file.lock
50
+
51
+ 1) safe usage - create a file.lock, execute a command, and remove file.lock
52
+
53
+ ~ > rlock file.lock ls file.lock
54
+
55
+ 2) same as above, but logging verbose messages
56
+
57
+ ~ > rlock -v4 file.lock ls file.lock
58
+
59
+ 3) same as above, but logging verbose messages and showing actions internal to
60
+ lockfile library
61
+
62
+ ~ > rlock -v4 -d file.lock ls file.lock
63
+
64
+ 4) same as above
65
+
66
+ ~ > LOCKFILE_DEBUG=1 rlock -v4 file.lock ls file.lock
67
+
68
+ 5) same as above
69
+
70
+ ~ > export LOCKFILE_DEBUG=1
71
+ ~ > rlock -v4 file.lock ls file.lock
72
+
73
+ 6) note that you need to tell the option parser to stop parsing rlock
74
+ options if you intend to pass options to 'program'
75
+
76
+ ~ > rlock -v4 -d file.lock -- ls -ltar file.lock
77
+
78
+ without the '--' rlock would consume the '-ltar' options, parsing it
79
+ as the logfile name 'tar'
80
+
81
+ 7) lock file.lock and exec 'program' - remove the file.lock if it is older
82
+ than 4242 seconds
83
+
84
+ ~ > rlock --max_age=4242 file.lock program
85
+
86
+ 8) lock file.lock and exec 'program' - remove the file.lock if it is older
87
+ than 4242 seconds, also spawn a background thread which will refresh
88
+ file.lock every 8 seonds will 'program' is executing
89
+
90
+ ~ > rlock --max_age=4242 --refresh=8 file.lock program
91
+
92
+ 9) same as above, but fail if file.lock cannot be obtained within 1 minute
93
+
94
+ ~ > rlock --max_age=4242 --refresh=8 --timeout=60 file.lock program
95
+