main 6.0.0 → 6.1.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.
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