ghazel-daemons 1.0.11

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.
@@ -0,0 +1,152 @@
1
+
2
+ module Daemons
3
+ class ApplicationGroup
4
+
5
+ attr_reader :app_name
6
+ attr_reader :script
7
+
8
+ attr_reader :monitor
9
+
10
+ #attr_reader :controller
11
+
12
+ attr_reader :options
13
+
14
+ attr_reader :applications
15
+
16
+ attr_accessor :controller_argv
17
+ attr_accessor :app_argv
18
+
19
+ attr_accessor :dir_mode
20
+ attr_accessor :dir
21
+
22
+ # true if the application is supposed to run in multiple instances
23
+ attr_reader :multiple
24
+
25
+
26
+ def initialize(app_name, options = {})
27
+ @app_name = app_name
28
+ @options = options
29
+
30
+ if options[:script]
31
+ @script = File.expand_path(options[:script])
32
+ end
33
+
34
+ #@controller = controller
35
+ @monitor = nil
36
+
37
+ #options = controller.options
38
+
39
+ @multiple = options[:multiple] || false
40
+
41
+ @dir_mode = options[:dir_mode] || :script
42
+ @dir = options[:dir] || ''
43
+
44
+ @keep_pid_files = options[:keep_pid_files] || false
45
+
46
+ #@applications = find_applications(pidfile_dir())
47
+ @applications = []
48
+ end
49
+
50
+ # Setup the application group.
51
+ # Currently this functions calls <tt>find_applications</tt> which finds
52
+ # all running instances of the application and populates the application array.
53
+ #
54
+ def setup
55
+ @applications = find_applications(pidfile_dir())
56
+ end
57
+
58
+ def pidfile_dir
59
+ PidFile.dir(@dir_mode, @dir, script)
60
+ end
61
+
62
+ def find_applications(dir)
63
+ pid_files = PidFile.find_files(dir, app_name, ! @keep_pid_files)
64
+
65
+ #pp pid_files
66
+
67
+ @monitor = Monitor.find(dir, app_name + '_monitor')
68
+
69
+ pid_files.reject! {|f| f =~ /_monitor.pid$/}
70
+
71
+ return pid_files.map {|f|
72
+ app = Application.new(self, {}, PidFile.existing(f))
73
+ setup_app(app)
74
+ app
75
+ }
76
+ end
77
+
78
+ def new_application(add_options = {})
79
+ if @applications.size > 0 and not @multiple
80
+ if options[:force]
81
+ @applications.delete_if {|a|
82
+ unless a.running?
83
+ a.zap
84
+ true
85
+ end
86
+ }
87
+ end
88
+
89
+ raise RuntimeException.new('there is already one or more instance(s) of the program running') unless @applications.empty?
90
+ end
91
+
92
+ app = Application.new(self, add_options)
93
+
94
+ setup_app(app)
95
+
96
+ @applications << app
97
+
98
+ return app
99
+ end
100
+
101
+ def setup_app(app)
102
+ app.controller_argv = @controller_argv
103
+ app.app_argv = @app_argv
104
+ end
105
+ private :setup_app
106
+
107
+ def create_monitor(an_app)
108
+ return if @monitor
109
+
110
+ if options[:monitor]
111
+ @monitor = Monitor.new(an_app)
112
+
113
+ @monitor.start(@applications)
114
+ end
115
+ end
116
+
117
+ def start_all
118
+ @monitor.stop if @monitor
119
+ @monitor = nil
120
+
121
+ @applications.each {|a|
122
+ fork {
123
+ a.start
124
+ }
125
+ }
126
+ end
127
+
128
+ def stop_all(force = false)
129
+ @monitor.stop if @monitor
130
+
131
+ @applications.each {|a|
132
+ if force
133
+ begin; a.stop; rescue ::Exception; end
134
+ else
135
+ a.stop
136
+ end
137
+ }
138
+ end
139
+
140
+ def zap_all
141
+ @monitor.stop if @monitor
142
+
143
+ @applications.each {|a| a.zap}
144
+ end
145
+
146
+ def show_status
147
+ @applications.each {|a| a.show_status}
148
+ end
149
+
150
+ end
151
+
152
+ end
@@ -0,0 +1,117 @@
1
+
2
+ module Daemons
3
+
4
+ class Optparse
5
+
6
+ attr_reader :usage
7
+
8
+ def initialize(controller)
9
+ @controller = controller
10
+ @options = {}
11
+
12
+ @opts = OptionParser.new do |opts|
13
+ #opts.banner = "Usage: example.rb [options]"
14
+ opts.banner = ""
15
+
16
+ # Boolean switch.
17
+ # opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
18
+ # @options[:verbose] = v
19
+ # end
20
+
21
+ opts.on("-t", "--ontop", "Stay on top (does not daemonize)") do |t|
22
+ @options[:ontop] = t
23
+ end
24
+
25
+ opts.on("-f", "--force", "Force operation") do |t|
26
+ @options[:force] = t
27
+ end
28
+
29
+ #opts.separator ""
30
+ #opts.separator "Specific options:"
31
+
32
+
33
+ opts.separator ""
34
+ opts.separator "Common options:"
35
+
36
+ # No argument, shows at tail. This will print an options summary.
37
+ # Try it and see!
38
+ opts.on_tail("-h", "--help", "Show this message") do
39
+ #puts opts
40
+ #@usage =
41
+ controller.print_usage()
42
+
43
+ exit
44
+ end
45
+
46
+ # Another typical switch to print the version.
47
+ opts.on_tail("--version", "Show version") do
48
+ puts "daemons version #{Daemons::VERSION}"
49
+ exit
50
+ end
51
+ end
52
+
53
+ begin
54
+ @usage = @opts.to_s
55
+ rescue ::Exception # work around a bug in ruby 1.9
56
+ @usage = <<END
57
+ -t, --ontop Stay on top (does not daemonize)
58
+ -f, --force Force operation
59
+
60
+ Common options:
61
+ -h, --help Show this message
62
+ --version Show version
63
+ END
64
+ end
65
+ end
66
+
67
+
68
+ #
69
+ # Return a hash describing the options.
70
+ #
71
+ def parse(args)
72
+ # The options specified on the command line will be collected in *options*.
73
+ # We set default values here.
74
+ #options = {}
75
+
76
+
77
+ ##pp args
78
+ @opts.parse(args)
79
+
80
+ return @options
81
+ end
82
+
83
+ end
84
+
85
+
86
+ class Controller
87
+
88
+ def print_usage
89
+ puts "Usage: #{@app_name} <command> <options> -- <application options>"
90
+ puts
91
+ puts "* where <command> is one of:"
92
+ puts " start start an instance of the application"
93
+ puts " stop stop all instances of the application"
94
+ puts " restart stop all instances and restart them afterwards"
95
+ puts " run start the application and stay on top"
96
+ puts " zap set the application to a stopped state"
97
+ puts
98
+ puts "* and where <options> may contain several of the following:"
99
+
100
+ puts @optparse.usage
101
+ end
102
+
103
+ def catch_exceptions(&block)
104
+ begin
105
+ block.call
106
+ rescue CmdException, OptionParser::ParseError => e
107
+ puts "ERROR: #{e.to_s}"
108
+ puts
109
+ print_usage()
110
+ rescue RuntimeException => e
111
+ puts "ERROR: #{e.to_s}"
112
+ end
113
+ end
114
+
115
+ end
116
+
117
+ end
@@ -0,0 +1,134 @@
1
+
2
+ module Daemons
3
+ class Controller
4
+
5
+ attr_reader :app_name
6
+
7
+ attr_reader :group
8
+
9
+ attr_reader :options
10
+
11
+
12
+ COMMANDS = [
13
+ 'start',
14
+ 'stop',
15
+ 'restart',
16
+ 'run',
17
+ 'zap',
18
+ 'status'
19
+ ]
20
+
21
+ def initialize(options = {}, argv = [])
22
+ @options = options
23
+ @argv = argv
24
+
25
+ # Allow an app_name to be specified. If not specified use the
26
+ # basename of the script.
27
+ @app_name = options[:app_name]
28
+
29
+ if options[:script]
30
+ @script = File.expand_path(options[:script])
31
+
32
+ @app_name ||= File.split(@script)[1]
33
+ end
34
+
35
+ @app_name ||= 'unknown_application'
36
+
37
+ @command, @controller_part, @app_part = Controller.split_argv(argv)
38
+
39
+ #@options[:dir_mode] ||= :script
40
+
41
+ @optparse = Optparse.new(self)
42
+ end
43
+
44
+
45
+ # This function is used to do a final update of the options passed to the application
46
+ # before they are really used.
47
+ #
48
+ # Note that this function should only update <tt>@options</tt> and no other variables.
49
+ #
50
+ def setup_options
51
+ #@options[:ontop] ||= true
52
+ end
53
+
54
+ def run
55
+ @options.update @optparse.parse(@controller_part).delete_if {|k,v| !v}
56
+
57
+ setup_options()
58
+
59
+ #pp @options
60
+
61
+ @group = ApplicationGroup.new(@app_name, @options)
62
+ @group.controller_argv = @controller_part
63
+ @group.app_argv = @app_part
64
+
65
+ @group.setup
66
+
67
+ case @command
68
+ when 'start'
69
+ @group.new_application.start
70
+ when 'run'
71
+ @options[:ontop] ||= true
72
+ @group.new_application.start
73
+ when 'stop'
74
+ @group.stop_all
75
+ when 'restart'
76
+ unless @group.applications.empty?
77
+ @group.stop_all
78
+ sleep 1
79
+ @group.start_all
80
+ end
81
+ when 'zap'
82
+ @group.zap_all
83
+ when 'status'
84
+ unless @group.applications.empty?
85
+ @group.show_status
86
+ else
87
+ puts "#{@group.app_name}: no instances running"
88
+ end
89
+ when nil
90
+ raise CmdException.new('no command given')
91
+ #puts "ERROR: No command given"; puts
92
+
93
+ #print_usage()
94
+ #raise('usage function not implemented')
95
+ else
96
+ raise Error.new("command '#{@command}' not implemented")
97
+ end
98
+ end
99
+
100
+
101
+ # Split an _argv_ array.
102
+ # +argv+ is assumed to be in the following format:
103
+ # ['command', 'controller option 1', 'controller option 2', ..., '--', 'app option 1', ...]
104
+ #
105
+ # <tt>command</tt> must be one of the commands listed in <tt>COMMANDS</tt>
106
+ #
107
+ # *Returns*: the command as a string, the controller options as an array, the appliation options
108
+ # as an array
109
+ #
110
+ def Controller.split_argv(argv)
111
+ argv = argv.dup
112
+
113
+ command = nil
114
+ controller_part = []
115
+ app_part = []
116
+
117
+ if COMMANDS.include? argv[0]
118
+ command = argv.shift
119
+ end
120
+
121
+ if i = argv.index('--')
122
+ # Handle the case where no controller options are given, just
123
+ # options after "--" as well (i == 0)
124
+ controller_part = (i == 0 ? [] : argv[0..i-1])
125
+ app_part = argv[i+1..-1]
126
+ else
127
+ controller_part = argv[0..-1]
128
+ end
129
+
130
+ return command, controller_part, app_part
131
+ end
132
+ end
133
+
134
+ end
@@ -0,0 +1,263 @@
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, because in :ontop mode, we normally want to see the output
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
+ begin; STDIN.reopen "/dev/null"; rescue ::Exception; end
138
+ end
139
+ module_function :simulate
140
+
141
+
142
+ def call_as_daemon(block, logfile_name = nil, app_name = nil)
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
+ # child
156
+
157
+ rd.close
158
+
159
+ # Detach from the controlling terminal
160
+ unless sess_id = Process.setsid
161
+ raise Daemons.RuntimeException.new('cannot detach from controlling terminal')
162
+ end
163
+
164
+ # Prevent the possibility of acquiring a controlling terminal
165
+ #if oldmode.zero?
166
+ trap 'SIGHUP', 'IGNORE'
167
+ exit if pid = safefork
168
+ #end
169
+
170
+ wr.write Process.pid
171
+ wr.close
172
+
173
+ $0 = app_name if app_name
174
+
175
+ Dir.chdir "/" # Release old working directory
176
+ File.umask 0000 # Insure sensible umask
177
+
178
+ # Make sure all file descriptors are closed
179
+ ObjectSpace.each_object(IO) do |io|
180
+ unless [STDIN, STDOUT, STDERR].include?(io)
181
+ begin
182
+ unless io.closed?
183
+ io.close
184
+ end
185
+ rescue ::Exception
186
+ end
187
+ end
188
+ end
189
+
190
+ redirect_io(logfile_name)
191
+
192
+ block.call
193
+
194
+ exit
195
+ end
196
+ end
197
+ module_function :call_as_daemon
198
+
199
+
200
+ # This method causes the current running process to become a daemon
201
+ def daemonize(logfile_name = nil, app_name = nil)
202
+ srand # Split rand streams between spawning and daemonized process
203
+ safefork and exit # Fork and exit from the parent
204
+
205
+ # Detach from the controlling terminal
206
+ unless sess_id = Process.setsid
207
+ raise Daemons.RuntimeException.new('cannot detach from controlling terminal')
208
+ end
209
+
210
+ # Prevent the possibility of acquiring a controlling terminal
211
+ #if oldmode.zero?
212
+ trap 'SIGHUP', 'IGNORE'
213
+ exit if pid = safefork
214
+ #end
215
+
216
+ $0 = app_name if app_name
217
+
218
+ Dir.chdir "/" # Release old working directory
219
+ File.umask 0000 # Insure sensible umask
220
+
221
+ # Make sure all file descriptors are closed
222
+ ObjectSpace.each_object(IO) do |io|
223
+ unless [STDIN, STDOUT, STDERR].include?(io)
224
+ begin
225
+ unless io.closed?
226
+ io.close
227
+ end
228
+ rescue ::Exception
229
+ end
230
+ end
231
+ end
232
+
233
+ redirect_io(logfile_name)
234
+
235
+ #return oldmode ? sess_id : 0 # Return value is mostly irrelevant
236
+ return sess_id
237
+ end
238
+ module_function :daemonize
239
+
240
+
241
+ # Free file descriptors and
242
+ # point them somewhere sensible
243
+ # STDOUT/STDERR should go to a logfile
244
+ def redirect_io(logfile_name)
245
+ begin; STDIN.reopen "/dev/null"; rescue ::Exception; end
246
+
247
+ if logfile_name
248
+ begin
249
+ STDOUT.reopen logfile_name, "a"
250
+ STDOUT.sync = true
251
+ rescue ::Exception
252
+ begin; STDOUT.reopen "/dev/null"; rescue ::Exception; end
253
+ end
254
+ else
255
+ begin; STDOUT.reopen "/dev/null"; rescue ::Exception; end
256
+ end
257
+
258
+ begin; STDERR.reopen STDOUT; rescue ::Exception; end
259
+ STDERR.sync = true
260
+ end
261
+ module_function :redirect_io
262
+
263
+ end