Added *pyc and *~ files to .gitignore
[mwamko.git] / lib / ciconia_daemonizer.rb
blob36e4876a99e820965564187603e97c3b5b8a8a1e
1 require 'rubygems'
2 require 'open4'
4 module Daemon
5   WorkingDirectory = File.expand_path(File.dirname(__FILE__))
7   class Base
8     def self.task_identifier
9       @@task_identifier
10     end
12     def self.uid
13       @@uid
14     end
16     def self.pid_fn(timestamp)
17       "/tmp/#{name}.#{timestamp}.pid"
18     end
19     
20     def self.daemonize(task_identifier, uid=nil)
21       @@uid = uid || 1000 # The default uid of the daemon process is 10000
22       @@task_identifier = task_identifier
23       Controller.daemonize(self)
24     end
26   private
28     def self.run_and_record(command)
29       @@has_stdout ||= nil
30       @@has_stderr ||= nil
31       @@has_rubyerr ||= nil
32       @@ruby_errors ||= ''
33       @@std_errors_txt = ''
36       @child_pid, stdin, stdout, stderr = 
37         Open4::popen4(command)
38       stdin.close
40       Process::waitpid2 @child_pid
42       output = ''
44       all_files = Dir.glob(pid_fn('*'))
45       all_files.each do |pid_file|
46         pid, command_id = `cat #{pid_file}`.split("\n")
47   
48         if pid.to_i == Process.pid
49           file = File.open(pid_file, "a")
50           file << command.center(80, "-")
51           if !stderr.eof?
52             file << "\n"
53             file << "STDERR".center(80, "=")
54             @@has_stderr = true
55             stderr.each do |err|
56               file << err
57               @@std_errors_txt << err
58             end
59           end
60           
61           if !stdout.eof?
62             file << "\n"
63             file << "STDOUT".center(80, "=")
64             @@has_stdout = true
65             stdout.each do |out|
66               file << out
67               output << out
68             end
69           end
71           if !@@ruby_errors.empty?
72             file << "\n"
73             file << "RUBYERR".center(80, "=")
74             file << @@ruby_errors
75             @@ruby_errors = ''
76             @@has_rubyerr = true
77           end
78           
79           file << "\n"
80           file << "\n"
81           file.close
83           @@pid_file = pid_file
84         end
85       end
87       return output
88     end
90   end
91   
92   module Controller
93     def self.daemonize(daemon)
94       
95       if ARGV.include?('--kill-daemon')
96         stop(daemon)
97       else
98         start(daemon)
99       end
100     end
101     
102     def self.stop(daemon)
103       all_files = Dir.glob(daemon.pid_fn('*')[0..-5])
105       pid_found = false
106       all_files.each do |pid_file|
107         pid, command_id = `cat #{pid_file}`.split("\n")[0..1] # first 2 lines
108         if command_id == daemon.task_identifier
109           
110           `rm #{pid_file}`
111           `kill #{pid}`
112           pid_found = true
113         end
114       end
116       if !pid_found
117         STDERR.puts "No PID files found for:\n  #{daemon.task_identifier}"
118         STDERR.puts
119         STDERR.puts "Is the daemon started?"
120         STDERR.puts
121         exit
122       end
124     end
127     # Daemonizer by Travis Whitton
128   
129     # Try to fork if at all possible retrying every 5 sec if the
130     # maximum process limit for the system has been reached
131     def self.safefork
132       tryagain = true
133   
134       while tryagain
135         tryagain = false
136         begin
137           if pid = fork
138             return pid
139           end
140         rescue Errno::EWOULDBLOCK
141           sleep 5
142           tryagain = true
143         end
144       end
145     end
146   
147     # This method causes the current running process to become a daemon
148     # If closefd is true, all existing file descriptors are closed
149     def self.start(daemon, oldmode=0, closefd=false)
150   
151       srand # Split rand streams between spawning and daemonized process
152       safefork and exit # Fork and exit from the parent
153   
154       pid_file = 
155         daemon.pid_fn(Time.now.to_i.to_s + '.' +
156                       Process.pid.to_s.rjust(5,'0') + '.' +
157                       daemon.task_identifier.rjust(26).gsub(' ', '_')
158                      )
159   
160       raise 'daemon.uid is not set' if daemon.uid == nil
161   
162       if Process.uid != daemon.uid
163         Process.gid = Process.egid =
164           `grep -E ^.*?:.*?:#{Process.uid} /etc/passwd`.split(':')[3].to_i
165         Process.uid = Process.euid = daemon.uid
166       end
167   
168       # Detach from the controlling terminal
169       unless sess_id = Process.setsid
170         raise "Cannot detach from controlled terminal"
171       end
172   
173       # Prevent the possibility of acquiring a controlling terminal
174       if oldmode.zero?
175         trap 'SIGHUP', 'IGNORE'
176         exit if pid = safefork
177       end
178   
179   
180       `echo #{Process.pid} >> #{pid_file}`
181       `echo #{daemon.task_identifier} >> #{pid_file}`
182   
183   
184       Dir.chdir "/"   # Release old working directory
185       File.umask 0000 # Insure sensible umask
186   
187       if closefd
188         # Make sure all file descriptors are closed
189         ObjectSpace.each_object(IO) do |io|
190           unless [STDIN, STDOUT, STDERR].include?(io)
191             io.close rescue nil
192           end
193         end
194       end
195   
196       STDIN.reopen "/dev/null"       # Free file descriptors and
197       STDOUT.reopen "/dev/null", "a" # point them somewhere sensible
198       STDERR.reopen STDOUT           # STDOUT/STDERR should go to a logfile
199   
200       
201       # The user defined tasks
202       daemon.start
203       daemon.stop # Clean up after daemon terminates self
204                   # if the user defined any cleanup procedures
205   
206       return # Return value is mostly irrelevant
207     end
208   
209   
211   end