5 WRITES_PID = [:start, :restart]
7 attr_accessor :name, :uid, :gid, :log, :start, :stop, :restart
10 self.log = '/dev/null'
19 pid = File.read(self.pid_file).strip.to_i
20 System::Process.new(pid).exists?
26 def file_writable?(file)
28 ::Process::Sys.setgid(Etc.getgrnam(self.gid).gid) if self.gid
29 ::Process::Sys.setuid(Etc.getpwnam(self.uid).uid) if self.uid
31 File.writable?(file) ? exit(0) : exit(1)
34 wpid, status = ::Process.waitpid2(pid)
35 status.exitstatus == 0 ? true : false
39 # determine if we're tracking pid or not
44 # a start command must be specified
47 applog(self, :error, "No start command was specified")
50 # self-daemonizing processes must specify a stop command
51 if !@tracking_pid && self.stop.nil?
53 applog(self, :error, "No stop command was specified")
56 # uid must exist if specified
59 Etc.getpwnam(self.uid)
62 applog(self, :error, "UID for '#{self.uid}' does not exist")
66 # gid must exist if specified
69 Etc.getgrnam(self.gid)
72 applog(self, :error, "GID for '#{self.gid}' does not exist")
76 # pid dir must exist if specified
77 if !@tracking_pid && !File.exist?(File.dirname(self.pid_file))
79 applog(self, :error, "PID file directory '#{File.dirname(self.pid_file)}' does not exist")
82 # pid dir must be writable if specified
83 if !@tracking_pid && !file_writable?(File.dirname(self.pid_file))
85 applog(self, :error, "PID file directory '#{File.dirname(self.pid_file)}' is not writable by #{self.uid || Etc.getlogin}")
89 if !File.exist?(File.dirname(self.log))
91 applog(self, :error, "Log directory '#{File.dirname(self.log)}' does not exist")
94 # log file or dir must be writable
95 if File.exist?(self.log)
96 unless file_writable?(self.log)
98 applog(self, :error, "Log file '#{self.log}' exists but is not writable by #{self.uid || Etc.getlogin}")
101 unless file_writable?(File.dirname(self.log))
103 applog(self, :error, "Log directory '#{File.dirname(self.log)}' is not writable by #{self.uid || Etc.getlogin}")
110 # DON'T USE THIS INTERNALLY. Use the instance variable. -- Kev
111 # No really, trust me. Use the instance variable.
113 # if value is nil, do the right thing
115 @tracking_pid = false
124 @pid_file ||= default_pid_file
136 call_action(:restart)
142 ::Process::Sys.setgid(Etc.getgrnam(self.gid).gid) if self.gid
143 ::Process::Sys.setuid(Etc.getpwnam(self.uid).uid) if self.uid
146 STDIN.reopen "/dev/null"
147 STDOUT.reopen self.log, "a"
150 # close any other file descriptors
151 3.upto(256){|fd| IO::new(fd).close rescue nil}
153 exec command unless command.empty?
157 def call_action(action)
158 command = send(action)
160 if action == :stop && command.nil?
161 pid = File.read(self.pid_file).strip.to_i
164 applog(self, :info, "#{self.name} stop: default lambda killer")
166 ::Process.kill('HUP', pid) rescue nil
168 # Poll to see if it's dead
171 ::Process.kill(0, pid)
180 ::Process.kill('KILL', pid) rescue nil
184 if command.kind_of?(String)
188 # double fork god-daemonized processes
189 # we don't want to wait for them to finish
194 pid = self.spawn(command)
198 ::Process.waitpid(opid, 0)
202 # single fork self-daemonizing processes
203 # we want to wait for them to finish
204 pid = self.spawn(command)
205 ::Process.waitpid(pid, 0)
208 if @tracking_pid or (@pid_file.nil? and WRITES_PID.include?(action))
209 File.open(default_pid_file, 'w') do |f|
214 @pid_file = default_pid_file
216 elsif command.kind_of?(Proc)
220 raise NotImplementedError
225 File.join(God.pid_file_directory, "#{self.name}.pid")