thoughtafter-lockfile 2.0.0

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.
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
+