feedupdater 0.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.
data/CHANGELOG ADDED
@@ -0,0 +1,2 @@
1
+ == FeedUpdater 0.1.0
2
+ * initial version
data/README ADDED
@@ -0,0 +1,34 @@
1
+ The FeedUpdater is a simple daemon for handling the automatic updating of
2
+ FeedTools feed objects.
3
+
4
+ USAGE: feed_updater <subcommand>
5
+
6
+ Subcommands:
7
+ start
8
+ stop
9
+ restart
10
+ install
11
+
12
+ The FeedUpdater daemon is very easy to use, especially if you're using it
13
+ with a Rails application. If you're going to be using it with a Rails app,
14
+ simply install the feedupdater gem, navigate to your Rails app, and run
15
+ the 'feed_updater install' command. This will install FeedUpdater. Then
16
+ edit the feed_updater.yml config file as necessary. You will probably need
17
+ to define a custom updater class, which should probably go in your lib
18
+ directory. It's recommended that you use the example/custom_updater.rb file
19
+ as a starting point. You should using the updater events to copy any
20
+ required fields to feed subscription tables or item tables as necessary so
21
+ that a full feed parse isn't required every time the feed is displayed.
22
+
23
+ The config file also has a start delay option. This should almost always be
24
+ set to true. You should only turn it off when you are debugging a custom
25
+ updater class, and it should probably never be turned off in production.
26
+ Keeping it on prevents FeedUpdater from hitting the CPU too hard immediately
27
+ after a server reboot, and also prevents hundreds of update processes from
28
+ kicking off simultaneously on a shared server.
29
+
30
+ If you chose to use FeedUpdater outside a Rails application, you should try
31
+ to use a similar directory structure to what would be found in a Rails
32
+ setup. FeedUpdater will usually look in the current directory if it cannot
33
+ find a file its looking for though, so if FeedUpdater can't find a file it
34
+ needs, changing directories will probably fix the problem.
data/bin/feed_updater ADDED
@@ -0,0 +1,309 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ #--
4
+ # Copyright (c) 2005 Robert Aman
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #++
25
+
26
+ if RUBY_PLATFORM =~ /mswin/
27
+ puts "FeedUpdater is not supported on Windows due to a lack of fork()."
28
+ exit
29
+ end
30
+
31
+ # :stopdoc:
32
+ # ROFLCOPTERS?
33
+ # This mainly exists because of the approximately 1 billion different
34
+ # ways to deploy this script.
35
+ class FileStalker
36
+ def self.hunt(paths)
37
+ for path in paths
38
+ if File.exists?(File.expand_path(path))
39
+ return File.expand_path(path)
40
+ elsif File.exists?(File.expand_path(
41
+ File.dirname(__FILE__) + path))
42
+ return File.expand_path(File.dirname(__FILE__) + path)
43
+ elsif File.exists?(File.expand_path(
44
+ File.dirname(__FILE__) + "/" + path))
45
+ return File.expand_path(File.dirname(__FILE__) + "/" + path)
46
+ end
47
+ end
48
+ return nil
49
+ end
50
+ end
51
+ # :startdoc:
52
+
53
+ $:.unshift(FileStalker.hunt([
54
+ "/../vendor/feedupdater/lib",
55
+ "vendor/feedupdater/lib",
56
+ "../lib"]))
57
+ $:.uniq!
58
+
59
+ require 'yaml'
60
+ require 'feed_updater'
61
+
62
+ if ARGV[0].nil?
63
+ # Exit quickly if no command line arguments given.
64
+
65
+ feed_updater_version = FeedTools::FEED_UPDATER_VERSION::STRING
66
+ feed_tools_version = "<error: not loaded>"
67
+ if defined?(FeedTools::FEED_TOOLS_VERSION::STRING)
68
+ feed_tools_version = FeedTools::FEED_TOOLS_VERSION::STRING
69
+ elsif defined?(FEED_TOOLS_VERSION)
70
+ feed_tools_version = FEED_TOOLS_VERSION
71
+ end
72
+ puts ""
73
+ puts "FeedUpdater #{feed_updater_version} / FeedTools #{feed_tools_version}"
74
+ puts "USAGE: feed_updater <subcommand>"
75
+ puts ""
76
+ puts "Subcommands:"
77
+ puts " start"
78
+ puts " This will start the daemon if it hasn't already been started."
79
+ puts ""
80
+ puts " stop"
81
+ puts " This will stop the daemon if it's already running."
82
+ puts ""
83
+ puts " restart"
84
+ puts " This will restart the daemon."
85
+ puts ""
86
+ puts " install"
87
+ puts " If the current directory is a Rails installation, this will"
88
+ puts " install FeedUpdater into the vendor folder and add a new script"
89
+ puts " to the scripts/ directory for running FeedUpdater. Rerunning"
90
+ puts " the install subcommand will update the files against the latest"
91
+ puts " version of the FeedUpdater gem file you have installed. Rails"
92
+ puts " is NOT required to use FeedUpdater."
93
+ puts ""
94
+ exit
95
+ elsif ARGV[0] == "install"
96
+ rails_root = nil
97
+ install_vendor_directory = nil
98
+ install_config_directory = nil
99
+ install_script_directory = nil
100
+
101
+ current_directory = File.expand_path(".")
102
+ loop do
103
+ entries = Dir.entries(current_directory)
104
+ if entries.include?("vendor") &&
105
+ entries.include?("config") &&
106
+ entries.include?("script")
107
+ rails_root = current_directory
108
+ break
109
+ else
110
+ break if current_directory == "/"
111
+ current_directory = File.expand_path(current_directory + "/..")
112
+ end
113
+ end
114
+ if rails_root.nil?
115
+ puts "You must run the install command when the current directory is"
116
+ puts "a Rails application."
117
+ else
118
+ install_vendor_directory = File.expand_path(rails_root + "/vendor")
119
+ install_config_directory = File.expand_path(rails_root + "/config")
120
+ install_script_directory = File.expand_path(rails_root + "/script")
121
+
122
+ # Install FeedUpdater gem to application vendor directory
123
+ system("rm -rf #{install_vendor_directory}/feedupdater")
124
+ unpack_string = `gem unpack feedupdater`
125
+ feed_updater_version =
126
+ unpack_string.scan(/\'feedupdater\-(.*)\'/).flatten[0]
127
+ if feed_updater_version.nil? || feed_updater_version == ""
128
+ puts "You need to have the feedupdater gem installed for the install"
129
+ puts "subcommand to run correctly."
130
+ exit
131
+ end
132
+ puts "Installing FeedUpdater #{feed_updater_version} " +
133
+ "to vendor directory..."
134
+
135
+ system("cp -rp feedupdater-#{feed_updater_version} " +
136
+ "#{install_vendor_directory}")
137
+ system("rm -rf feedupdater-#{feed_updater_version}")
138
+ system("mv #{install_vendor_directory}/" +
139
+ "feedupdater-#{feed_updater_version} " +
140
+ "#{install_vendor_directory}/feedupdater")
141
+
142
+ # Install FeedTools gem to application vendor directory
143
+ system("rm -rf #{install_vendor_directory}/feedtools")
144
+ unpack_string = `gem unpack feedtools`
145
+ feed_tools_version =
146
+ unpack_string.scan(/\'feedtools\-(.*)\'/).flatten[0]
147
+ if feed_tools_version.nil? || feed_tools_version == ""
148
+ puts "You need to have the feedtools gem installed for the install"
149
+ puts "subcommand to run correctly."
150
+ exit
151
+ end
152
+ puts "Installing FeedTools #{feed_tools_version} to vendor directory..."
153
+
154
+ system("cp -rp feedtools-#{feed_tools_version} " +
155
+ "#{install_vendor_directory}")
156
+ system("rm -rf feedtools-#{feed_tools_version}")
157
+ system("mv #{install_vendor_directory}/" +
158
+ "feedtools-#{feed_tools_version} " +
159
+ "#{install_vendor_directory}/feedtools")
160
+
161
+ # Copying the default config file to the config directory
162
+ if !File.exists?(File.expand_path(install_config_directory +
163
+ "/feed_updater.yml"))
164
+ puts "Installing default config file..."
165
+ config_file =
166
+ File.expand_path(File.dirname(__FILE__) + '/../config/feed_updater.yml')
167
+ system("cp -p #{config_file} #{install_config_directory}")
168
+ else
169
+ puts "Config file already exists, skipping..."
170
+ end
171
+
172
+ # Copying this executable to the script directory
173
+ puts "Copying executable to script directory..."
174
+ system("rm -rf #{install_script_directory}/feed_updater")
175
+ system("cp -p #{__FILE__} #{install_script_directory}")
176
+ system("chmod +x #{install_script_directory}/feed_updater")
177
+
178
+ puts "Use script/feed_updater to run FeedUpdater."
179
+ end
180
+ exit
181
+ end
182
+
183
+ config_file = FileStalker.hunt([
184
+ "./feed_updater.yml",
185
+ "../config/feed_updater.yml"
186
+ ])
187
+ if config_file.nil?
188
+ puts "Could not locate feed_updater.yml config file."
189
+ exit
190
+ end
191
+
192
+ config_hash = YAML.load(File.open(config_file, "r").read)
193
+ load_script_path = config_hash["load_script"]
194
+ if !load_script_path.nil?
195
+ result = FileStalker.hunt([
196
+ load_script_path,
197
+ File.dirname(config_file) + "/" + load_script_path,
198
+ File.dirname(config_file) + load_script_path,
199
+ File.dirname(__FILE__) + "/../" + load_script_path,
200
+ File.dirname(__FILE__) + "/.." + load_script_path
201
+ ])
202
+ if result.nil?
203
+ puts "Could not locate #{load_script_path.inspect} file."
204
+ exit
205
+ else
206
+ load_script_path = result
207
+ end
208
+ end
209
+
210
+ pid_file_path = config_hash["pid_file_path"]
211
+ if !pid_file_path.nil?
212
+ result = FileStalker.hunt([
213
+ pid_file_path,
214
+ File.dirname(config_file) + "/" + pid_file_path,
215
+ File.dirname(config_file) + pid_file_path,
216
+ File.dirname(__FILE__) + "/../" + pid_file_path,
217
+ File.dirname(__FILE__) + "/.." + pid_file_path,
218
+ "../" + pid_file_path
219
+ ])
220
+ pid_file_path = result if !result.nil?
221
+ else
222
+ result = FileStalker.hunt([
223
+ File.dirname(__FILE__) + "/../config",
224
+ "./config",
225
+ File.dirname(__FILE__) + "/config"
226
+ ])
227
+ pid_file_path = result if !result.nil?
228
+ end
229
+
230
+ log_file_path = config_hash["log_file_path"]
231
+ if !log_file_path.nil?
232
+ result = FileStalker.hunt([
233
+ log_file_path,
234
+ File.dirname(config_file) + "/" + log_file_path,
235
+ File.dirname(config_file) + log_file_path,
236
+ File.dirname(__FILE__) + "/../" + log_file_path,
237
+ File.dirname(__FILE__) + "/.." + log_file_path,
238
+ "../" + log_file_path
239
+ ])
240
+ log_file_path = result if !result.nil?
241
+ else
242
+ result = FileStalker.hunt([
243
+ File.dirname(__FILE__) + "/../log",
244
+ "./log",
245
+ File.dirname(__FILE__) + "/log",
246
+ File.dirname(__FILE__) + "/../logs",
247
+ "./logs",
248
+ File.dirname(__FILE__) + "/logs"
249
+ ])
250
+ log_file_path = result if !result.nil?
251
+ end
252
+
253
+ script_class = nil
254
+ existing_subclasses = []
255
+ ObjectSpace.each_object do |instance|
256
+ if instance.class.name == "Class" &&
257
+ instance.ancestors.include?(FeedTools::FeedUpdater)
258
+ existing_subclasses << instance
259
+ end
260
+ end
261
+ load(load_script_path)
262
+ loaded_subclasses = []
263
+ ObjectSpace.each_object do |instance|
264
+ if instance.class.name == "Class" &&
265
+ instance.ancestors.include?(FeedTools::FeedUpdater)
266
+ loaded_subclasses << instance
267
+ end
268
+ end
269
+ script_classes = (loaded_subclasses - existing_subclasses)
270
+ if script_classes.size == 0
271
+ puts "There do not appear to be any subclasses of FeedTools::FeedUpdater."
272
+ exit
273
+ elsif script_classes.size > 1
274
+ puts "There appear to be multiple subclasses of FeedTools::FeedUpdater."
275
+ exit
276
+ else
277
+ script_class = script_classes[0]
278
+ end
279
+
280
+ updater = nil
281
+ if load_script_path.nil?
282
+ updater = FeedTools::FeedUpdater.new
283
+ FeedTools::FeedUpdater.on_update do |feed, seconds|
284
+ updater.logger.info("Loaded '#{feed.href}'.")
285
+ updater.logger.info("=> Updated (#{feed.title}) in #{seconds} seconds.")
286
+ end
287
+ FeedTools::FeedUpdater.on_error do |href, error|
288
+ updater.logger.info("Error updating '#{href}':")
289
+ updater.logger.info(error)
290
+ end
291
+ else
292
+ updater = script_class.new
293
+ end
294
+
295
+ for key in config_hash.keys
296
+ key_sym = key.to_s.to_sym
297
+ updater.updater_options[key_sym] = config_hash[key]
298
+ end
299
+ updater.pid_file_dir = pid_file_path
300
+ updater.log_file_dir = log_file_path
301
+
302
+ case ARGV[0]
303
+ when "start"
304
+ updater.start()
305
+ when "stop"
306
+ updater.stop()
307
+ when "restart"
308
+ updater.restart()
309
+ end
@@ -0,0 +1 @@
1
+ 617
@@ -0,0 +1,12 @@
1
+ # This ensures that you don't break shared servers.
2
+ start_delay: false
3
+
4
+ # This is the path to the custom handler for updated feeds.
5
+ # See the documentation for details.
6
+ load_script: example/custom_updater.rb
7
+
8
+ # This is the path to store the pid files.
9
+ pid_file_path: config
10
+
11
+ # This is the path to store the log files.
12
+ log_file_path: log
@@ -0,0 +1,14 @@
1
+ class CustomUpdater < FeedTools::FeedUpdater
2
+ on_update do |feed, seconds|
3
+ logger.info("Loaded '#{feed.href}'.")
4
+ logger.info("=> Updated (#{feed.title}) in #{seconds} seconds.")
5
+ end
6
+
7
+ on_error do |href, error|
8
+ logger.info("Error updating '#{href}':")
9
+ logger.info(error)
10
+ end
11
+
12
+ on_complete do |updated_feed_hrefs|
13
+ end
14
+ end
@@ -0,0 +1,298 @@
1
+ require 'daemons/pidfile'
2
+ require 'daemons/pidmem'
3
+
4
+
5
+ module Daemons
6
+
7
+ class Application
8
+
9
+ attr_accessor :app_argv
10
+ attr_accessor :controller_argv
11
+
12
+ # the Pid instance belonging to this application
13
+ attr_reader :pid
14
+
15
+ # the ApplicationGroup the application belongs to
16
+ attr_reader :group
17
+
18
+ # my private options
19
+ attr_reader :options
20
+
21
+
22
+ def initialize(group, add_options = {}, pid = nil)
23
+ @group = group
24
+ @options = group.options.dup
25
+ @options.update(add_options)
26
+
27
+ unless @pid = pid
28
+ if dir = pidfile_dir
29
+ @pid = PidFile.new(pidfile_dir(), @group.app_name, @group.multiple)
30
+ else
31
+ @pid = PidMem.new
32
+ end
33
+ end
34
+ end
35
+
36
+ def script
37
+ @script || @group.script
38
+ end
39
+
40
+ def pidfile_dir
41
+ Pid.dir(@dir_mode || @group.dir_mode, @dir || @group.dir, @script || @group.script)
42
+ end
43
+
44
+ def logfile
45
+ (options[:log_output] && pidfile_dir()) ? File.join(pidfile_dir(), @group.app_name + '.output') : nil
46
+ end
47
+
48
+ def start_none
49
+ unless options[:ontop]
50
+ Daemonize.daemonize #(logfile)
51
+ else
52
+ Daemonize.simulate
53
+ end
54
+
55
+ @pid.pid = Process.pid
56
+
57
+
58
+ # We need this to remove the pid-file if the applications exits by itself.
59
+ # Note that <tt>at_text</tt> will only be run if the applications exits by calling
60
+ # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt>
61
+ # in your application!
62
+ #
63
+ at_exit {
64
+ @pid.cleanup rescue nil
65
+
66
+ # If the option <tt>:backtrace</tt> is used and the application did exit by itself
67
+ # create a exception log.
68
+ if options[:backtrace] and not options[:ontop] and not $daemons_sigterm
69
+ exception_log() rescue nil
70
+ end
71
+
72
+ }
73
+
74
+ # This part is needed to remove the pid-file if the application is killed by
75
+ # daemons or manually by the user.
76
+ # Note that the applications is not supposed to overwrite the signal handler for
77
+ # 'TERM'.
78
+ #
79
+ trap('TERM') {
80
+ @pid.cleanup rescue nil
81
+ $daemons_sigterm = true
82
+
83
+ exit
84
+ }
85
+ end
86
+
87
+ def start_exec
88
+ unless options[:ontop]
89
+ Daemonize.daemonize(logfile)
90
+ else
91
+ Daemonize.simulate(logfile)
92
+ end
93
+
94
+ @pid.pid = Process.pid
95
+
96
+ ENV['DAEMONS_ARGV'] = @controller_argv.join(' ')
97
+ # haven't tested yet if this is really passed to the exec'd process...
98
+
99
+ Kernel.exec(script(), *ARGV)
100
+ end
101
+
102
+ def start_load
103
+ unless options[:ontop]
104
+ Daemonize.daemonize(logfile)
105
+ else
106
+ Daemonize.simulate(logfile)
107
+ end
108
+
109
+ @pid.pid = Process.pid
110
+
111
+
112
+ # We need this to remove the pid-file if the applications exits by itself.
113
+ # Note that <tt>at_text</tt> will only be run if the applications exits by calling
114
+ # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt>
115
+ # in your application!
116
+ #
117
+ at_exit {
118
+ @pid.cleanup rescue nil
119
+
120
+ # If the option <tt>:backtrace</tt> is used and the application did exit by itself
121
+ # create a exception log.
122
+ if options[:backtrace] and not options[:ontop] and not $daemons_sigterm
123
+ exception_log() rescue nil
124
+ end
125
+
126
+ }
127
+
128
+ # This part is needed to remove the pid-file if the application is killed by
129
+ # daemons or manually by the user.
130
+ # Note that the applications is not supposed to overwrite the signal handler for
131
+ # 'TERM'.
132
+ #
133
+ trap('TERM') {
134
+ @pid.cleanup rescue nil
135
+ $daemons_sigterm = true
136
+
137
+ exit
138
+ }
139
+
140
+ # Know we really start the script...
141
+ $DAEMONS_ARGV = @controller_argv
142
+ ENV['DAEMONS_ARGV'] = @controller_argv.join(' ')
143
+
144
+ ARGV.clear
145
+ ARGV.concat @app_argv if @app_argv
146
+
147
+ # TODO: begin - rescue - end around this and exception logging
148
+ load script()
149
+ end
150
+
151
+ def start_proc
152
+ return unless options[:proc]
153
+
154
+ unless options[:ontop]
155
+ @pid.pid = Daemonize.call_as_daemon(options[:proc], logfile)
156
+ else
157
+ # Daemonize.simulate(logfile)
158
+ #
159
+ # @pid.pid = Process.pid
160
+ #
161
+ # Thread.new(&options[:proc])
162
+ unless @pid.pid = Process.fork
163
+ Daemonize.simulate(logfile)
164
+ options[:proc].call
165
+ exit
166
+ else
167
+ Process.detach(@pid.pid)
168
+ end
169
+ end
170
+ end
171
+
172
+
173
+ def start
174
+ @group.create_monitor(@group.applications[0] || self)
175
+
176
+ case options[:mode]
177
+ when :none
178
+ start_none
179
+ when :exec
180
+ start_exec
181
+ when :load
182
+ start_load
183
+ when :proc
184
+ start_proc
185
+ else
186
+ start_load
187
+ end
188
+ end
189
+
190
+ # def run
191
+ # if @group.controller.options[:exec]
192
+ # run_via_exec()
193
+ # else
194
+ # run_via_load()
195
+ # end
196
+ # end
197
+ #
198
+ # def run_via_exec
199
+ #
200
+ # end
201
+ #
202
+ # def run_via_load
203
+ #
204
+ # end
205
+
206
+
207
+ # This is a nice little function for debugging purposes:
208
+ # In case a multi-threaded ruby script exits due to an uncaught exception
209
+ # it may be difficult to find out where the exception came from because
210
+ # one cannot catch exceptions that are thrown in threads other than the main
211
+ # thread.
212
+ #
213
+ # This function searches for all exceptions in memory and outputs them to STDERR
214
+ # (if it is connected) and to a log file in the pid-file directory.
215
+ #
216
+ def exception_log
217
+ require 'logger'
218
+
219
+ l_file = Logger.new(File.join(pidfile_dir(), @group.app_name + '.log'))
220
+
221
+
222
+ # the code below only logs the last exception
223
+ # e = nil
224
+ #
225
+ # ObjectSpace.each_object {|o|
226
+ # if ::Exception === o
227
+ # e = o
228
+ # end
229
+ # }
230
+ #
231
+ # l_file.error e
232
+ # l_file.close
233
+
234
+ # this code logs every exception found in memory
235
+ ObjectSpace.each_object {|o|
236
+ if ::Exception === o
237
+ l_file.error o
238
+ end
239
+ }
240
+
241
+ l_file.close
242
+ end
243
+
244
+
245
+ def stop
246
+ if options[:force] and not running?
247
+ self.zap
248
+ return
249
+ end
250
+
251
+ # Catch errors when trying to kill a process that doesn't
252
+ # exist. This happens when the process quits and hasn't been
253
+ # restarted by the monitor yet. By catching the error, we allow the
254
+ # pid file clean-up to occur.
255
+ begin
256
+ Process.kill('TERM', @pid.pid)
257
+ rescue Errno::ESRCH => e
258
+ puts "#{e} #{@pid.pid}"
259
+ puts "deleting pid-file."
260
+ end
261
+
262
+ # We try to remove the pid-files by ourselves, in case the application
263
+ # didn't clean it up.
264
+ @pid.cleanup rescue nil
265
+
266
+ end
267
+
268
+ def zap
269
+ @pid.cleanup
270
+ end
271
+
272
+ def zap!
273
+ @pid.cleanup rescue nil
274
+ end
275
+
276
+ def show_status
277
+ running = self.running?
278
+
279
+ puts "#{self.group.app_name}: #{running ? '' : 'not '}running#{(running and @pid.exists?) ? ' [pid ' + @pid.pid.to_s + ']' : ''}#{(@pid.exists? and not running) ? ' (but pid-file exists: ' + @pid.pid.to_s + ')' : ''}"
280
+ end
281
+
282
+ # This function implements a (probably too simle) method to detect
283
+ # whether the program with the pid found in the pid-file is still running.
284
+ # It just searches for the pid in the output of <tt>ps ax</tt>, which
285
+ # is probably not a good idea in some cases.
286
+ # Alternatives would be to use a direct access method the unix process control
287
+ # system.
288
+ #
289
+ def running?
290
+ if @pid.exists?
291
+ return Pid.running?(@pid.pid)
292
+ end
293
+
294
+ return false
295
+ end
296
+ end
297
+
298
+ end