feedupdater 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,10 @@
1
+ require 'daemons/pid'
2
+
3
+
4
+ module Daemons
5
+
6
+ class PidMem < Pid
7
+ attr_accessor :pid
8
+ end
9
+
10
+ end
@@ -0,0 +1,274 @@
1
+ require 'optparse'
2
+ require 'optparse/time'
3
+
4
+
5
+ require 'daemons/pidfile'
6
+ require 'daemons/cmdline'
7
+ require 'daemons/exceptions'
8
+ require 'daemons/monitor'
9
+
10
+
11
+ require 'daemons/application'
12
+ require 'daemons/application_group'
13
+ require 'daemons/controller'
14
+
15
+
16
+ # All functions and classes that Daemons provides reside in this module.
17
+ #
18
+ # Daemons is normally invoked by one of the following four ways:
19
+ #
20
+ # 1. <tt>Daemons.run(script, options)</tt>:
21
+ # This is used in wrapper-scripts that are supposed to control other ruby scripts or
22
+ # external applications. Control is completely passed to the daemons library.
23
+ # Such wrapper script need to be invoked with command line options like 'start' or 'stop'
24
+ # to do anything useful.
25
+ #
26
+ # 2. <tt>Daemons.run_proc(app_name, options) { (...) }</tt>:
27
+ # This is used in wrapper-scripts that are supposed to control a proc.
28
+ # Control is completely passed to the daemons library.
29
+ # Such wrapper script need to be invoked with command line options like 'start' or 'stop'
30
+ # to do anything useful.
31
+ #
32
+ # 3. <tt>Daemons.call(options) { block }</tt>:
33
+ # Execute the block in a new daemon. <tt>Daemons.call</tt> will return immediately
34
+ # after spawning the daemon with the new Application object as a return value.
35
+ #
36
+ # 4. <tt>Daemons.daemonize(options)</tt>:
37
+ # Daemonize the currently runnig process, i.e. the calling process will become a daemon.
38
+ #
39
+ # == What does daemons internally do with my daemons?
40
+ # *or*:: why do my daemons crash when they try to open a file?
41
+ # *or*:: why can I not see any output from the daemon on the console (when using for example +puts+?
42
+ #
43
+ # From a technical aspect of view, daemons does the following when creating a daemon:
44
+ #
45
+ # 1. Forks a child (and exits the parent process, if needed)
46
+ # 2. Becomes a session leader (which detaches the program from
47
+ # the controlling terminal).
48
+ # 3. Forks another child process and exits first child. This prevents
49
+ # the potential of acquiring a controlling terminal.
50
+ # 4. Changes the current working directory to "/".
51
+ # 5. Clears the file creation mask (sets +umask+ to +0000+).
52
+ # 6. Closes file descriptors (reopens +STDOUT+ and +STDERR+ to point to a logfile if
53
+ # possible).
54
+ #
55
+ # So what does this mean for your daemons:
56
+ # - the current directory is '/'
57
+ # - you cannot receive any input from the console (for example no +gets+)
58
+ # - you cannot output anything from the daemons with +puts+/+print+ unless a logfile is used
59
+ #
60
+ # == How do PidFiles work? Where are they stored?
61
+ #
62
+ # Also, you are maybe interested in reading the documentation for the class PidFile.
63
+ # There you can find out about how Daemons works internally and how and where the so
64
+ # called <i>PidFiles</i> are stored.
65
+ #
66
+ module Daemons
67
+
68
+ VERSION = "0.4.4"
69
+
70
+ require 'daemons/daemonize'
71
+
72
+
73
+ # Passes control to Daemons.
74
+ # This is used in wrapper-scripts that are supposed to control other ruby scripts or
75
+ # external applications. Control is completely passed to the daemons library.
76
+ # Such wrapper script should be invoked with command line options like 'start' or 'stop'
77
+ # to do anything useful.
78
+ #
79
+ # +script+:: This is the path to the script that should be run as a daemon.
80
+ # Please note that Daemons runs this script with <tt>load <script></tt>.
81
+ # Also note that Daemons cannot detect the directory in which the controlling
82
+ # script resides, so this has to be either an absolute path or you have to run
83
+ # the controlling script from the appropriate directory.
84
+ #
85
+ # +options+:: A hash that may contain one or more of the options listed below
86
+ #
87
+ # === Options:
88
+ # <tt>:app_name</tt>:: The name of the application. This will be
89
+ # used to contruct the name of the pid files
90
+ # and log files. Defaults to the basename of
91
+ # the script.
92
+ # <tt>:dir_mode</tt>:: Either <tt>:script</tt> (the directory for writing the pid files to
93
+ # given by <tt>:dir</tt> is interpreted relative
94
+ # to the script location given by +script+) or <tt>:normal</tt> (the directory given by
95
+ # <tt>:dir</tt> is interpreted relative to the current directory) or <tt>:system</tt>
96
+ # (<tt>/var/run</tt> is used as the pid file directory)
97
+ #
98
+ # <tt>:dir</tt>:: Used in combination with <tt>:dir_mode</tt> (description above)
99
+ # <tt>:multiple</tt>:: Specifies whether multiple instances of the same script are allowed to run at the
100
+ # same time
101
+ # <tt>:ontop</tt>:: When given, stay on top, i.e. do not daemonize the application
102
+ # (but the pid-file and other things are written as usual)
103
+ # <tt>:mode</tt>:: <tt>:load</tt> Load the script with <tt>Kernel.load</tt>;
104
+ # <tt>:exec</tt> Execute the script file with <tt>Kernel.exec</tt>
105
+ # <tt>:backtrace</tt>:: Write a backtrace of the last exceptions to the file '[app_name].log' in the
106
+ # pid-file directory if the application exits due to an uncaught exception
107
+ # <tt>:monitor</tt>:: Monitor the programs and restart crashed instances
108
+ # -----
109
+ #
110
+ # === Example:
111
+ # options = {
112
+ # :app_name => "my_app",
113
+ # :dir_mode => :script,
114
+ # :dir => 'pids',
115
+ # :multiple => true,
116
+ # :ontop => true,
117
+ # :mode => :exec,
118
+ # :backtrace => true,
119
+ # :monitor => true,
120
+ # :script => "path/to/script.rb"
121
+ # }
122
+ #
123
+ # Daemons.run(File.join(File.split(__FILE__)[0], 'myscript.rb'), options)
124
+ #
125
+ def run(script, options = {})
126
+ options[:script] = script
127
+ @controller = Controller.new(options, ARGV)
128
+
129
+ @controller.catch_exceptions {
130
+ @controller.run
131
+ }
132
+
133
+ # I don't think anybody will ever use @group, as this location should not be reached under non-error conditions
134
+ @group = @controller.group
135
+ end
136
+ module_function :run
137
+
138
+
139
+ # Passes control to Daemons.
140
+ # This function does the same as Daemons.run except that not a script but a proc
141
+ # will be run as a daemon while this script provides command line options like 'start' or 'stop'
142
+ # and the whole pid-file management to control the proc.
143
+ #
144
+ # +app_name+:: The name of the application. This will be
145
+ # used to contruct the name of the pid files
146
+ # and log files. Defaults to the basename of
147
+ # the script.
148
+ #
149
+ # +options+:: A hash that may contain one or more of the options listed in the documentation for Daemons.run
150
+ #
151
+ # A block must be given to this function. The block will be used as the :proc entry in the options hash.
152
+ # -----
153
+ #
154
+ # === Example:
155
+ #
156
+ # Daemons.run_proc('myproc.rb') do
157
+ # loop do
158
+ # accept_connection()
159
+ # read_request()
160
+ # send_response()
161
+ # close_connection()
162
+ # end
163
+ # end
164
+ #
165
+ def run_proc(app_name, options = {}, &block)
166
+ options[:app_name] = app_name
167
+ options[:mode] = :proc
168
+ options[:proc] = block
169
+
170
+ if [nil, :script].include? options[:dir_mode]
171
+ options[:dir_mode] = :normal
172
+ options[:dir] = File.split(__FILE__)[0]
173
+ end
174
+
175
+ @controller = Controller.new(options, ARGV)
176
+
177
+ @controller.catch_exceptions {
178
+ @controller.run
179
+ }
180
+
181
+ # I don't think anybody will ever use @group, as this location should not be reached under non-error conditions
182
+ @group = @controller.group
183
+ end
184
+ module_function :run_proc
185
+
186
+
187
+ # Execute the block in a new daemon. <tt>Daemons.call</tt> will return immediately
188
+ # after spawning the daemon with the new Application object as a return value.
189
+ #
190
+ # +options+:: A hash that may contain one or more of the options listed below
191
+ #
192
+ # +block+:: The block to call in the daemon.
193
+ #
194
+ # === Options:
195
+ # <tt>:multiple</tt>:: Specifies whether multiple instances of the same script are allowed to run at the
196
+ # same time
197
+ # <tt>:ontop</tt>:: When given, stay on top, i.e. do not daemonize the application
198
+ # <tt>:backtrace</tt>:: Write a backtrace of the last exceptions to the file '[app_name].log' in the
199
+ # pid-file directory if the application exits due to an uncaught exception
200
+ # -----
201
+ #
202
+ # === Example:
203
+ # options = {
204
+ # :backtrace => true,
205
+ # :monitor => true,
206
+ # :ontop => true
207
+ # }
208
+ #
209
+ # Daemons.call(options) begin
210
+ # # Server loop:
211
+ # loop {
212
+ # conn = accept_conn()
213
+ # serve(conn)
214
+ # }
215
+ # end
216
+ #
217
+ def call(options = {}, &block)
218
+ unless block_given?
219
+ raise "Daemons.call: no block given"
220
+ end
221
+
222
+ options[:proc] = block
223
+ options[:mode] = :proc
224
+
225
+ @group ||= ApplicationGroup.new('proc', options)
226
+
227
+ new_app = @group.new_application(options)
228
+ new_app.start
229
+
230
+ return new_app
231
+ end
232
+ module_function :call
233
+
234
+
235
+ # Daemonize the currently runnig process, i.e. the calling process will become a daemon.
236
+ #
237
+ # +options+:: A hash that may contain one or more of the options listed below
238
+ #
239
+ # === Options:
240
+ # <tt>:ontop</tt>:: When given, stay on top, i.e. do not daemonize the application
241
+ # <tt>:backtrace</tt>:: Write a backtrace of the last exceptions to the file '[app_name].log' in the
242
+ # pid-file directory if the application exits due to an uncaught exception
243
+ # -----
244
+ #
245
+ # === Example:
246
+ # options = {
247
+ # :backtrace => true,
248
+ # :ontop => true
249
+ # }
250
+ #
251
+ # Daemons.daemonize(options)
252
+ #
253
+ # # Server loop:
254
+ # loop {
255
+ # conn = accept_conn()
256
+ # serve(conn)
257
+ # }
258
+ #
259
+ def daemonize(options = {})
260
+ @group ||= ApplicationGroup.new('self', options)
261
+
262
+ @group.new_application(:mode => :none).start
263
+
264
+ end
265
+ module_function :daemonize
266
+
267
+ # Return the internal ApplicationGroup instance.
268
+ def group; @group; end
269
+ module_function :group
270
+
271
+ # Return the internal Controller instance.
272
+ def controller; @controller; end
273
+ module_function :controller
274
+ end
@@ -0,0 +1,9 @@
1
+ module FeedTools
2
+ module FEED_UPDATER_VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end