Class: Cognizant::Daemon
- Inherits:
-
Object
- Object
- Cognizant::Daemon
- Defined in:
- lib/cognizant/daemon.rb
Instance Attribute Summary collapse
-
#applications ⇒ Hash
Hash of applications being managed.
-
#daemonize ⇒ true, false
Whether or not to the daemon in the background.
-
#logfile ⇒ String
The file to log the daemon’s operations information into.
-
#loglevel ⇒ Logger::Severity
The level of information to log.
-
#pidfile ⇒ String
The pid lock file for the daemon.
- #socket ⇒ Object
-
#sockfile ⇒ Object
Returns the value of attribute sockfile.
-
#syslog ⇒ true, false
Whether or not to log to syslog daemon instead of file.
Instance Method Summary collapse
- #create_application(name, options = {}, &block) ⇒ Object
-
#daemonize_process ⇒ Object
Daemonize the current process and save it pid in a file.
- #load_file(file) ⇒ Object
- #reset! ⇒ Object
- #setup_directories ⇒ Object
- #setup_logging ⇒ Object
-
#shutdown! ⇒ Object
Stops the socket server and the tick loop, and performs cleanup.
- #start(options = {}) ⇒ Object
- #stop_previous_daemon ⇒ Object
- #stop_previous_socket ⇒ Object
- #trap_signals ⇒ Object
- #unlink_pid ⇒ Object
- #unlink_sockfile ⇒ Object
- #write_pid ⇒ Object
Instance Attribute Details
#applications ⇒ Hash
Hash of applications being managed.
42 43 44 |
# File 'lib/cognizant/daemon.rb', line 42 def applications @applications end |
#daemonize ⇒ true, false
Whether or not to the daemon in the background.
14 15 16 |
# File 'lib/cognizant/daemon.rb', line 14 def daemonize @daemonize end |
#logfile ⇒ String
The file to log the daemon’s operations information into. e.g. /Users/Gurpartap/.cognizant/cognizantd.log
26 27 28 |
# File 'lib/cognizant/daemon.rb', line 26 def logfile @logfile end |
#loglevel ⇒ Logger::Severity
The possible values must be one of the following: DEBUG, INFO, WARN, ERROR and FATAL or 0, 1, 2, 3, 4 (respectively).
The level of information to log. This does not affect the log level of managed processes.
37 38 39 |
# File 'lib/cognizant/daemon.rb', line 37 def loglevel @loglevel end |
#pidfile ⇒ String
The pid lock file for the daemon. e.g. /Users/Gurpartap/.cognizant/cognizantd.pid
21 22 23 |
# File 'lib/cognizant/daemon.rb', line 21 def pidfile @pidfile end |
#socket ⇒ Object
45 46 47 |
# File 'lib/cognizant/daemon.rb', line 45 def socket @socket end |
#sockfile ⇒ Object
Returns the value of attribute sockfile.
16 17 18 |
# File 'lib/cognizant/daemon.rb', line 16 def sockfile @sockfile end |
#syslog ⇒ true, false
Whether or not to log to syslog daemon instead of file.
30 31 32 |
# File 'lib/cognizant/daemon.rb', line 30 def syslog @syslog end |
Instance Method Details
#create_application(name, options = {}, &block) ⇒ Object
142 143 144 145 146 |
# File 'lib/cognizant/daemon.rb', line 142 def create_application(name, = {}, &block) app = Cognizant::Application.new(name, , &block) self.applications[app.name.to_sym] = app app end |
#daemonize_process ⇒ Object
Daemonize the current process and save it pid in a file.
256 257 258 259 260 261 |
# File 'lib/cognizant/daemon.rb', line 256 def daemonize_process if self.daemonize Log[self].info "Daemonizing into the background..." ::Process.daemon end end |
#load_file(file) ⇒ Object
117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/cognizant/daemon.rb', line 117 def load_file(file) if file.end_with?(".yml") yml_config = YAML.load_file(file) if yml_config yml_config.deep_symbolize_keys! yml_config_apps = yml_config.delete(:applications) if yml_config.has_key?(:applications) yml_config_apps.each { |key, value| self.create_application(key, value) } end else rb_file = File.(file) Kernel.load(rb_file) if File.exists?(rb_file) end end |
#reset! ⇒ Object
131 132 133 134 135 136 137 138 139 140 |
# File 'lib/cognizant/daemon.rb', line 131 def reset! self.daemonize = true self.sockfile = "/var/run/cognizant/cognizantd.sock" self.pidfile = "/var/run/cognizant/cognizantd.pid" self.syslog = false self.logfile = "/var/log/cognizant/cognizantd.log" self.loglevel = Logging::LEVELS["INFO"] self.applications.values.each(&:reset!) if self.applications.is_a?(Hash) self.applications = {} end |
#setup_directories ⇒ Object
166 167 168 169 |
# File 'lib/cognizant/daemon.rb', line 166 def setup_directories # Create the require directories. System.mkdir(File.dirname(self.sockfile), File.dirname(self.pidfile), File.dirname(self.logfile)) end |
#setup_logging ⇒ Object
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/cognizant/daemon.rb', line 148 def setup_logging Cognizant::Log.logger.root.level = self.loglevel unless self.daemonize stdout_appender = Cognizant::Log.stdout Cognizant::Log.logger.root.add_appenders(stdout_appender) end if self.syslog # TODO: Choose a non-default facility? (default: LOG_USR). syslog_appender = Cognizant::Log.syslog("cognizantd") Cognizant::Log.logger.root.add_appenders(syslog_appender) elsif self.logfile logfile_appender = Cognizant::Log.file(self.logfile) Cognizant::Log.logger.root.add_appenders(logfile_appender) end end |
#shutdown! ⇒ Object
Stops the socket server and the tick loop, and performs cleanup.
172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/cognizant/daemon.rb', line 172 def shutdown! Log[self].info "Shutting down Cognizant daemon..." EventMachine.next_tick do EventMachine.add_shutdown_hook do self.applications.values.each(&:shutdown!) unlink_pid # TODO: Close logger? end EventMachine.stop end end |
#start(options = {}) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/cognizant/daemon.rb', line 47 def start( = {}) self.reset! load_files = [] rb_files = [] yml_files = [] apps = [] load_files = [*.delete(:load)] if and .has_key?(:load) load_files.each do |include| Dir[File.(include)].each do |file| Log[self].info "Including config from #{file}." if file.end_with?(".yml") yml_files << file else rb_files << file end end end apps << .delete(:applications) if and .has_key?(:applications) yml_files.each do |file| file_opts = YAML.load_file(file) if file_opts file_opts.deep_symbolize_keys! apps << file_opts.delete(:applications) if file_opts.has_key?(:applications) end end # Attributes. .each do |key, value| self.send("#{key}=", .delete(key)) if self.respond_to?("#{key}=") end self.sockfile = File.(self.sockfile) self.pidfile = File.(self.pidfile) self.logfile = File.(self.logfile) Log[self].info "Booting up Cognizant daemon..." setup_directories setup_logging stop_previous_daemon stop_previous_socket trap_signals daemonize_process write_pid EventMachine.run do # Applications. Log[self].info "Cognizant daemon running successfully." apps.each do |app| app.each do |key, value| self.create_application(key, value) end end [*rb_files].each do |rb_file| load_file(rb_file) end EventMachine.start_unix_domain_server(self.sockfile, Cognizant::Interface) EventMachine.add_periodic_timer(1) do Cognizant::System.reset_data! self.applications.values.each(&:tick) end end end |
#stop_previous_daemon ⇒ Object
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/cognizant/daemon.rb', line 195 def stop_previous_daemon if self.pidfile and File.exists?(self.pidfile) if previous_daemon_pid = File.read(self.pidfile).to_i # Only attempt to stop automatically if the daemon will run in background. if self.daemonize and Cognizant::System.pid_running?(previous_daemon_pid) # Ensure that the process stops within 5 seconds or force kill. signals = ["TERM", "KILL"] timeout = 2 catch :stopped do signals.each do |stop_signal| # Send the stop signal and wait for it to stop. Cognizant::System.signal(stop_signal, previous_daemon_pid) # Poll to see if it has stopped yet. Minimum 2 so that we check at least once again. ([timeout / signals.size, 2].max).times do throw :stopped unless Cognizant::System.pid_running?(previous_daemon_pid) sleep 1 end end end end end # Alert the user to manually stop the previous daemon, if it is [still] alive. if Cognizant::System.pid_running?(previous_daemon_pid) raise "There is already a daemon running with pid #{previous_daemon_pid}." else unlink_pid # This will be overwritten anyways. end end end |
#stop_previous_socket ⇒ Object
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/cognizant/daemon.rb', line 227 def stop_previous_socket # Socket isn't actually owned by anyone. begin sock = UNIXSocket.new(self.sockfile) rescue Errno::ECONNREFUSED # This happens with non-socket files and when the listening # end of a socket has exited. rescue Errno::ENOENT # Socket doesn't exist. return else # Rats, it's still active. sock.close raise Errno::EADDRINUSE.new("Another process or application is likely already listening on the socket at #{self.sockfile}.") end # Socket should still exist, so don't need to handle error. stat = File.stat(self.sockfile) unless stat.socket? raise Errno::EADDRINUSE.new("Non-socket file present at socket file path #{self.sockfile}. Either remove that file and restart Cognizant, or change the socket file path.") end Log[self].info("Blowing away old socket file at #{self.sockfile}. This likely indicates a previous Cognizant application which did not shutdown gracefully.") # Whee, blow it away. unlink_sockfile end |
#trap_signals ⇒ Object
184 185 186 187 188 189 190 191 192 193 |
# File 'lib/cognizant/daemon.rb', line 184 def trap_signals terminator = Proc.new do Log[self].info "Received signal to shutdown." shutdown! end Signal.trap('TERM', &terminator) Signal.trap('INT', &terminator) Signal.trap('QUIT', &terminator) end |
#unlink_pid ⇒ Object
271 272 273 |
# File 'lib/cognizant/daemon.rb', line 271 def unlink_pid Cognizant::System.unlink_file(self.pidfile) if self.pidfile end |
#unlink_sockfile ⇒ Object
275 276 277 |
# File 'lib/cognizant/daemon.rb', line 275 def unlink_sockfile Cognizant::System.unlink_file(self.sockfile) if self.sockfile end |
#write_pid ⇒ Object
263 264 265 266 267 268 269 |
# File 'lib/cognizant/daemon.rb', line 263 def write_pid pid = ::Process.pid if self.pidfile Log[self].info "Writing the daemon pid (#{pid}) to the pidfile..." File.open(self.pidfile, "w") { |f| f.write(pid) } end end |