gregfitz23-chrono_trigger 0.0.3 → 0.0.4
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/History.txt +4 -0
- data/Manifest.txt +3 -0
- data/PostInstall.txt +0 -2
- data/README.rdoc +1 -3
- data/VERSION.yml +1 -1
- data/bin/chrono_trigger +7 -0
- data/lib/chrono_trigger/process.rb +22 -0
- data/lib/chrono_trigger/runner.rb +276 -0
- data/lib/chrono_trigger/shell.rb +1 -0
- data/lib/chrono_trigger/trigger.rb +1 -0
- data/lib/triggers/test_triggers.rb +1 -1
- metadata +8 -5
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
@@ -3,8 +3,11 @@ Manifest.txt
|
|
3
3
|
PostInstall.txt
|
4
4
|
README.rdoc
|
5
5
|
Rakefile
|
6
|
+
bin/chrono_trigger
|
6
7
|
lib/chrono_trigger.rb
|
7
8
|
lib/chrono_trigger/cron_entry.rb
|
9
|
+
lib/chrono_trigger/process.rb
|
10
|
+
lib/chrono_trigger/runner.rb
|
8
11
|
lib/chrono_trigger/shell.rb
|
9
12
|
lib/chrono_trigger/tasks.rb
|
10
13
|
lib/chrono_trigger/trigger.rb
|
data/PostInstall.txt
CHANGED
data/README.rdoc
CHANGED
@@ -10,8 +10,6 @@ A cron framework for defining cron tasks using a readable DSL.
|
|
10
10
|
|
11
11
|
== SYNOPSIS:
|
12
12
|
|
13
|
-
After installation, create lib/tasks/chrono_trigger.rake containing: require 'chrono_trigger/tasks'
|
14
|
-
|
15
13
|
Create trigger files (by default in the lib/triggers) directory.
|
16
14
|
Triggers should follow the pattern:
|
17
15
|
|
@@ -22,7 +20,7 @@ trigger "name" do
|
|
22
20
|
at :hour=>9, :minute=>[30,50]
|
23
21
|
end
|
24
22
|
|
25
|
-
|
23
|
+
Run chrono_trigger -t{full path to trigger file}
|
26
24
|
|
27
25
|
== REQUIREMENTS:
|
28
26
|
|
data/VERSION.yml
CHANGED
data/bin/chrono_trigger
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module ChronoTrigger
|
2
|
+
|
3
|
+
class Process
|
4
|
+
|
5
|
+
def run(options={})
|
6
|
+
@t = Thread.new do
|
7
|
+
shell = ChronoTrigger::Shell.new
|
8
|
+
shell.load_triggers(options[:trigger_file])
|
9
|
+
loop do
|
10
|
+
shell.execute_triggers
|
11
|
+
sleep 1.minute.to_i
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
@t.join
|
16
|
+
end
|
17
|
+
|
18
|
+
def stop
|
19
|
+
@t.exit
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,276 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'process')
|
2
|
+
require "chrono_trigger"
|
3
|
+
require 'optparse'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module ChronoTrigger
|
7
|
+
class Runner
|
8
|
+
|
9
|
+
attr_accessor :options
|
10
|
+
private :options, :options=
|
11
|
+
|
12
|
+
def self.run
|
13
|
+
new
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.shutdown
|
17
|
+
@@instance.shutdown
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@@instance = self
|
22
|
+
parse_options
|
23
|
+
|
24
|
+
@process = ProcessHelper.new(options[:logger], options[:pid_file], options[:user], options[:group])
|
25
|
+
|
26
|
+
if options[:stop]
|
27
|
+
@process.kill
|
28
|
+
exit(1)
|
29
|
+
end
|
30
|
+
|
31
|
+
pid = @process.running?
|
32
|
+
if pid
|
33
|
+
if options[:force]
|
34
|
+
STDERR.puts "Shutting down existing ChronoTrigger."
|
35
|
+
@process.kill
|
36
|
+
@process = ProcessHelper.new(options[:logger], options[:pid_file], options[:user], options[:group])
|
37
|
+
else
|
38
|
+
STDERR.puts "There is already a ChronoTrigger process running (pid #{pid}), exiting."
|
39
|
+
exit(1)
|
40
|
+
end
|
41
|
+
elsif pid.nil?
|
42
|
+
STDERR.puts "Cleaning up stale pidfile at #{options[:pid_file]}."
|
43
|
+
end
|
44
|
+
|
45
|
+
start
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_options
|
49
|
+
self.options = {
|
50
|
+
:log_level => Logger::INFO,
|
51
|
+
:daemonize => false,
|
52
|
+
:pid_file => File.join('', 'var', 'run', 'chrono_trigger.pid')
|
53
|
+
}
|
54
|
+
|
55
|
+
OptionParser.new do |opts|
|
56
|
+
opts.summary_width = 25
|
57
|
+
|
58
|
+
opts.banner = "ChronoTrigger\n\n",
|
59
|
+
"usage: chrono_trigger [options...]\n",
|
60
|
+
" chrono_trigger --help\n",
|
61
|
+
" chrono_trigger --version\n"
|
62
|
+
|
63
|
+
opts.separator ""
|
64
|
+
opts.separator ""; opts.separator "ChronoTrigger Options:"
|
65
|
+
|
66
|
+
opts.on("-tTRIGGER_FILE", "--triggers TRIGGERS", "Path to file specifying triggers to be executed") do |trigger_file|
|
67
|
+
options[:trigger_file] = trigger_file
|
68
|
+
end
|
69
|
+
|
70
|
+
opts.on("-f", "--force", "Force restart of ChronoTrigger process.") do
|
71
|
+
options[:force] = true
|
72
|
+
end
|
73
|
+
|
74
|
+
opts.on("-s", "--stop", "Stop a currently running ChronoTrigger process.") do
|
75
|
+
options[:stop] = true
|
76
|
+
end
|
77
|
+
|
78
|
+
opts.separator ""
|
79
|
+
opts.separator ""; opts.separator "Process:"
|
80
|
+
|
81
|
+
opts.on("-PFILE", "--pid FILENAME", "save PID in FILENAME when using -d option.", "(default: #{options[:pid_file]})") do |pid_file|
|
82
|
+
options[:pid_file] = File.expand_path(pid_file)
|
83
|
+
end
|
84
|
+
|
85
|
+
opts.on("-u", "--user USER", "User to run as") do |user|
|
86
|
+
options[:user] = user.to_i == 0 ? Etc.getpwnam(user).uid : user.to_i
|
87
|
+
end
|
88
|
+
|
89
|
+
opts.on("-gGROUP", "--group GROUP", "Group to run as") do |group|
|
90
|
+
options[:group] = group.to_i == 0 ? Etc.getgrnam(group).gid : group.to_i
|
91
|
+
end
|
92
|
+
|
93
|
+
opts.separator ""; opts.separator "Logging:"
|
94
|
+
|
95
|
+
opts.on("-L", "--log [FILE]", "Path to print debugging information.") do |log_path|
|
96
|
+
options[:logger] = File.expand_path(log_path)
|
97
|
+
end
|
98
|
+
|
99
|
+
opts.on("-v", "Increase logging verbosity (may be used multiple times).") do
|
100
|
+
options[:log_level] -= 1
|
101
|
+
end
|
102
|
+
|
103
|
+
opts.on("-d", "Run as a daemon.") do
|
104
|
+
options[:daemonize] = true
|
105
|
+
end
|
106
|
+
end.parse!
|
107
|
+
end
|
108
|
+
|
109
|
+
def start
|
110
|
+
drop_privileges
|
111
|
+
|
112
|
+
@process.daemonize if options[:daemonize]
|
113
|
+
|
114
|
+
setup_signal_traps
|
115
|
+
@process.write_pid_file
|
116
|
+
|
117
|
+
STDOUT.puts "Starting ChronoTrigger."
|
118
|
+
@chrono_trigger_process = ChronoTrigger::Process.new
|
119
|
+
@chrono_trigger_process.run(options)
|
120
|
+
|
121
|
+
@process.remove_pid_file
|
122
|
+
end
|
123
|
+
|
124
|
+
def drop_privileges
|
125
|
+
::Process.egid = options[:group] if options[:group]
|
126
|
+
::Process.euid = options[:user] if options[:user]
|
127
|
+
end
|
128
|
+
|
129
|
+
def shutdown
|
130
|
+
begin
|
131
|
+
STDOUT.puts "Shutting down."
|
132
|
+
@chrono_trigger_process.stop
|
133
|
+
exit(1)
|
134
|
+
rescue Object => e
|
135
|
+
STDERR.puts "There was an error shutting down: #{e}"
|
136
|
+
exit(70)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def setup_signal_traps
|
141
|
+
Signal.trap("INT") { shutdown }
|
142
|
+
Signal.trap("TERM") { shutdown }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class ProcessHelper
|
147
|
+
|
148
|
+
def initialize(log_file = nil, pid_file = nil, user = nil, group = nil)
|
149
|
+
@log_file = log_file
|
150
|
+
@pid_file = pid_file
|
151
|
+
@user = user
|
152
|
+
@group = group
|
153
|
+
end
|
154
|
+
|
155
|
+
def safefork
|
156
|
+
begin
|
157
|
+
if pid = fork
|
158
|
+
return pid
|
159
|
+
end
|
160
|
+
rescue Errno::EWOULDBLOCK
|
161
|
+
sleep 5
|
162
|
+
retry
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def daemonize
|
167
|
+
sess_id = detach_from_terminal
|
168
|
+
exit if pid = safefork
|
169
|
+
|
170
|
+
Dir.chdir("/")
|
171
|
+
File.umask 0000
|
172
|
+
|
173
|
+
close_io_handles
|
174
|
+
redirect_io
|
175
|
+
|
176
|
+
return sess_id
|
177
|
+
end
|
178
|
+
|
179
|
+
def detach_from_terminal
|
180
|
+
srand
|
181
|
+
safefork and exit
|
182
|
+
|
183
|
+
unless sess_id = ::Process.setsid
|
184
|
+
raise "Couldn't detach from controlling terminal."
|
185
|
+
end
|
186
|
+
|
187
|
+
trap 'SIGHUP', 'IGNORE'
|
188
|
+
|
189
|
+
sess_id
|
190
|
+
end
|
191
|
+
|
192
|
+
def close_io_handles
|
193
|
+
ObjectSpace.each_object(IO) do |io|
|
194
|
+
unless [STDIN, STDOUT, STDERR].include?(io)
|
195
|
+
begin
|
196
|
+
io.close unless io.closed?
|
197
|
+
rescue Exception
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def redirect_io
|
204
|
+
begin; STDIN.reopen('/dev/null'); rescue Exception; end
|
205
|
+
|
206
|
+
if @log_file
|
207
|
+
begin
|
208
|
+
STDOUT.reopen(@log_file, "a")
|
209
|
+
STDOUT.sync = true
|
210
|
+
rescue Exception
|
211
|
+
begin; STDOUT.reopen('/dev/null'); rescue Exception; end
|
212
|
+
end
|
213
|
+
else
|
214
|
+
begin; STDOUT.reopen('/dev/null'); rescue Exception; end
|
215
|
+
end
|
216
|
+
|
217
|
+
begin; STDERR.reopen(STDOUT); rescue Exception; end
|
218
|
+
STDERR.sync = true
|
219
|
+
end
|
220
|
+
|
221
|
+
def rescue_exception
|
222
|
+
begin
|
223
|
+
yield
|
224
|
+
rescue Exception
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def write_pid_file
|
229
|
+
return unless @pid_file
|
230
|
+
FileUtils.mkdir_p(File.dirname(@pid_file))
|
231
|
+
File.open(@pid_file, "w") { |f| f.write(::Process.pid) }
|
232
|
+
File.chmod(0644, @pid_file)
|
233
|
+
end
|
234
|
+
|
235
|
+
def remove_pid_file
|
236
|
+
return unless @pid_file
|
237
|
+
File.unlink(@pid_file) if File.exists?(@pid_file)
|
238
|
+
end
|
239
|
+
|
240
|
+
def running?
|
241
|
+
return false unless @pid_file
|
242
|
+
|
243
|
+
pid = File.read(@pid_file).chomp.to_i rescue nil
|
244
|
+
pid = nil if pid == 0
|
245
|
+
return false unless pid
|
246
|
+
|
247
|
+
begin
|
248
|
+
::Process.kill(0, pid)
|
249
|
+
return pid
|
250
|
+
rescue Errno::ESRCH
|
251
|
+
return nil
|
252
|
+
rescue Errno::EPERM
|
253
|
+
return pid
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def kill
|
258
|
+
return false unless @pid_file
|
259
|
+
|
260
|
+
pid = File.read(@pid_file).chomp.to_i rescue nil
|
261
|
+
pid = nil if pid == 0
|
262
|
+
return false unless pid
|
263
|
+
|
264
|
+
begin
|
265
|
+
::Process.kill("TERM", pid)
|
266
|
+
remove_pid_file
|
267
|
+
return pid
|
268
|
+
rescue Errno::ESRCH
|
269
|
+
return nil
|
270
|
+
rescue Errno::EPERM
|
271
|
+
return pid
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
data/lib/chrono_trigger/shell.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gregfitz23-chrono_trigger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg Fitzgerald
|
@@ -9,14 +9,14 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-04-
|
13
|
-
default_executable:
|
12
|
+
date: 2009-04-09 00:00:00 -07:00
|
13
|
+
default_executable: chrono_trigger
|
14
14
|
dependencies: []
|
15
15
|
|
16
16
|
description: TODO
|
17
17
|
email: greg_fitz@yahoo.com
|
18
|
-
executables:
|
19
|
-
|
18
|
+
executables:
|
19
|
+
- chrono_trigger
|
20
20
|
extensions: []
|
21
21
|
|
22
22
|
extra_rdoc_files:
|
@@ -27,8 +27,11 @@ files:
|
|
27
27
|
- PostInstall.txt
|
28
28
|
- README.rdoc
|
29
29
|
- VERSION.yml
|
30
|
+
- bin/chrono_trigger
|
30
31
|
- lib/chrono_trigger
|
31
32
|
- lib/chrono_trigger/cron_entry.rb
|
33
|
+
- lib/chrono_trigger/process.rb
|
34
|
+
- lib/chrono_trigger/runner.rb
|
32
35
|
- lib/chrono_trigger/shell.rb
|
33
36
|
- lib/chrono_trigger/tasks.rb
|
34
37
|
- lib/chrono_trigger/trigger.rb
|