chrono_trigger 0.2.1 → 1.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.
- checksums.yaml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +167 -0
- data/LICENSE.txt +21 -0
- data/README.md +2 -0
- data/Rakefile +3 -25
- data/SUMMARY.md +4 -0
- data/bin/console +7 -0
- data/bin/loc +3 -0
- data/bin/setup +8 -0
- data/bin/standardize +4 -0
- data/lib/chrono_trigger.rb +28 -12
- data/lib/chrono_trigger/clock.rb +50 -0
- data/lib/chrono_trigger/config.rb +12 -0
- data/lib/chrono_trigger/event.rb +118 -0
- data/lib/chrono_trigger/schedule.rb +71 -0
- data/lib/chrono_trigger/timeline.rb +28 -0
- data/lib/chrono_trigger/version.rb +2 -3
- data/lib/chrono_trigger/worker.rb +10 -0
- metadata +159 -155
- data/.project +0 -11
- data/History.txt +0 -21
- data/Manifest.txt +0 -25
- data/PostInstall.txt +0 -1
- data/README.rdoc +0 -61
- data/VERSION.yml +0 -4
- data/bin/chrono_trigger +0 -7
- data/chrono_trigger.gemspec +0 -72
- data/lib/chrono_trigger/cron_entry.rb +0 -71
- data/lib/chrono_trigger/process.rb +0 -37
- data/lib/chrono_trigger/runner.rb +0 -293
- data/lib/chrono_trigger/shell.rb +0 -36
- data/lib/chrono_trigger/tasks.rb +0 -3
- data/lib/chrono_trigger/trigger.rb +0 -127
- data/lib/tasks/chrono_trigger.rake +0 -14
- data/lib/triggers/test_triggers.rb +0 -31
- data/script/console +0 -10
- data/script/destroy +0 -14
- data/script/generate +0 -14
- data/test/test_chrono_trigger.rb +0 -11
- data/test/test_cron_entry.rb +0 -198
- data/test/test_helper.rb +0 -14
- data/test/test_shell.rb +0 -52
- data/test/test_trigger.rb +0 -170
- data/test/triggers.rb +0 -17
data/.project
DELETED
data/History.txt
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
== 0.2.1 2013-01-15
|
2
|
-
* 1 patch
|
3
|
-
* Upgraded support for Ruby 1.8.6
|
4
|
-
|
5
|
-
== 0.2.0 2013-01-10
|
6
|
-
* 1 enhancement
|
7
|
-
* Upgraded to support Ruby 1.9.x
|
8
|
-
|
9
|
-
== 0.0.4 2009-04-09
|
10
|
-
* 1 enhancement
|
11
|
-
* Added chrono_trigger binary and supporting files
|
12
|
-
|
13
|
-
== 0.0.2 2009-04-02
|
14
|
-
|
15
|
-
* 1 minor enhancement:
|
16
|
-
* Updated to add tasks file
|
17
|
-
|
18
|
-
== 0.0.1 2009-04-02
|
19
|
-
|
20
|
-
* 1 major enhancement:
|
21
|
-
* Initial release
|
data/Manifest.txt
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
History.txt
|
2
|
-
Manifest.txt
|
3
|
-
PostInstall.txt
|
4
|
-
README.rdoc
|
5
|
-
Rakefile
|
6
|
-
bin/chrono_trigger
|
7
|
-
lib/chrono_trigger.rb
|
8
|
-
lib/chrono_trigger/cron_entry.rb
|
9
|
-
lib/chrono_trigger/process.rb
|
10
|
-
lib/chrono_trigger/runner.rb
|
11
|
-
lib/chrono_trigger/shell.rb
|
12
|
-
lib/chrono_trigger/tasks.rb
|
13
|
-
lib/chrono_trigger/trigger.rb
|
14
|
-
lib/chrono_trigger/version.rb
|
15
|
-
lib/triggers/test_triggers.rb
|
16
|
-
script/console
|
17
|
-
script/destroy
|
18
|
-
script/generate
|
19
|
-
tasks/chrono_trigger.rake
|
20
|
-
test/test_chrono_trigger.rb
|
21
|
-
test/test_cron_entry.rb
|
22
|
-
test/test_helper.rb
|
23
|
-
test/test_shell.rb
|
24
|
-
test/test_trigger.rb
|
25
|
-
test/triggers.rb
|
data/PostInstall.txt
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
|
data/README.rdoc
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
= chrono_trigger
|
2
|
-
|
3
|
-
This is a branch of http://github.com/gregfitz23/chrono_trigger/tree/master as it hasn't been updated in sometime.
|
4
|
-
New code can be found at https://github.com/darful/chrono_trigger. Now with Ruby 1.9.x support.
|
5
|
-
|
6
|
-
|
7
|
-
== DESCRIPTION:
|
8
|
-
|
9
|
-
A cron framework for defining cron tasks using a readable DSL.
|
10
|
-
|
11
|
-
== FEATURES/PROBLEMS:
|
12
|
-
|
13
|
-
== SYNOPSIS:
|
14
|
-
|
15
|
-
Create trigger files directory.
|
16
|
-
Triggers should follow the pattern:
|
17
|
-
|
18
|
-
trigger "name" do
|
19
|
-
runs { code to execute }
|
20
|
-
on :monday
|
21
|
-
every :minutes=>10
|
22
|
-
at :hour=>9, :minute=>[30,50]
|
23
|
-
end
|
24
|
-
Run `chrono_trigger -t{full path to trigger file}`.
|
25
|
-
Other available options are:
|
26
|
-
* -a - Specify an application context for the triggers to run against.
|
27
|
-
* -e - Specify the environment the triggers should run against
|
28
|
-
|
29
|
-
== REQUIREMENTS:
|
30
|
-
|
31
|
-
* ActiveSupport >= 2.3.4
|
32
|
-
* Ruby >= 1.8.6
|
33
|
-
|
34
|
-
== INSTALL:
|
35
|
-
|
36
|
-
* gem install chrono_trigger
|
37
|
-
|
38
|
-
== LICENSE:
|
39
|
-
|
40
|
-
(The MIT License)
|
41
|
-
|
42
|
-
Copyright (c) 2009 FIXME full name
|
43
|
-
|
44
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
45
|
-
a copy of this software and associated documentation files (the
|
46
|
-
'Software'), to deal in the Software without restriction, including
|
47
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
48
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
49
|
-
permit persons to whom the Software is furnished to do so, subject to
|
50
|
-
the following conditions:
|
51
|
-
|
52
|
-
The above copyright notice and this permission notice shall be
|
53
|
-
included in all copies or substantial portions of the Software.
|
54
|
-
|
55
|
-
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
56
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
57
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
58
|
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
59
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
60
|
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
61
|
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/VERSION.yml
DELETED
data/bin/chrono_trigger
DELETED
data/chrono_trigger.gemspec
DELETED
@@ -1,72 +0,0 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
-
|
5
|
-
# -*- encoding: utf-8 -*-
|
6
|
-
|
7
|
-
Gem::Specification.new do |s|
|
8
|
-
|
9
|
-
s.name = "chrono_trigger"
|
10
|
-
s.authors = ["Jon Ciccone"]
|
11
|
-
s.date = "2013-01-10"
|
12
|
-
s.summary = "Rails cron jobs."
|
13
|
-
s.description = "This gem allows you to write, deploy, and maintain cron jobs withing the rails framework."
|
14
|
-
s.email = "darful@gmail.com"
|
15
|
-
s.homepage = "https://github.com/darful/chrono_trigger"
|
16
|
-
s.version = '0.2.1'
|
17
|
-
|
18
|
-
s.files = [
|
19
|
-
".project",
|
20
|
-
"History.txt",
|
21
|
-
"Manifest.txt",
|
22
|
-
"PostInstall.txt",
|
23
|
-
"README.rdoc",
|
24
|
-
"Rakefile",
|
25
|
-
"VERSION.yml",
|
26
|
-
"bin/chrono_trigger",
|
27
|
-
"chrono_trigger.gemspec",
|
28
|
-
"lib/chrono_trigger.rb",
|
29
|
-
"lib/chrono_trigger/cron_entry.rb",
|
30
|
-
"lib/chrono_trigger/process.rb",
|
31
|
-
"lib/chrono_trigger/runner.rb",
|
32
|
-
"lib/chrono_trigger/shell.rb",
|
33
|
-
"lib/chrono_trigger/tasks.rb",
|
34
|
-
"lib/chrono_trigger/trigger.rb",
|
35
|
-
"lib/chrono_trigger/version.rb",
|
36
|
-
"lib/tasks/chrono_trigger.rake",
|
37
|
-
"lib/triggers/test_triggers.rb",
|
38
|
-
"script/console",
|
39
|
-
"script/destroy",
|
40
|
-
"script/generate",
|
41
|
-
"test/test_chrono_trigger.rb",
|
42
|
-
"test/test_cron_entry.rb",
|
43
|
-
"test/test_helper.rb",
|
44
|
-
"test/test_shell.rb",
|
45
|
-
"test/test_trigger.rb",
|
46
|
-
"test/triggers.rb"
|
47
|
-
]
|
48
|
-
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
49
|
-
s.test_files = s.files.grep(%r{^(test)/})
|
50
|
-
s.require_paths = ["lib"]
|
51
|
-
s.extra_rdoc_files = ["README.rdoc"]
|
52
|
-
|
53
|
-
s.add_dependency "activesupport", ">= 2.3.4"
|
54
|
-
#s.add_dependency "activerecord", ">= 2.3.4"
|
55
|
-
|
56
|
-
s.add_development_dependency "shoulda", ">= 2.10"
|
57
|
-
s.add_development_dependency "mocha", ">= 0.9.5"
|
58
|
-
|
59
|
-
s.add_development_dependency "rubyforge"
|
60
|
-
s.add_development_dependency "git"
|
61
|
-
s.add_development_dependency "technicalpickles-jeweler"
|
62
|
-
|
63
|
-
if s.respond_to? :specification_version then
|
64
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
65
|
-
s.specification_version = 3
|
66
|
-
|
67
|
-
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
68
|
-
else
|
69
|
-
end
|
70
|
-
else
|
71
|
-
end
|
72
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
module ChronoTrigger
|
2
|
-
|
3
|
-
class CronEntry
|
4
|
-
|
5
|
-
def initialize(options={})
|
6
|
-
set_days(options[:days])
|
7
|
-
set_hours(options[:hours])
|
8
|
-
set_minutes(options[:minutes])
|
9
|
-
end
|
10
|
-
|
11
|
-
DAYS_CONVERSION = {
|
12
|
-
:sunday => 0,
|
13
|
-
:monday => 1,
|
14
|
-
:tuesday => 2,
|
15
|
-
:wednesday => 3,
|
16
|
-
:thursday => 4,
|
17
|
-
:friday => 5,
|
18
|
-
:saturday => 6
|
19
|
-
}
|
20
|
-
|
21
|
-
CALENDAR_DAYS = *(1..31)
|
22
|
-
|
23
|
-
def set_hours(*args)
|
24
|
-
args.compact!
|
25
|
-
args.flatten!
|
26
|
-
raise ChronoTrigger::ConfigurationException.new("Hours must be less than 24") if args.any? {|hour| hour >= 24}
|
27
|
-
@hours = args
|
28
|
-
end
|
29
|
-
|
30
|
-
def set_days(*args)
|
31
|
-
args.compact!
|
32
|
-
args.flatten!
|
33
|
-
args.each {|day| raise ChronoTrigger::ConfigurationException.new("Day #{day} setting is invalid") if !DAYS_CONVERSION.keys.include?(day)}
|
34
|
-
@days = args.map { |day| DAYS_CONVERSION[day] }
|
35
|
-
end
|
36
|
-
|
37
|
-
def set_minutes(*args)
|
38
|
-
args.compact!
|
39
|
-
args.flatten!
|
40
|
-
raise ChronoTrigger::ConfigurationException.new("Minutes must be less than 60") if args.any? {|minute| minute >= 60}
|
41
|
-
@minutes = args
|
42
|
-
end
|
43
|
-
|
44
|
-
def set_calendar_days(*args)
|
45
|
-
args.compact!
|
46
|
-
args.flatten!
|
47
|
-
args.each {|calendar_day| raise ChronoTrigger::ConfigurationException.new("Calendar Day #{calendar_day} setting is invalid") if !CALENDAR_DAYS.include?(calendar_day)}
|
48
|
-
@calendar_days = args
|
49
|
-
end
|
50
|
-
|
51
|
-
def matches?(datetime)
|
52
|
-
if @minutes.blank? && !@days.blank?
|
53
|
-
raise ChronoTrigger::ConfigurationException.new("Days were specified for a CronEntry with no minutes specified")
|
54
|
-
end
|
55
|
-
|
56
|
-
if (@minutes.blank? || @hours.blank?) && !@calendar_days.blank?
|
57
|
-
raise ChronoTrigger::ConfigurationException.new("Calendar Days were specified for a CronEntry with no minutes and/or no hours specified")
|
58
|
-
end
|
59
|
-
|
60
|
-
if !@days.blank? && !@calendar_days.blank?
|
61
|
-
raise ChronoTrigger::ConfigurationException.new("Calendar Days and Days were specified. This is unsupported.")
|
62
|
-
end
|
63
|
-
|
64
|
-
return false if !@minutes.blank? && !@minutes.include?(datetime.min)
|
65
|
-
return false if !@hours.blank? && !@hours.include?(datetime.hour)
|
66
|
-
return false if (!@days.blank? && !@days.include?(datetime.wday)) || (!@calendar_days.blank? && !@calendar_days.include?(datetime.day))
|
67
|
-
return true
|
68
|
-
end
|
69
|
-
|
70
|
-
end
|
71
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module ChronoTrigger
|
2
|
-
|
3
|
-
class Process
|
4
|
-
|
5
|
-
def run(options={})
|
6
|
-
@thread = Thread.new do
|
7
|
-
setup(options)
|
8
|
-
|
9
|
-
shell = ChronoTrigger::Shell.new
|
10
|
-
options[:trigger_files] ? shell.load_triggers(options[:trigger_files].split(":")) : shell.load_triggers
|
11
|
-
loop do
|
12
|
-
shell.execute_triggers
|
13
|
-
sleep 1.minute.to_i
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
@thread.join
|
18
|
-
end
|
19
|
-
|
20
|
-
def stop
|
21
|
-
@thread.exit
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
def setup(options={})
|
26
|
-
if application_context = options[:application_context]
|
27
|
-
ENV['RAILS_ENV'] = options[:env] || "development"
|
28
|
-
|
29
|
-
application_path = File.join(application_context, 'config', 'environment')
|
30
|
-
STDOUT.puts "Loading application environment at #{File.join(application_context, 'config', 'environment')} for '#{ENV['RAILS_ENV']}' environment."
|
31
|
-
require(application_path)
|
32
|
-
end
|
33
|
-
|
34
|
-
require "chrono_trigger"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,293 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'process')
|
2
|
-
require "logger"
|
3
|
-
require 'optparse'
|
4
|
-
require 'yaml'
|
5
|
-
require 'fileutils'
|
6
|
-
|
7
|
-
module ChronoTrigger
|
8
|
-
class Runner
|
9
|
-
|
10
|
-
attr_accessor :options
|
11
|
-
private :options, :options=
|
12
|
-
|
13
|
-
def self.run
|
14
|
-
new
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.shutdown
|
18
|
-
@@instance.shutdown
|
19
|
-
end
|
20
|
-
|
21
|
-
def initialize
|
22
|
-
@@instance = self
|
23
|
-
parse_options
|
24
|
-
|
25
|
-
@process = ProcessHelper.new(options[:logger], options[:pid_file], options[:user], options[:group])
|
26
|
-
|
27
|
-
if options[:stop]
|
28
|
-
@process.kill
|
29
|
-
exit(1)
|
30
|
-
end
|
31
|
-
|
32
|
-
pid = @process.running?
|
33
|
-
if pid
|
34
|
-
if options[:force]
|
35
|
-
STDOUT.puts "Shutting down existing ChronoTrigger."
|
36
|
-
@process.kill
|
37
|
-
@process = ProcessHelper.new(options[:logger], options[:pid_file], options[:user], options[:group])
|
38
|
-
else
|
39
|
-
STDERR.puts "There is already a ChronoTrigger process running (pid #{pid}), exiting."
|
40
|
-
exit(1)
|
41
|
-
end
|
42
|
-
elsif pid.nil?
|
43
|
-
STDERR.puts "Cleaning up stale pidfile at #{options[:pid_file]}."
|
44
|
-
end
|
45
|
-
|
46
|
-
start
|
47
|
-
end
|
48
|
-
|
49
|
-
def parse_options
|
50
|
-
self.options = {
|
51
|
-
:log_level => Logger::INFO,
|
52
|
-
:daemonize => false,
|
53
|
-
:pid_file => File.join('', 'var', 'run', 'chrono_trigger.pid'),
|
54
|
-
:env => "development"
|
55
|
-
}
|
56
|
-
|
57
|
-
OptionParser.new do |opts|
|
58
|
-
opts.summary_width = 25
|
59
|
-
|
60
|
-
opts.banner = "ChronoTrigger - Execute cron jobs within the context of a Rails application\n\n",
|
61
|
-
"usage: chrono_trigger [options...]\n",
|
62
|
-
" chrono_trigger --help\n",
|
63
|
-
" chrono_trigger --version\n"
|
64
|
-
|
65
|
-
opts.separator ""
|
66
|
-
opts.separator ""; opts.separator "ChronoTrigger Options:"
|
67
|
-
|
68
|
-
opts.on("-tTRIGGER_FILES", "--triggers TRIGGER_FILES", "Path to file(s) specifying triggers to be executed. Multiple files should be separated by a :. When also specifying -a, this path will be relative to the application path") do |trigger_files|
|
69
|
-
options[:trigger_files] = trigger_files
|
70
|
-
end
|
71
|
-
|
72
|
-
opts.on("-f", "--force", "Force restart of ChronoTrigger process (can be used in conjunction with -P).") do
|
73
|
-
options[:force] = true
|
74
|
-
end
|
75
|
-
|
76
|
-
opts.on("-s", "--stop", "Stop a currently running ChronoTrigger process (can be used in conjunction with -P).") do
|
77
|
-
options[:stop] = true
|
78
|
-
end
|
79
|
-
|
80
|
-
opts.separator ""
|
81
|
-
opts.separator ""; opts.separator "Rails options:"
|
82
|
-
|
83
|
-
opts.on("-aAPPLICATION", "--application RAILS", "Path to Rails application context to execture triggers in.") do |application_context|
|
84
|
-
options[:application_context] = application_context
|
85
|
-
end
|
86
|
-
|
87
|
-
opts.on("-eENVIRONMENT", "--environment ENVIRONMENT", "Rails environment to execute triggers in.") do |environment|
|
88
|
-
options[:env] = environment
|
89
|
-
end
|
90
|
-
|
91
|
-
opts.separator ""
|
92
|
-
opts.separator ""; opts.separator "Process:"
|
93
|
-
|
94
|
-
opts.on("-PFILE", "--pid FILENAME", "save PID in FILENAME when using -d option.", "(default: #{options[:pid_file]})") do |pid_file|
|
95
|
-
options[:pid_file] = File.expand_path(pid_file)
|
96
|
-
end
|
97
|
-
|
98
|
-
opts.on("-u", "--user USER", "User to run as") do |user|
|
99
|
-
options[:user] = user.to_i == 0 ? Etc.getpwnam(user).uid : user.to_i
|
100
|
-
end
|
101
|
-
|
102
|
-
opts.on("-gGROUP", "--group GROUP", "Group to run as") do |group|
|
103
|
-
options[:group] = group.to_i == 0 ? Etc.getgrnam(group).gid : group.to_i
|
104
|
-
end
|
105
|
-
|
106
|
-
opts.separator ""; opts.separator "Logging:"
|
107
|
-
|
108
|
-
opts.on("-L", "--log [FILE]", "Path to print debugging information.") do |log_path|
|
109
|
-
options[:logger] = File.expand_path(log_path)
|
110
|
-
end
|
111
|
-
|
112
|
-
opts.on("-v", "Increase logging verbosity (may be used multiple times).") do
|
113
|
-
options[:log_level] -= 1
|
114
|
-
end
|
115
|
-
|
116
|
-
opts.on("-d", "Run as a daemon.") do
|
117
|
-
options[:daemonize] = true
|
118
|
-
end
|
119
|
-
end.parse!
|
120
|
-
end
|
121
|
-
|
122
|
-
def start
|
123
|
-
drop_privileges
|
124
|
-
|
125
|
-
@process.daemonize if options[:daemonize]
|
126
|
-
|
127
|
-
if application_context = options[:application_context]
|
128
|
-
Dir.chdir(application_context)
|
129
|
-
end
|
130
|
-
|
131
|
-
setup_signal_traps
|
132
|
-
@process.write_pid_file
|
133
|
-
|
134
|
-
STDOUT.puts "Starting ChronoTrigger."
|
135
|
-
@chrono_trigger_process = ChronoTrigger::Process.new
|
136
|
-
@chrono_trigger_process.run(options)
|
137
|
-
|
138
|
-
@process.remove_pid_file
|
139
|
-
end
|
140
|
-
|
141
|
-
def drop_privileges
|
142
|
-
::Process.egid = options[:group] if options[:group]
|
143
|
-
::Process.euid = options[:user] if options[:user]
|
144
|
-
end
|
145
|
-
|
146
|
-
def shutdown
|
147
|
-
begin
|
148
|
-
STDOUT.puts "Shutting down."
|
149
|
-
@chrono_trigger_process.stop
|
150
|
-
exit(1)
|
151
|
-
rescue Object => e
|
152
|
-
STDERR.puts "There was an error shutting down: #{e}"
|
153
|
-
exit(70)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def setup_signal_traps
|
158
|
-
Signal.trap("INT") { shutdown }
|
159
|
-
Signal.trap("TERM") { shutdown }
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
class ProcessHelper
|
164
|
-
|
165
|
-
def initialize(log_file = nil, pid_file = nil, user = nil, group = nil)
|
166
|
-
@log_file = log_file
|
167
|
-
@pid_file = pid_file
|
168
|
-
@user = user
|
169
|
-
@group = group
|
170
|
-
end
|
171
|
-
|
172
|
-
def safefork
|
173
|
-
begin
|
174
|
-
if pid = fork
|
175
|
-
return pid
|
176
|
-
end
|
177
|
-
rescue Errno::EWOULDBLOCK
|
178
|
-
sleep 5
|
179
|
-
retry
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
def daemonize
|
184
|
-
sess_id = detach_from_terminal
|
185
|
-
exit if pid = safefork
|
186
|
-
|
187
|
-
Dir.chdir("/")
|
188
|
-
File.umask 0000
|
189
|
-
|
190
|
-
close_io_handles
|
191
|
-
redirect_io
|
192
|
-
|
193
|
-
return sess_id
|
194
|
-
end
|
195
|
-
|
196
|
-
def detach_from_terminal
|
197
|
-
srand
|
198
|
-
safefork and exit
|
199
|
-
|
200
|
-
unless sess_id = ::Process.setsid
|
201
|
-
raise "Couldn't detach from controlling terminal."
|
202
|
-
end
|
203
|
-
|
204
|
-
trap 'SIGHUP', 'IGNORE'
|
205
|
-
|
206
|
-
sess_id
|
207
|
-
end
|
208
|
-
|
209
|
-
def close_io_handles
|
210
|
-
ObjectSpace.each_object(IO) do |io|
|
211
|
-
unless [STDIN, STDOUT, STDERR].include?(io)
|
212
|
-
begin
|
213
|
-
io.close unless io.closed?
|
214
|
-
rescue Exception
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
def redirect_io
|
221
|
-
begin; STDIN.reopen('/dev/null'); rescue Exception; end
|
222
|
-
|
223
|
-
if @log_file
|
224
|
-
begin
|
225
|
-
STDOUT.reopen(@log_file, "a")
|
226
|
-
STDOUT.sync = true
|
227
|
-
rescue Exception
|
228
|
-
begin; STDOUT.reopen('/dev/null'); rescue Exception; end
|
229
|
-
end
|
230
|
-
else
|
231
|
-
begin; STDOUT.reopen('/dev/null'); rescue Exception; end
|
232
|
-
end
|
233
|
-
|
234
|
-
begin; STDERR.reopen(STDOUT); rescue Exception; end
|
235
|
-
STDERR.sync = true
|
236
|
-
end
|
237
|
-
|
238
|
-
def rescue_exception
|
239
|
-
begin
|
240
|
-
yield
|
241
|
-
rescue Exception
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
def write_pid_file
|
246
|
-
return unless @pid_file
|
247
|
-
FileUtils.mkdir_p(File.dirname(@pid_file))
|
248
|
-
File.open(@pid_file, "w") { |f| f.write(::Process.pid) }
|
249
|
-
File.chmod(0644, @pid_file)
|
250
|
-
end
|
251
|
-
|
252
|
-
def remove_pid_file
|
253
|
-
return unless @pid_file
|
254
|
-
File.unlink(@pid_file) if File.exists?(@pid_file)
|
255
|
-
end
|
256
|
-
|
257
|
-
def running?
|
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(0, pid)
|
266
|
-
return pid
|
267
|
-
rescue Errno::ESRCH
|
268
|
-
return nil
|
269
|
-
rescue Errno::EPERM
|
270
|
-
return pid
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
def kill
|
275
|
-
return false unless @pid_file
|
276
|
-
|
277
|
-
pid = File.read(@pid_file).chomp.to_i rescue nil
|
278
|
-
pid = nil if pid == 0
|
279
|
-
return false unless pid
|
280
|
-
|
281
|
-
begin
|
282
|
-
::Process.kill("TERM", pid)
|
283
|
-
remove_pid_file
|
284
|
-
return pid
|
285
|
-
rescue Errno::ESRCH
|
286
|
-
return nil
|
287
|
-
rescue Errno::EPERM
|
288
|
-
return pid
|
289
|
-
end
|
290
|
-
|
291
|
-
end
|
292
|
-
end
|
293
|
-
end
|