5 WRITES_PID = [:start, :restart]
7 attr_accessor :name, :uid, :gid, :log, :start, :stop, :restart
10 self.log = '/dev/null'
20 pid = File.read(self.pid_file).strip.to_i
21 System::Process.new(pid).exists?
27 def file_writable?(file)
29 ::Process::Sys.setgid(Etc.getgrnam(self.gid).gid) if self.gid
30 ::Process::Sys.setuid(Etc.getpwnam(self.uid).uid) if self.uid
32 File.writable?(file) ? exit(0) : exit(1)
35 wpid, status = ::Process.waitpid2(pid)
36 status.exitstatus == 0 ? true : false
40 # determine if we're tracking pid or not
45 # a start command must be specified
48 applog(self, :error, "No start command was specified")
51 # self-daemonizing processes must specify a stop command
52 if !@tracking_pid && self.stop.nil?
54 applog(self, :error, "No stop command was specified")
57 # uid must exist if specified
60 Etc.getpwnam(self.uid)
63 applog(self, :error, "UID for '#{self.uid}' does not exist")
67 # gid must exist if specified
70 Etc.getgrnam(self.gid)
73 applog(self, :error, "GID for '#{self.gid}' does not exist")
77 # pid dir must exist if specified
78 if !@tracking_pid && !File.exist?(File.dirname(self.pid_file))
80 applog(self, :error, "PID file directory '#{File.dirname(self.pid_file)}' does not exist")
83 # pid dir must be writable if specified
84 if !@tracking_pid && File.exist?(File.dirname(self.pid_file)) && !file_writable?(File.dirname(self.pid_file))
86 applog(self, :error, "PID file directory '#{File.dirname(self.pid_file)}' is not writable by #{self.uid || Etc.getlogin}")
90 if !File.exist?(File.dirname(self.log))
92 applog(self, :error, "Log directory '#{File.dirname(self.log)}' does not exist")
95 # log file or dir must be writable
96 if File.exist?(self.log)
97 unless file_writable?(self.log)
99 applog(self, :error, "Log file '#{self.log}' exists but is not writable by #{self.uid || Etc.getlogin}")
102 unless file_writable?(File.dirname(self.log))
104 applog(self, :error, "Log directory '#{File.dirname(self.log)}' is not writable by #{self.uid || Etc.getlogin}")
111 # DON'T USE THIS INTERNALLY. Use the instance variable. -- Kev
112 # No really, trust me. Use the instance variable.
114 # if value is nil, do the right thing
116 @tracking_pid = false
125 @pid_file ||= default_pid_file
129 contents = File.read(self.pid_file).strip rescue ''
130 real_pid = contents =~ /^\d+$/ ? contents.to_i : nil
149 call_action(:restart)
155 ::Process::Sys.setgid(Etc.getgrnam(self.gid).gid) if self.gid
156 ::Process::Sys.setuid(Etc.getpwnam(self.uid).uid) if self.uid
159 STDIN.reopen "/dev/null"
160 STDOUT.reopen self.log, "a"
163 # close any other file descriptors
164 3.upto(256){|fd| IO::new(fd).close rescue nil}
166 exec command unless command.empty?
170 def call_action(action)
171 command = send(action)
173 if action == :stop && command.nil?
174 pid = File.read(self.pid_file).strip.to_i
177 applog(self, :info, "#{self.name} stop: default lambda killer")
179 ::Process.kill('HUP', pid) rescue nil
181 # Poll to see if it's dead
184 ::Process.kill(0, pid)
193 ::Process.kill('KILL', pid) rescue nil
197 if command.kind_of?(String)
201 # double fork god-daemonized processes
202 # we don't want to wait for them to finish
208 pid = self.spawn(command)
209 puts pid.to_s # send pid back to forker
212 ::Process.waitpid(opid, 0)
216 # make sure the file descriptors get closed no matter what
221 # single fork self-daemonizing processes
222 # we want to wait for them to finish
223 pid = self.spawn(command)
224 ::Process.waitpid(pid, 0)
227 if @tracking_pid or (@pid_file.nil? and WRITES_PID.include?(action))
228 File.open(default_pid_file, 'w') do |f|
233 @pid_file = default_pid_file
235 elsif command.kind_of?(Proc)
239 raise NotImplementedError
244 File.join(God.pid_file_directory, "#{self.name}.pid")