KeeperPat-feedupdater 0.2.6
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/CHANGELOG +21 -0
- data/README +34 -0
- data/bin/feed_updater +317 -0
- data/config/feed_updater.yml +22 -0
- data/example/custom_updater.rb +24 -0
- data/lib/feed_updater/vendor/daemons/application.rb +298 -0
- data/lib/feed_updater/vendor/daemons/application_group.rb +150 -0
- data/lib/feed_updater/vendor/daemons/cmdline.rb +106 -0
- data/lib/feed_updater/vendor/daemons/controller.rb +134 -0
- data/lib/feed_updater/vendor/daemons/daemonize.rb +265 -0
- data/lib/feed_updater/vendor/daemons/exceptions.rb +28 -0
- data/lib/feed_updater/vendor/daemons/monitor.rb +126 -0
- data/lib/feed_updater/vendor/daemons/pid.rb +61 -0
- data/lib/feed_updater/vendor/daemons/pidfile.rb +99 -0
- data/lib/feed_updater/vendor/daemons/pidmem.rb +10 -0
- data/lib/feed_updater/vendor/daemons.rb +274 -0
- data/lib/feed_updater/version.rb +9 -0
- data/lib/feed_updater.rb +717 -0
- data/rakefile +120 -0
- metadata +91 -0
@@ -0,0 +1,265 @@
|
|
1
|
+
#--
|
2
|
+
###############################################################################
|
3
|
+
# daemonize.rb is a slightly modified version of daemonize.rb was #
|
4
|
+
# from the Daemonize Library written by Travis Whitton #
|
5
|
+
# for details, read the notice below #
|
6
|
+
###############################################################################
|
7
|
+
#++
|
8
|
+
#
|
9
|
+
#
|
10
|
+
# =Daemonize Library
|
11
|
+
#
|
12
|
+
# February. 4, 2005 Travis Whitton <whitton@atlantic.net>
|
13
|
+
#
|
14
|
+
# Daemonize allows you to easily modify any existing Ruby program to run
|
15
|
+
# as a daemon. See README.rdoc for more details.
|
16
|
+
#
|
17
|
+
# == How to install
|
18
|
+
# 1. su to root
|
19
|
+
# 2. ruby install.rb
|
20
|
+
# build the docs if you want to
|
21
|
+
# 3. rdoc --main README.rdoc daemonize.rb README.rdoc
|
22
|
+
#
|
23
|
+
# == Copying
|
24
|
+
# The Daemonize extension module is copywrited free software by Travis Whitton
|
25
|
+
# <whitton@atlantic.net>. You can redistribute it under the terms specified in
|
26
|
+
# the COPYING file of the Ruby distribution.
|
27
|
+
#
|
28
|
+
# == WARRANTY
|
29
|
+
# THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
30
|
+
# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
31
|
+
# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
|
32
|
+
# PURPOSE.
|
33
|
+
#
|
34
|
+
#
|
35
|
+
# ----
|
36
|
+
#
|
37
|
+
# == Purpose
|
38
|
+
#
|
39
|
+
# Daemonize is a module derived from Perl's Proc::Daemon module. This module
|
40
|
+
# allows you to easily modify any existing Ruby program to run as a daemon.
|
41
|
+
# A daemon is a process that runs in the background with no controlling terminal.
|
42
|
+
# Generally servers (like FTP and HTTP servers) run as daemon processes.
|
43
|
+
# Note, do not make the mistake that a daemon == server. Converting a program
|
44
|
+
# to a daemon by hand is a relatively simple process; however, this module will
|
45
|
+
# save you the effort of repeatedly looking up the procedure, and it will also
|
46
|
+
# insure that your programs are daemonized in the safest and most corrects
|
47
|
+
# fashion possible.
|
48
|
+
#
|
49
|
+
# == Procedure
|
50
|
+
#
|
51
|
+
# The Daemonize module does the following:
|
52
|
+
#
|
53
|
+
# Forks a child and exits the parent process.
|
54
|
+
#
|
55
|
+
# Becomes a session leader (which detaches the program from
|
56
|
+
# the controlling terminal).
|
57
|
+
#
|
58
|
+
# Forks another child process and exits first child. This prevents
|
59
|
+
# the potential of acquiring a controlling terminal.
|
60
|
+
#
|
61
|
+
# Changes the current working directory to "/".
|
62
|
+
#
|
63
|
+
# Clears the file creation mask.
|
64
|
+
#
|
65
|
+
# Closes file descriptors.
|
66
|
+
#
|
67
|
+
# == Example usage
|
68
|
+
#
|
69
|
+
# Using the Daemonize module is extremely simple:
|
70
|
+
#
|
71
|
+
# require 'daemonize'
|
72
|
+
#
|
73
|
+
# class TestDaemon
|
74
|
+
# include Daemonize
|
75
|
+
#
|
76
|
+
# def initialize
|
77
|
+
# daemonize()
|
78
|
+
# loop do
|
79
|
+
# # do some work here
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# == Credits
|
85
|
+
#
|
86
|
+
# Daemonize was written by Travis Whitton and is based on Perl's
|
87
|
+
# Proc::Daemonize, which was written by Earl Hood. The above documentation
|
88
|
+
# is also partially borrowed from the Proc::Daemonize POD documentation.
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
module Daemonize
|
93
|
+
VERSION = "0.1.1m"
|
94
|
+
|
95
|
+
# Try to fork if at all possible retrying every 5 sec if the
|
96
|
+
# maximum process limit for the system has been reached
|
97
|
+
def safefork
|
98
|
+
tryagain = true
|
99
|
+
|
100
|
+
while tryagain
|
101
|
+
tryagain = false
|
102
|
+
begin
|
103
|
+
if pid = fork
|
104
|
+
return pid
|
105
|
+
end
|
106
|
+
rescue Errno::EWOULDBLOCK
|
107
|
+
sleep 5
|
108
|
+
tryagain = true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
module_function :safefork
|
113
|
+
|
114
|
+
|
115
|
+
def simulate(logfile_name = nil)
|
116
|
+
# NOTE: STDOUT and STDERR will not be redirected to the logfile!
|
117
|
+
|
118
|
+
Dir.chdir "/" # Release old working directory
|
119
|
+
File.umask 0000 # Insure sensible umask
|
120
|
+
|
121
|
+
# Make sure all file descriptors are closed
|
122
|
+
ObjectSpace.each_object(IO) do |io|
|
123
|
+
unless [STDIN, STDOUT, STDERR].include?(io)
|
124
|
+
begin
|
125
|
+
unless io.closed?
|
126
|
+
io.close
|
127
|
+
end
|
128
|
+
rescue ::Exception
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Free file descriptors and
|
134
|
+
# point them somewhere sensible
|
135
|
+
# STDOUT/STDERR should go to a logfile
|
136
|
+
|
137
|
+
STDIN.reopen "/dev/null" rescue nil
|
138
|
+
end
|
139
|
+
module_function :simulate
|
140
|
+
|
141
|
+
|
142
|
+
def call_as_daemon(block, logfile_name = nil, oldmode = 0)
|
143
|
+
rd, wr = IO.pipe
|
144
|
+
|
145
|
+
if tmppid = safefork
|
146
|
+
# parent
|
147
|
+
wr.close
|
148
|
+
pid = rd.read.to_i
|
149
|
+
rd.close
|
150
|
+
|
151
|
+
Process.waitpid(tmppid)
|
152
|
+
|
153
|
+
return pid
|
154
|
+
else
|
155
|
+
rd.close
|
156
|
+
|
157
|
+
# Detach from the controlling terminal
|
158
|
+
unless sess_id = Process.setsid
|
159
|
+
raise Daemons.RuntimeException.new('cannot detach from controlling terminal')
|
160
|
+
end
|
161
|
+
|
162
|
+
# Prevent the possibility of acquiring a controlling terminal
|
163
|
+
if oldmode.zero?
|
164
|
+
trap 'SIGHUP', 'IGNORE'
|
165
|
+
exit if pid = safefork
|
166
|
+
end
|
167
|
+
|
168
|
+
wr.write Process.pid
|
169
|
+
wr.close
|
170
|
+
|
171
|
+
Dir.chdir "/" # Release old working directory
|
172
|
+
File.umask 0000 # Insure sensible umask
|
173
|
+
|
174
|
+
# Make sure all file descriptors are closed
|
175
|
+
ObjectSpace.each_object(IO) do |io|
|
176
|
+
unless [STDIN, STDOUT, STDERR].include?(io)
|
177
|
+
begin
|
178
|
+
unless io.closed?
|
179
|
+
io.close
|
180
|
+
end
|
181
|
+
rescue ::Exception
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Free file descriptors and
|
187
|
+
# point them somewhere sensible
|
188
|
+
# STDOUT/STDERR should go to a logfile
|
189
|
+
|
190
|
+
STDIN.reopen "/dev/null" rescue nil
|
191
|
+
|
192
|
+
if logfile_name
|
193
|
+
begin
|
194
|
+
STDOUT.reopen logfile_name, "a"
|
195
|
+
rescue ::Exception
|
196
|
+
STDOUT.reopen "/dev/null" rescue nil
|
197
|
+
end
|
198
|
+
else
|
199
|
+
STDOUT.reopen "/dev/null" rescue nil
|
200
|
+
end
|
201
|
+
|
202
|
+
STDERR.reopen STDOUT rescue nil
|
203
|
+
|
204
|
+
block.call
|
205
|
+
|
206
|
+
exit
|
207
|
+
end
|
208
|
+
end
|
209
|
+
module_function :call_as_daemon
|
210
|
+
|
211
|
+
|
212
|
+
# This method causes the current running process to become a daemon
|
213
|
+
def daemonize(logfile_name = nil, oldmode=0)
|
214
|
+
srand # Split rand streams between spawning and daemonized process
|
215
|
+
safefork and exit # Fork and exit from the parent
|
216
|
+
|
217
|
+
# Detach from the controlling terminal
|
218
|
+
unless sess_id = Process.setsid
|
219
|
+
raise Daemons.RuntimeException.new('cannot detach from controlling terminal')
|
220
|
+
end
|
221
|
+
|
222
|
+
# Prevent the possibility of acquiring a controlling terminal
|
223
|
+
if oldmode.zero?
|
224
|
+
trap 'SIGHUP', 'IGNORE'
|
225
|
+
exit if pid = safefork
|
226
|
+
end
|
227
|
+
|
228
|
+
Dir.chdir "/" # Release old working directory
|
229
|
+
File.umask 0000 # Insure sensible umask
|
230
|
+
|
231
|
+
# Make sure all file descriptors are closed
|
232
|
+
ObjectSpace.each_object(IO) do |io|
|
233
|
+
unless [STDIN, STDOUT, STDERR].include?(io)
|
234
|
+
begin
|
235
|
+
unless io.closed?
|
236
|
+
io.close
|
237
|
+
end
|
238
|
+
rescue ::Exception
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
# Free file descriptors and
|
244
|
+
# point them somewhere sensible
|
245
|
+
# STDOUT/STDERR should go to a logfile
|
246
|
+
|
247
|
+
STDIN.reopen "/dev/null" rescue nil
|
248
|
+
|
249
|
+
if logfile_name
|
250
|
+
begin
|
251
|
+
STDOUT.reopen logfile_name, "a"
|
252
|
+
rescue ::Exception
|
253
|
+
STDOUT.reopen "/dev/null" rescue nil
|
254
|
+
end
|
255
|
+
else
|
256
|
+
STDOUT.reopen "/dev/null" rescue nil
|
257
|
+
end
|
258
|
+
|
259
|
+
STDERR.reopen STDOUT rescue nil
|
260
|
+
|
261
|
+
return oldmode ? sess_id : 0 # Return value is mostly irrelevant
|
262
|
+
end
|
263
|
+
module_function :daemonize
|
264
|
+
|
265
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
module Daemons
|
3
|
+
|
4
|
+
class Exception < ::RuntimeError
|
5
|
+
end
|
6
|
+
|
7
|
+
class RuntimeException < Exception
|
8
|
+
end
|
9
|
+
|
10
|
+
class CmdException < Exception
|
11
|
+
end
|
12
|
+
|
13
|
+
class Error < Exception
|
14
|
+
end
|
15
|
+
|
16
|
+
class SystemError < Error
|
17
|
+
|
18
|
+
attr_reader :system_error
|
19
|
+
|
20
|
+
def initialize(msg, system_error)
|
21
|
+
super(msg)
|
22
|
+
|
23
|
+
@system_error = system_error
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
|
2
|
+
module Daemons
|
3
|
+
|
4
|
+
require 'daemons/daemonize'
|
5
|
+
|
6
|
+
class Monitor
|
7
|
+
|
8
|
+
def self.find(dir, app_name)
|
9
|
+
pid = PidFile.find_files(dir, app_name)[0]
|
10
|
+
|
11
|
+
if pid
|
12
|
+
pid = PidFile.existing(pid)
|
13
|
+
|
14
|
+
unless PidFile.running?(pid.pid)
|
15
|
+
pid.cleanup rescue nil
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
monitor = self.allocate
|
20
|
+
|
21
|
+
monitor.instance_variable_set(:@pid, pid)
|
22
|
+
|
23
|
+
return monitor
|
24
|
+
end
|
25
|
+
|
26
|
+
return nil
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def initialize(an_app)
|
31
|
+
if an_app.pidfile_dir
|
32
|
+
@pid = PidFile.new(an_app.pidfile_dir, an_app.group.app_name + '_monitor', false)
|
33
|
+
else
|
34
|
+
@pid = PidMem.new
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def watch(applications)
|
39
|
+
sleep(30)
|
40
|
+
|
41
|
+
loop do
|
42
|
+
applications.each {|a|
|
43
|
+
sleep(10)
|
44
|
+
|
45
|
+
unless a.running?
|
46
|
+
a.zap!
|
47
|
+
|
48
|
+
Process.detach(fork { a.start })
|
49
|
+
|
50
|
+
sleep(10)
|
51
|
+
end
|
52
|
+
}
|
53
|
+
|
54
|
+
sleep(30)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
private :watch
|
58
|
+
|
59
|
+
|
60
|
+
def start_with_pidfile(applications)
|
61
|
+
fork do
|
62
|
+
Daemonize.daemonize
|
63
|
+
|
64
|
+
begin
|
65
|
+
@pid.pid = Process.pid
|
66
|
+
|
67
|
+
# at_exit {
|
68
|
+
# @pid.cleanup rescue nil
|
69
|
+
# }
|
70
|
+
|
71
|
+
# This part is needed to remove the pid-file if the application is killed by
|
72
|
+
# daemons or manually by the user.
|
73
|
+
# Note that the applications is not supposed to overwrite the signal handler for
|
74
|
+
# 'TERM'.
|
75
|
+
#
|
76
|
+
# trap('TERM') {
|
77
|
+
# @pid.cleanup rescue nil
|
78
|
+
# exit
|
79
|
+
# }
|
80
|
+
|
81
|
+
watch(applications)
|
82
|
+
rescue ::Exception => e
|
83
|
+
begin
|
84
|
+
File.open(File.join(@pid.dir, @pid.progname + '.log'), 'a') {|f|
|
85
|
+
f.puts Time.now
|
86
|
+
f.puts e
|
87
|
+
f.puts e.backtrace.inspect
|
88
|
+
}
|
89
|
+
ensure
|
90
|
+
@pid.cleanup rescue nil
|
91
|
+
exit!
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
private :start_with_pidfile
|
97
|
+
|
98
|
+
def start_without_pidfile(applications)
|
99
|
+
Thread.new { watch(applications) }
|
100
|
+
end
|
101
|
+
private :start_without_pidfile
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
def start(applications)
|
106
|
+
return if applications.empty?
|
107
|
+
|
108
|
+
if @pid.kind_of?(PidFile)
|
109
|
+
start_with_pidfile(applications)
|
110
|
+
else
|
111
|
+
start_without_pidfile(applications)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
def stop
|
117
|
+
Process.kill('TERM', @pid.pid) rescue nil
|
118
|
+
|
119
|
+
# We try to remove the pid-files by ourselves, in case the application
|
120
|
+
# didn't clean it up.
|
121
|
+
@pid.cleanup rescue nil
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
|
2
|
+
module Daemons
|
3
|
+
|
4
|
+
class Pid
|
5
|
+
|
6
|
+
def Pid.running?(pid, additional = nil)
|
7
|
+
output = `ps ax`
|
8
|
+
return (/#{pid} / =~ output and (additional ? /#{additional}/ =~ output : true))
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
# Returns the directory that should be used to write the pid file to
|
13
|
+
# depending on the given mode.
|
14
|
+
#
|
15
|
+
# Some modes may require an additionaly hint, others may determine
|
16
|
+
# the directory automatically.
|
17
|
+
#
|
18
|
+
# If no valid directory is found, returns nil.
|
19
|
+
#
|
20
|
+
def Pid.dir(dir_mode, dir, script)
|
21
|
+
# nil script parameter is allowed so long as dir_mode is not :script
|
22
|
+
return nil if dir_mode == :script && script.nil?
|
23
|
+
|
24
|
+
case dir_mode
|
25
|
+
when :normal
|
26
|
+
return File.expand_path(dir)
|
27
|
+
when :script
|
28
|
+
return File.expand_path(File.join(File.split(script)[0],dir))
|
29
|
+
when :system
|
30
|
+
return '/var/run'
|
31
|
+
else
|
32
|
+
raise Error.new("pid file mode '#{dir_mode}' not implemented")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Initialization method
|
37
|
+
def initialize
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# Get method
|
42
|
+
def pid
|
43
|
+
end
|
44
|
+
|
45
|
+
# Set method
|
46
|
+
def pid=(p)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Cleanup method
|
50
|
+
def cleanup
|
51
|
+
end
|
52
|
+
|
53
|
+
# Exists? method
|
54
|
+
def exists?
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'daemons/pid'
|
2
|
+
|
3
|
+
|
4
|
+
module Daemons
|
5
|
+
|
6
|
+
# === What is a Pid-File?
|
7
|
+
# A <i>Pid-File</i> is a file containing the <i>process identification number</i>
|
8
|
+
# (pid) that is stored in a well-defined location of the filesystem thus allowing other
|
9
|
+
# programs to find out the pid of a running script.
|
10
|
+
#
|
11
|
+
# Daemons needs the pid of the scripts that are currently running in the background
|
12
|
+
# to send them so called _signals_. Daemons uses the +TERM+ signal to tell the script
|
13
|
+
# to exit when you issue a +stop+ command.
|
14
|
+
#
|
15
|
+
# === How does a Pid-File look like?
|
16
|
+
#
|
17
|
+
# Pid-Files generated by Daemons have to following format:
|
18
|
+
# <scriptname>.rb<number>.pid
|
19
|
+
# (Note that <tt><number></tt> is omitted if only one instance of the script can
|
20
|
+
# run at any time)
|
21
|
+
#
|
22
|
+
# Each file just contains one line with the pid as string (for example <tt>6432</tt>).
|
23
|
+
#
|
24
|
+
# === Where are Pid-Files stored?
|
25
|
+
#
|
26
|
+
# Daemons is configurable to store the Pid-Files relative to three different locations:
|
27
|
+
# 1. in a directory relative to the directory where the script (the one that is supposed to run
|
28
|
+
# as a daemon) resides
|
29
|
+
# 2. in a directory relative to the current directory or the filesystem root
|
30
|
+
# 3. in the preconfigured directory <tt>/var/run</tt>
|
31
|
+
#
|
32
|
+
class PidFile < Pid
|
33
|
+
|
34
|
+
attr_reader :dir, :progname, :multiple, :number
|
35
|
+
|
36
|
+
def PidFile.find_files(dir, progname)
|
37
|
+
files = Dir[File.join(dir, "#{progname}*.pid")]
|
38
|
+
|
39
|
+
files.delete_if {|f| not (File.file?(f) and File.readable?(f))}
|
40
|
+
|
41
|
+
return files
|
42
|
+
end
|
43
|
+
|
44
|
+
def PidFile.existing(path)
|
45
|
+
new_instance = PidFile.allocate
|
46
|
+
|
47
|
+
new_instance.instance_variable_set(:@path, path)
|
48
|
+
|
49
|
+
def new_instance.filename
|
50
|
+
return @path
|
51
|
+
end
|
52
|
+
|
53
|
+
return new_instance
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize(dir, progname, multiple = false)
|
57
|
+
@dir = File.expand_path(dir)
|
58
|
+
@progname = progname
|
59
|
+
@multiple = multiple
|
60
|
+
@number = 0 if multiple
|
61
|
+
end
|
62
|
+
|
63
|
+
def filename
|
64
|
+
File.join(@dir, "#{@progname}#{ @number or '' }.pid")
|
65
|
+
end
|
66
|
+
|
67
|
+
def exists?
|
68
|
+
File.exists? filename
|
69
|
+
end
|
70
|
+
|
71
|
+
def pid=(p)
|
72
|
+
if multiple
|
73
|
+
while File.exists?(filename) and @number < 1024
|
74
|
+
@number += 1
|
75
|
+
end
|
76
|
+
|
77
|
+
if @number == 1024
|
78
|
+
raise RuntimeException('cannot run more than 1024 instances of the application')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
File.open(filename, 'w') {|f|
|
83
|
+
f.puts p #Process.pid
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def cleanup
|
88
|
+
File.delete(filename)
|
89
|
+
end
|
90
|
+
|
91
|
+
def pid
|
92
|
+
File.open(filename) {|f|
|
93
|
+
return f.gets.to_i
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|