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
128 # Fetch the PID from pid_file. If the pid_file does not
129 # exist, then use the PID from the last time it was read.
130 # If it has never been read, then return nil.
132 # Returns Integer(pid) or nil
134 contents = File.read(self.pid_file).strip rescue ''
135 real_pid = contents =~ /^\d+$/ ? contents.to_i : nil
154 call_action(:restart)
160 ::Process::Sys.setgid(Etc.getgrnam(self.gid).gid) if self.gid
161 ::Process::Sys.setuid(Etc.getpwnam(self.uid).uid) if self.uid
164 STDIN.reopen "/dev/null"
165 STDOUT.reopen self.log, "a"
168 # close any other file descriptors
169 3.upto(256){|fd| IO::new(fd).close rescue nil}
171 exec command unless command.empty?
175 def call_action(action)
176 command = send(action)
178 if action == :stop && command.nil?
179 pid = File.read(self.pid_file).strip.to_i
182 applog(self, :info, "#{self.name} stop: default lambda killer")
184 ::Process.kill('HUP', pid) rescue nil
186 # Poll to see if it's dead
189 ::Process.kill(0, pid)
198 ::Process.kill('KILL', pid) rescue nil
202 if command.kind_of?(String)
206 # double fork god-daemonized processes
207 # we don't want to wait for them to finish
213 pid = self.spawn(command)
214 puts pid.to_s # send pid back to forker
217 ::Process.waitpid(opid, 0)
221 # make sure the file descriptors get closed no matter what
226 # single fork self-daemonizing processes
227 # we want to wait for them to finish
228 pid = self.spawn(command)
229 ::Process.waitpid(pid, 0)
232 if @tracking_pid or (@pid_file.nil? and WRITES_PID.include?(action))
233 File.open(default_pid_file, 'w') do |f|
238 @pid_file = default_pid_file
240 elsif command.kind_of?(Proc)
244 raise NotImplementedError
249 File.join(God.pid_file_directory, "#{self.name}.pid")