module Procrastinator::Scheduler::DaemonWorking
Daemonized work style
@see WorkProxy
Constants
- DEFAULT_PID_DIR
Default directory to store PID files in.
- PID_EXT
File extension for process ID files
Public Class Methods
Stops the procrastinator process denoted by the provided pid file
# File lib/procrastinator/scheduler.rb, line 280 def self.halt!(pid_path) pid_path = normalize_pid pid_path Process.kill('TERM', pid_path.read.to_i) end
Normalizes the given pid path, including conversion to absolute path and defaults.
@param pid_path [Pathname, File, String, nil] path to normalize
# File lib/procrastinator/scheduler.rb, line 272 def self.normalize_pid(pid_path) normalized = Pathname.new(pid_path || DEFAULT_PID_DIR) normalized /= "#{ PROG_NAME.downcase }#{ PID_EXT }" unless normalized.extname == PID_EXT normalized.expand_path end
# File lib/procrastinator/scheduler.rb, line 286 def self.running?(pid_path) pid = normalize_pid(pid_path).read.to_i # this raises Errno::ESRCH when no process found, therefore if found we should exit Process.getpgid pid true rescue Errno::ENOENT, Errno::ESRCH false end
Public Instance Methods
Consumes the current process and turns it into a background daemon and proceed as threaded
. Additional logging is recorded in the directory specified by the Procrastinator.setup
configuration.
If pid_path ends with extension ‘.pid’, the basename will be requested as process title (depending on OS support). An extensionless path is assumed to be a directory and a default filename (and proctitle) is used.
@param pid_path [Pathname, File, String, nil] Path to where the process ID file is to be kept.
# File lib/procrastinator/scheduler.rb, line 263 def daemonized!(pid_path = nil) spawn_daemon(pid_path) threaded end
Private Instance Methods
# File lib/procrastinator/scheduler.rb, line 338 def ensure_unique(pid_path) return unless pid_path.exist? @logger.debug "Checking pid file #{ pid_path }" if DaemonWorking.running? pid_path hint = 'Either terminate that process or remove the pid file (if coincidental).' msg = "Another process (pid #{ pid_path.read }) already exists for #{ pid_path }. #{ hint }" @logger.fatal msg raise ProcessExistsError, msg else @logger.warn "Replacing old pid file of defunct process (pid #{ pid_path.read }) at #{ pid_path }." end end
# File lib/procrastinator/scheduler.rb, line 322 def manage_pid(pid_path) ensure_unique(pid_path) @logger.debug "Managing pid at path: #{ pid_path }" pid_path.dirname.mkpath pid_path.write Process.pid.to_s at_exit do if pid_path.exist? @logger.debug "Cleaning up pid file #{ pid_path }" pid_path.delete end @logger.info "Procrastinator (pid #{ Process.pid }) halted." end end
# File lib/procrastinator/scheduler.rb, line 353 def print_debug_context @logger.debug "Ruby Path: #{ ENV.fetch 'RUBY_ROOT', '?' }" @logger.debug "Bundler Path: #{ ENV.fetch 'BUNDLE_BIN_PATH', '?' }" # LOGNAME is the posix standard and is set by cron, so probably reliable. @logger.debug "Runtime User: #{ ENV.fetch('LOGNAME', ENV.fetch('USERNAME', '?')) }" end
# File lib/procrastinator/scheduler.rb, line 360 def rename_process(pid_path) name = pid_path.basename(PID_EXT).to_s if system('pidof', name, out: File::NULL) @logger.warn "Another process is already named '#{ name }'. Consider the 'name:' keyword to distinguish." end @logger.debug "Renaming process to: #{ name }" Process.setproctitle name end
# File lib/procrastinator/scheduler.rb, line 303 def spawn_daemon(pid_path) pid_path = DaemonWorking.normalize_pid pid_path open_log quiet: true @logger.info "Starting #{ PROG_NAME } daemon..." print_debug_context # "You, search from the spastic dentistry department down through disembowelment. # You, cover children's dance recitals through holiday weekend IKEA. Go." Process.daemon manage_pid pid_path rename_process pid_path rescue StandardError => e @logger&.fatal ([e.message] + e.backtrace).join("\n") raise e end