mikehale-daemons 1.0.12.1

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,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
@@ -0,0 +1,12 @@
1
+ require 'etc'
2
+
3
+ Etc.instance_eval do
4
+ def groupname(gid)
5
+ Etc.group {|e| return e.name if gid == e.gid }
6
+ nil
7
+ end
8
+ def username(uid)
9
+ Etc.passwd {|e| return e.name if uid == e.uid }
10
+ nil
11
+ end
12
+ 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,127 @@
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, false)[0]
10
+
11
+ if pid
12
+ pid = PidFile.existing(pid)
13
+
14
+ unless PidFile.running?(pid.pid)
15
+ begin; pid.cleanup; rescue ::Exception; end
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
+ @app = an_app
32
+ @app_name = an_app.group.app_name + '_monitor'
33
+
34
+ if an_app.pidfile_dir
35
+ @pid = PidFile.new(an_app.pidfile_dir, @app_name, false)
36
+ else
37
+ @pid = PidMem.new
38
+ end
39
+ end
40
+
41
+ def watch(applications)
42
+ sleep(30)
43
+
44
+ loop do
45
+ applications.each {|a|
46
+ sleep(10)
47
+
48
+ unless a.running?
49
+ a.zap!
50
+
51
+ Process.detach(fork { a.start })
52
+
53
+ sleep(10)
54
+ end
55
+ }
56
+
57
+ sleep(30)
58
+ end
59
+ end
60
+ private :watch
61
+
62
+
63
+ def start_with_pidfile(applications)
64
+ fork do
65
+ Daemonize.daemonize(nil, @app_name)
66
+
67
+ begin
68
+ @pid.pid = Process.pid
69
+
70
+ # at_exit {
71
+ # begin; @pid.cleanup; rescue ::Exception; end
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
+ # begin; @pid.cleanup; rescue ::Exception; end
81
+ # exit
82
+ # }
83
+
84
+ watch(applications)
85
+ rescue ::Exception => e
86
+ begin
87
+ File.open(@app.logfile, 'a') {|f|
88
+ f.puts Time.now
89
+ f.puts e
90
+ f.puts e.backtrace.inspect
91
+ }
92
+ ensure
93
+ begin; @pid.cleanup; rescue ::Exception; end
94
+ exit!
95
+ end
96
+ end
97
+ end
98
+ end
99
+ private :start_with_pidfile
100
+
101
+ def start_without_pidfile(applications)
102
+ Thread.new { watch(applications) }
103
+ end
104
+ private :start_without_pidfile
105
+
106
+
107
+ def start(applications)
108
+ return if applications.empty?
109
+
110
+ if @pid.kind_of?(PidFile)
111
+ start_with_pidfile(applications)
112
+ else
113
+ start_without_pidfile(applications)
114
+ end
115
+ end
116
+
117
+
118
+ def stop
119
+ begin; Process.kill(Application::SIGNAL, @pid.pid); rescue ::Exception; end
120
+
121
+ # We try to remove the pid-files by ourselves, in case the application
122
+ # didn't clean it up.
123
+ begin; @pid.cleanup; rescue ::Exception; end
124
+ end
125
+
126
+ end
127
+ end
@@ -0,0 +1,101 @@
1
+ require 'open3'
2
+
3
+
4
+ module Daemons
5
+
6
+ class Pid
7
+
8
+ def Pid.running?(pid)
9
+ # Check if process is in existence
10
+ # The simplest way to do this is to send signal '0'
11
+ # (which is a single system call) that doesn't actually
12
+ # send a signal
13
+ begin
14
+ Process.kill(0, pid)
15
+ return true
16
+ rescue Errno::ESRCH
17
+ return false
18
+ rescue ::Exception # for example on EPERM (process exists but does not belong to us)
19
+ return true
20
+ #rescue Errno::EPERM
21
+ # return false
22
+ end
23
+ end
24
+
25
+ # def Pid.running?(pid, additional = nil)
26
+ # match_pid = Regexp.new("^\\s*#{pid}\\s")
27
+ # got_match = false
28
+ #
29
+ # #ps_all = IO.popen('ps ax') # the correct syntax is without a dash (-) !
30
+ # ps_in, ps_out, ps_err = Open3.popen3('ps ax') # the correct syntax is without a dash (-) !
31
+ #
32
+ # return true unless ps_out.gets
33
+ #
34
+ # begin
35
+ # ps_out.each { |psline|
36
+ # next unless psline =~ match_pid
37
+ # got_match = true
38
+ # got_match = false if additional and psline !~ /#{additional}/
39
+ # break
40
+ # }
41
+ # ensure
42
+ # begin; begin; ps_in.close; rescue ::Exception; end; begin; ps_out.close; rescue ::Exception; end; ps_err.close; rescue ::Exception; end
43
+ # end
44
+ #
45
+ # # an alternative would be to use the code below, but I don't know whether this is portable
46
+ # # `ps axo pid=`.split.include? pid.to_s
47
+ #
48
+ # return got_match
49
+ # end
50
+
51
+
52
+ # Returns the directory that should be used to write the pid file to
53
+ # depending on the given mode.
54
+ #
55
+ # Some modes may require an additionaly hint, others may determine
56
+ # the directory automatically.
57
+ #
58
+ # If no valid directory is found, returns nil.
59
+ #
60
+ def Pid.dir(dir_mode, dir, script)
61
+ # nil script parameter is allowed as long as dir_mode is not :script
62
+ return nil if dir_mode == :script && script.nil?
63
+
64
+ case dir_mode
65
+ when :normal
66
+ return File.expand_path(dir)
67
+ when :script
68
+ return File.expand_path(File.join(File.dirname(script),dir))
69
+ when :system
70
+ return '/var/run'
71
+ else
72
+ raise Error.new("pid file mode '#{dir_mode}' not implemented")
73
+ end
74
+ end
75
+
76
+ # Initialization method
77
+ def initialize
78
+ end
79
+
80
+
81
+ # Get method
82
+ def pid
83
+ end
84
+
85
+ # Set method
86
+ def pid=(p)
87
+ end
88
+
89
+ # Cleanup method
90
+ def cleanup
91
+ end
92
+
93
+ # Exists? method
94
+ def exist?
95
+ true
96
+ end
97
+
98
+ end
99
+
100
+
101
+ end