main 6.0.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NDNiMzU0MjNhZDhjYTJhMzY0OGRlYTZjZWJiNDcwMTFkNjkzNDNlYg==
4
+ YmVhZWRjMmM1NGY2MmU2YzAxOGJmZDMwOTFlMjc2NzMzNWJkNmJlZA==
5
5
  data.tar.gz: !binary |-
6
- NTkxZDc1ODQ2ZWQ5NzliYWNjZGY2ZTFkNzU3NDI5ZDYwNDY4Mjk1Zg==
6
+ Y2IzNDNiMWI0MTRmMDA4YmQ3YjRiYzg2Y2Q2ZDYwZDYyNjVjOGY1NA==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- NTg3NjY5NTJmOWE0N2QwYjAyOGY4ODgzNWM3NTVkNDYwZDM0YzNkYmViNmRm
10
- ZjMxMWE4ZGViNjg5ZGY0ZWRmYmRkZDE4NzFkMjg3YzY5ZWQzZjBhYjRlYjIz
11
- NmFiMTJjZDY5ZmEzNmFjOWE3MGU4ZjljZjgzYjhlYTVkYTVlNmQ=
9
+ NGM0OTdlMThiNDMwZmEwOGZjMmVlZWYxNTA2ZjcwYWVmZjhjNzdkOTNiMTAw
10
+ OWM1ZmNlZThmYjYyOTEzZjhkODU5OTAzNDZmYTk3MjQ5ZmRkZjQ4MTYyNDU0
11
+ YTIzOGZhNzU4NWRhNjIwNDRhODhlOTkyMjFjYzZlMWUyOGY2MWM=
12
12
  data.tar.gz: !binary |-
13
- YmE1MWU1MmE3MDhiMzhiN2EzZGMxNTEyYjYyZjhiMzRlMWEwNTNkOWIxYWE1
14
- NzJmODkyNDE3MDY0MmJlOWJiZmFkODhjMmEyYWRjNmM1YTQ4NjY5MmI1MDJh
15
- YjEzMTFlMTU1MTRjNjM3MjY5ODNmNzZkZjdiZDczODVjNDdkMzY=
13
+ ODU2MDQ0NWExNTZhMTBhY2EwNDI2ZDBhMTM5YTg5NmFiMzFiYWM3MGJjYzBl
14
+ ODgxNDgwODkxNWU5YmMxN2IzMmY1ZDQxOWEwNmZmMDMzMjIyNDlmYjYxMjgy
15
+ MDIxMTUyMGMxMDlkNGI1ZDU2YjAxN2EwOWY1MWM2MzI5MjE1ZjY=
data/a.rb CHANGED
@@ -1,3 +1,28 @@
1
- require 'main'
1
+ require_relative './lib/main.rb'
2
2
 
3
- Main{ run{ shell! } }
3
+ Main{
4
+ daemonizes!
5
+
6
+ def run
7
+ i = 0
8
+
9
+ loop do
10
+ p argv
11
+ p i
12
+ sleep(3 + rand)
13
+ i += 1
14
+ end
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+ }
22
+
23
+
24
+ __END__
25
+
26
+
27
+ a.rb daemon start
28
+ a.rb daemon stop
@@ -1,7 +1,7 @@
1
1
  module Main
2
2
  # top level constants
3
3
  #
4
- Main::VERSION = '6.0.0' unless
4
+ Main::VERSION = '6.1.0' unless
5
5
  defined? Main::VERSION
6
6
  def Main.version() Main::VERSION end
7
7
 
@@ -90,4 +90,5 @@ end
90
90
  mode.rb
91
91
  program.rb
92
92
  factories.rb
93
+ daemon.rb
93
94
  ]
@@ -0,0 +1,525 @@
1
+ module Main
2
+ class Daemon
3
+ require 'fileutils'
4
+ require 'ostruct'
5
+ require 'rbconfig'
6
+ require 'pathname'
7
+ require 'yaml'
8
+
9
+ %w(
10
+
11
+ main
12
+ script
13
+ dotdir
14
+
15
+ dirname
16
+ basename
17
+ script_dir
18
+ daemon_dir
19
+ lock_file
20
+ log_file
21
+ pid_file
22
+ started_at
23
+
24
+ ).each{|a| attr(a)}
25
+
26
+ def initialize(main)
27
+ @main = main
28
+ @script = @main.script
29
+ end
30
+
31
+ def setup!
32
+ @dotdir = @main.dotdir
33
+
34
+ @dirname = File.expand_path(File.dirname(@script))
35
+ @basename = File.basename(@script)
36
+ @script_dir = File.expand_path(File.dirname(@script))
37
+
38
+ @daemon_dir = File.join(@dotdir, 'daemon')
39
+
40
+ @lock_file = File.join(@daemon_dir, 'lock')
41
+ @log_file = File.join(@daemon_dir, 'log')
42
+ @pid_file = File.join(@daemon_dir, 'pid')
43
+ @stdin_file = File.join(@daemon_dir, 'stdin')
44
+ @stdout_file = File.join(@daemon_dir, 'stdout')
45
+ @stderr_file = File.join(@daemon_dir, 'stderr')
46
+
47
+ FileUtils.mkdir_p(@daemon_dir) rescue nil
48
+
49
+ %w( lock log pid stdin stdout stderr ).each do |which|
50
+ file = instance_variable_get("@#{ which }_file")
51
+ FileUtils.touch(file)
52
+ end
53
+
54
+ @started_at = Time.now
55
+
56
+ @ppid = Process.pid
57
+
58
+ STDOUT.sync = true
59
+ STDERR.sync = true
60
+
61
+ self
62
+ end
63
+
64
+ def cmd(cmd, &block)
65
+ setup!
66
+
67
+ process_cmd!(cmd)
68
+ end
69
+
70
+ def process_cmd!(cmd)
71
+ case cmd.to_s
72
+ when /USAGE/i
73
+ cmd_usage
74
+
75
+ when /INFO/i
76
+ cmd_info
77
+
78
+ when /RESTART/i
79
+ cmd_restart
80
+
81
+ when /START/i
82
+ cmd_start
83
+
84
+ when /STOP/i
85
+ cmd_stop
86
+
87
+ when /PING/i
88
+ cmd_ping
89
+
90
+ when /RUN/i
91
+ cmd_run
92
+
93
+ when /PID/i
94
+ cmd_pid
95
+
96
+ when /SIGNAL/i
97
+ cmd_signal
98
+
99
+ when /LOG/i
100
+ cmd_log
101
+
102
+ when /DIR/i
103
+ cmd_dir
104
+
105
+ when /TAIL/i
106
+ cmd_tail
107
+
108
+ else
109
+ cmd_usage
110
+ end
111
+ end
112
+
113
+ def Daemon.commands
114
+ instance_methods.map{|method| method.to_s =~ /\Acmd_(.*)/ && $1}.compact
115
+ end
116
+
117
+ def commands
118
+ Daemon.commands
119
+ end
120
+
121
+ def usage
122
+ "#{ main.program } daemon #{ commands.join('|') }"
123
+ end
124
+
125
+ def cmd_usage
126
+ STDERR.puts usage
127
+ exit(42)
128
+ end
129
+
130
+ def cmd_info
131
+ info =
132
+ {
133
+ 'main' => @main.program,
134
+ 'script' => @script,
135
+ 'dotdir' => @dotdir
136
+ }
137
+
138
+ %w[
139
+ daemon_dir
140
+
141
+ lock_file
142
+ log_file
143
+ pid_file
144
+ stdin_file
145
+ stdout_file
146
+ stderr_file
147
+ ].each do |key|
148
+ value = instance_variable_get("@#{ key }")
149
+ info[key] = value
150
+ end
151
+
152
+ STDERR.puts(info.to_yaml)
153
+
154
+ exit(42)
155
+ end
156
+
157
+ def cmd_start
158
+ lock!(:complain => true)
159
+
160
+ daemonize!{|pid| puts(pid)}
161
+
162
+ redirect_io!
163
+
164
+ pid!
165
+
166
+ log!
167
+
168
+ exec!
169
+ end
170
+
171
+ def cmd_stop
172
+ pid = Integer(IO.read(@pid_file)) rescue nil
173
+
174
+ if pid
175
+ alive = true
176
+
177
+ %w( QUIT TERM ).each do |signal|
178
+ begin
179
+ Process.kill(signal, pid)
180
+ rescue Errno::ESRCH
181
+ nil
182
+ end
183
+
184
+ 42.times do
185
+ begin
186
+ Process.kill(0, pid)
187
+ sleep(rand)
188
+ rescue Errno::ESRCH
189
+ alive = false
190
+ puts(pid)
191
+ exit(0)
192
+ end
193
+ end
194
+ end
195
+
196
+ if alive
197
+ begin
198
+ Process.kill(-9, pid)
199
+ sleep(rand)
200
+ rescue Errno::ESRCH
201
+ nil
202
+ end
203
+
204
+ begin
205
+ Process.kill(0, pid)
206
+ rescue Errno::ESRCH
207
+ puts(pid)
208
+ exit(0)
209
+ end
210
+ end
211
+ end
212
+
213
+ exit(1)
214
+ ensure
215
+ unless alive?
216
+ begin
217
+ FileUtils.rm_f(@pid_file) rescue nil
218
+ rescue Object
219
+ end
220
+ end
221
+ end
222
+
223
+ def cmd_restart
224
+ 42.times do
225
+ begin
226
+ cmd_stop
227
+ break
228
+ rescue Object => e
229
+ if alive?
230
+ sleep(rand)
231
+ else
232
+ break
233
+ end
234
+ end
235
+ end
236
+
237
+ abort("could not stop #{ @script }!") if alive?
238
+
239
+ sleep(rand)
240
+
241
+ cmd_start
242
+ end
243
+
244
+ def cmd_pid
245
+ pid = Integer(IO.read(@pid_file)) rescue nil
246
+
247
+ if pid
248
+ begin
249
+ Process.kill(0, pid)
250
+ puts(pid)
251
+ exit(0)
252
+ rescue Errno::ESRCH
253
+ exit(1)
254
+ end
255
+ else
256
+ exit(1)
257
+ end
258
+
259
+ exit(1)
260
+ end
261
+
262
+ def cmd_ping
263
+ pid = Integer(IO.read(@pid_file)) rescue nil
264
+
265
+ if pid
266
+ signaled = false
267
+
268
+ begin
269
+ Process.kill('SIGALRM', pid)
270
+ signaled = true
271
+ rescue Object
272
+ nil
273
+ end
274
+
275
+ if signaled
276
+ STDOUT.puts(pid)
277
+ exit
278
+ end
279
+ end
280
+ end
281
+
282
+ def cmd_run
283
+ lock!(:complain => true)
284
+
285
+ pid!
286
+
287
+ log!
288
+
289
+ exec!
290
+ end
291
+
292
+ def cmd_signal
293
+ pid = Integer(IO.read(@pid_file)) rescue nil
294
+ if pid
295
+ signal = ARGV.shift || 'SIGALRM'
296
+ Process.kill(signal, pid)
297
+ puts(pid)
298
+ exit(0)
299
+ end
300
+ exit(42)
301
+ end
302
+
303
+ def cmd_log
304
+ puts(@log_file)
305
+ exit(42)
306
+ end
307
+
308
+ def cmd_dir
309
+ puts(@daemon_dir)
310
+ exit(42)
311
+ end
312
+
313
+ def cmd_tail
314
+ system("tail -F #{ @stdout_file.inspect } #{ @stderr_file.inspect } #{ @log_file.inspect }")
315
+ exit(42)
316
+ end
317
+
318
+ def lock!(options = {})
319
+ complain = options['complain'] || options[:complain]
320
+ fd = open(@lock_file, 'r+')
321
+ status = fd.flock(File::LOCK_EX|File::LOCK_NB)
322
+
323
+ unless status == 0
324
+ if complain
325
+ pid = Integer(IO.read(@pid_file)) rescue '?'
326
+ warn("instance(#{ pid }) is already running!")
327
+ end
328
+ exit(42)
329
+ end
330
+ @lock = fd # prevent garbage collection from closing the file!
331
+ at_exit{ unlock! }
332
+ end
333
+
334
+ def unlock!
335
+ @lock.flock(File::LOCK_UN|File::LOCK_NB) if @lock
336
+ end
337
+
338
+ def pid!
339
+ open(@pid_file, 'w+') do |fd|
340
+ fd.puts(Process.pid)
341
+ end
342
+ at_exit{ FileUtils.rm_f(@pid_file) }
343
+ end
344
+
345
+ def exec!
346
+ ::Kernel.exec(script_start_command)
347
+ end
348
+
349
+ def script_start_command
350
+ argv = @main.argv.dup
351
+ argv.shift if argv.first == "--"
352
+ "#{ which_ruby } #{ @script.inspect } #{ argv.map{|arg| arg.inspect}.join(' ') }"
353
+ end
354
+
355
+ def log!
356
+ logger.info("DAEMON START - #{ Process.pid }")
357
+
358
+ at_exit do
359
+ logger.info("DAEMON STOP - #{ Process.pid }") rescue nil
360
+ end
361
+ end
362
+
363
+ def alive?
364
+ pid = Integer(IO.read(@pid_file)) rescue nil
365
+ alive = !!pid
366
+
367
+ if pid
368
+ alive =
369
+ begin
370
+ Process.kill(0, pid)
371
+ true
372
+ rescue Errno::ESRCH
373
+ false
374
+ end
375
+ end
376
+
377
+ alive
378
+ end
379
+
380
+ def which_ruby
381
+ c = ::RbConfig::CONFIG
382
+ ruby = File::join(c['bindir'], c['ruby_install_name']) << c['EXEEXT']
383
+ raise "ruby @ #{ ruby } not executable!?" unless test(?e, ruby)
384
+ ruby
385
+ end
386
+
387
+ def logger
388
+ @logger ||= (
389
+ require 'logger' unless defined?(Logger)
390
+
391
+ if @log_file
392
+ number_rolled = 7
393
+ megabytes = 2 ** 20
394
+ max_size = 42 * megabytes
395
+
396
+ ::Logger.new(@log_file, number_rolled, max_size)
397
+ else
398
+ ::Logger.new(STDERR)
399
+ end
400
+ )
401
+ end
402
+
403
+ def logger=(logger)
404
+ @logger = logger
405
+ end
406
+
407
+ # daemonize{|pid| puts "the pid of the daemon is #{ pid }"}
408
+ #
409
+
410
+ def daemonize!(options = {}, &block)
411
+ # optional directory and umask
412
+ #
413
+ chdir = options[:chdir] || options['chdir'] || @daemon_dir || '.'
414
+ umask = options[:umask] || options['umask'] || 0
415
+
416
+ # drop to the background avoiding the possibility of zombies..
417
+ #
418
+ detach!(&block)
419
+
420
+ # close all open io handles *except* these ones
421
+ #
422
+ keep_ios(STDIN, STDOUT, STDERR, @lock)
423
+
424
+ # sane directory and umask
425
+ #
426
+ Dir::chdir(chdir)
427
+ File::umask(umask)
428
+
429
+ # global daemon flag
430
+ #
431
+ $DAEMON = true
432
+ end
433
+
434
+ def detach!(&block)
435
+ # setup a pipe to relay the grandchild pid through
436
+ #
437
+ a, b = IO.pipe
438
+
439
+ # in the parent we wait for the pid, wait on our child to avoid zombies, and
440
+ # then exit
441
+ #
442
+ if fork
443
+ b.close
444
+ pid = Integer(a.read.strip)
445
+ a.close
446
+ block.call(pid) if block
447
+ Process.waitall
448
+ exit!
449
+ end
450
+
451
+ # the child simply exits so it can be reaped - avoiding zombies. the pipes
452
+ # are inherited in the grandchild
453
+ #
454
+ if fork
455
+ exit!
456
+ end
457
+
458
+ # finally, the grandchild sends it's pid back up the pipe to the parent is
459
+ # aware of the pid
460
+ #
461
+ a.close
462
+ b.puts(Process.pid)
463
+ b.close
464
+
465
+ # might as well nohup too...
466
+ #
467
+ Process::setsid rescue nil
468
+ end
469
+
470
+ def redirect_io!(options = {})
471
+ stdin = options[:stdin] || @stdin_file
472
+ stdout = options[:stdout] || @stdout_file
473
+ stderr = options[:stderr] || @stderr_file
474
+
475
+ {
476
+ STDIN => stdin, STDOUT => stdout, STDERR => stderr
477
+ }.each do |io, file|
478
+ opened = false
479
+
480
+ fd =
481
+ case
482
+ when file.is_a?(IO)
483
+ file
484
+ when file.to_s == 'null'
485
+ opened = true
486
+ open('/dev/null', 'ab+')
487
+ else
488
+ opened = true
489
+ open(file, 'ab+')
490
+ end
491
+
492
+ begin
493
+ fd.sync = true rescue nil
494
+ fd.truncate(0) rescue nil
495
+ io.reopen(fd)
496
+ ensure
497
+ fd.close rescue nil if opened
498
+ end
499
+ end
500
+ end
501
+
502
+ def keep_ios(*ios)
503
+ filenos = []
504
+
505
+ ios.flatten.compact.each do |io|
506
+ begin
507
+ fileno = io.respond_to?(:fileno) ? io.fileno : Integer(io)
508
+ filenos.push(fileno)
509
+ rescue Object
510
+ next
511
+ end
512
+ end
513
+
514
+ ObjectSpace.each_object(IO) do |io|
515
+ begin
516
+ fileno = io.fileno
517
+ next if filenos.include?(fileno)
518
+ io.close unless io.closed?
519
+ rescue Object
520
+ next
521
+ end
522
+ end
523
+ end
524
+ end
525
+ end
@@ -3,6 +3,7 @@ module Main
3
3
  module ClassMethods
4
4
  fattr('name'){ File.basename($0) }
5
5
  fattr('program'){ File.basename($0) }
6
+ fattr('script'){ File.expand_path(*[script=$0, cwd=ENV['PWD']].compact) }
6
7
  fattr('synopsis'){ Main::Usage.default_synopsis(self) }
7
8
  fattr('description')
8
9
  fattr('usage'){ Main::Usage.default_usage(self) }
@@ -374,6 +375,16 @@ module Main
374
375
  o = output(*[args.shift].compact).default('-')
375
376
  [i, o]
376
377
  end
378
+
379
+ def daemonizes!(*args)
380
+ mode(:daemon){
381
+ run {
382
+ cmd = argv.shift || :usage
383
+
384
+ daemon.cmd(cmd)
385
+ }
386
+ }
387
+ end
377
388
  end
378
389
 
379
390
  extend ClassMethods
@@ -11,6 +11,7 @@ module Main
11
11
  fattr('stdout'){ main.stdout }
12
12
  fattr('stderr'){ main.stderr }
13
13
  fattr('logger'){ main.logger }
14
+ fattr('script'){ main.script }
14
15
  fattr('params')
15
16
  fattr('finalizers')
16
17
 
@@ -296,6 +297,10 @@ module Main
296
297
  def output
297
298
  @output ||= params[:output].value if params[:output]
298
299
  end
300
+
301
+ def daemon
302
+ @daemon ||= Main::Daemon.new(self)
303
+ end
299
304
  end
300
305
 
301
306
  include InstanceMethods
@@ -3,7 +3,7 @@
3
3
 
4
4
  Gem::Specification::new do |spec|
5
5
  spec.name = "main"
6
- spec.version = "6.0.0"
6
+ spec.version = "6.1.0"
7
7
  spec.platform = Gem::Platform::RUBY
8
8
  spec.summary = "main"
9
9
  spec.description = "a class factory and dsl for generating command line programs real quick"
@@ -20,6 +20,7 @@ Gem::Specification::new do |spec|
20
20
  "lib/main",
21
21
  "lib/main.rb",
22
22
  "lib/main/cast.rb",
23
+ "lib/main/daemon.rb",
23
24
  "lib/main/dsl.rb",
24
25
  "lib/main/factories.rb",
25
26
  "lib/main/getoptlong.rb",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: main
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0
4
+ version: 6.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ara T. Howard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-09 00:00:00.000000000 Z
11
+ date: 2014-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chronic
@@ -80,6 +80,7 @@ files:
80
80
  - a.rb
81
81
  - lib/main.rb
82
82
  - lib/main/cast.rb
83
+ - lib/main/daemon.rb
83
84
  - lib/main/dsl.rb
84
85
  - lib/main/factories.rb
85
86
  - lib/main/getoptlong.rb