Class: Process::Daemon

Inherits:
Object
  • Object
show all
Defined in:
lib/process/daemon.rb,
lib/process/daemon/listen.rb,
lib/process/daemon/version.rb,
lib/process/daemon/log_file.rb,
lib/process/daemon/controller.rb,
lib/process/daemon/privileges.rb,
lib/process/daemon/notification.rb,
lib/process/daemon/process_file.rb

Overview

Provides the infrastructure for spawning a daemon.

Defined Under Namespace

Modules: Privileges Classes: Controller, Listen, LogFile, Notification, ProcessFile

Constant Summary collapse

VERSION =
"1.0.1"
TIMEOUT =

Daemon startup timeout

5

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(working_directory = ".") ⇒ Daemon

Initialize the daemon in the given working root.



34
35
36
37
38
# File 'lib/process/daemon.rb', line 34

def initialize(working_directory = ".")
  @working_directory = working_directory
  
  @shutdown_notification = Notification.new
end

Instance Attribute Details

#titleObject

The process title of the daemon.



110
111
112
# File 'lib/process/daemon.rb', line 110

def title
  @title
end

#working_directoryObject (readonly)

The directory the daemon will run in.



46
47
48
# File 'lib/process/daemon.rb', line 46

def working_directory
  @working_directory
end

Class Method Details

.controller(options = {}) ⇒ Object

The process controller, responsible for managing the daemon process start, stop, restart, etc.



172
173
174
# File 'lib/process/daemon.rb', line 172

def controller(options = {})
  @controller ||= Controller.new(instance, options)
end

.daemonize(*args, **options) ⇒ Object

The main entry point for daemonized scripts.



177
178
179
180
181
# File 'lib/process/daemon.rb', line 177

def daemonize(*args, **options)
  args = ARGV if args.empty?
  
  controller(options).daemonize(args)
end

.instanceObject

A shared instance of the daemon.



167
168
169
# File 'lib/process/daemon.rb', line 167

def instance
  @instance ||= self.new
end

.startObject

Start the shared daemon instance.



184
185
186
# File 'lib/process/daemon.rb', line 184

def start
  controller.start
end

.statusObject

Check if the shared daemon instance is runnning or not.



194
195
196
# File 'lib/process/daemon.rb', line 194

def status
  controller.status
end

.stopObject

Stop the shared daemon instance.



189
190
191
# File 'lib/process/daemon.rb', line 189

def stop
  controller.stop
end

Instance Method Details

#crashed?Boolean

Check the last few lines of the log file to find out if the daemon crashed.

Returns:

  • (Boolean)


85
86
87
88
89
90
91
92
93
94
95
# File 'lib/process/daemon.rb', line 85

def crashed?
  count = 3
  
  LogFile.open(log_file_path).tail_log do |line|
    return true if line.match("=== Daemon Crashed")

    break if (count -= 1) == 0
  end

  return false
end

#log_directoryObject

Return the directory to store log files in.



49
50
51
# File 'lib/process/daemon.rb', line 49

def log_directory
  File.join(working_directory, "log")
end

#log_file_pathObject

Standard log file for stdout and stderr.



54
55
56
# File 'lib/process/daemon.rb', line 54

def log_file_path
  File.join(log_directory, "#{name}.log")
end

#mark_logObject

Mark the output log.



69
70
71
72
73
# File 'lib/process/daemon.rb', line 69

def mark_log
  File.open(log_file_path, "a") do |log_file|
    log_file.puts "=== Log Marked @ #{Time.now.to_s} [#{Process.pid}] ==="
  end
end

#nameObject

Return the name of the daemon



41
42
43
# File 'lib/process/daemon.rb', line 41

def name
  return self.class.name.gsub(/[^a-zA-Z0-9]+/, '-')
end

#preforkObject

The main function to setup any environment required by the daemon



98
99
100
101
102
103
104
105
106
107
# File 'lib/process/daemon.rb', line 98

def prefork
  # Ignore any previously setup signal handler for SIGINT:
  trap(:INT, :DEFAULT)
  
  # We update the working directory to a full path:
  @working_directory = File.expand_path(working_directory)
  
  FileUtils.mkdir_p(log_directory)
  FileUtils.mkdir_p(runtime_directory)
end

#process_file_pathObject

Standard location of process pid file.



64
65
66
# File 'lib/process/daemon.rb', line 64

def process_file_path
  File.join(runtime_directory, "#{name}.pid")
end

#request_shutdownObject

Request that the sleep_until_interrupted function call returns.



124
125
126
# File 'lib/process/daemon.rb', line 124

def request_shutdown
  @shutdown_notification.signal
end

#runObject

If you want to implement a long running process you override this method. You may like to call super but it is not necessary to use the supplied interruption machinery.



142
143
144
# File 'lib/process/daemon.rb', line 142

def run
  sleep_until_interrupted
end

#runtime_directoryObject

Runtime data directory for the daemon.



59
60
61
# File 'lib/process/daemon.rb', line 59

def runtime_directory
  File.join(working_directory, "run")
end

#shutdownObject

This function should terminate any active processes in the daemon and return as quickly as possible.



147
148
# File 'lib/process/daemon.rb', line 147

def shutdown
end

#sleep_until_interruptedObject

Call this function to sleep until the daemon is sent SIGINT.



129
130
131
132
133
134
135
# File 'lib/process/daemon.rb', line 129

def sleep_until_interrupted
  trap(:INT) do
    self.request_shutdown
  end

  @shutdown_notification.wait
end

#spawnObject

The entry point from the newly forked process.



151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/process/daemon.rb', line 151

def spawn
  self.title = self.name
  
  self.startup
  
  begin
    self.run
  rescue Interrupt
    $stderr.puts "Daemon interrupted, proceeding to shutdown."
  end
  
  self.shutdown
end

#startupObject

This function must setup the daemon quickly and return.



138
139
# File 'lib/process/daemon.rb', line 138

def startup
end

#tail_log(output) ⇒ Object

Prints some information relating to daemon startup problems.



76
77
78
79
80
81
82
# File 'lib/process/daemon.rb', line 76

def tail_log(output)
  lines = LogFile.open(log_file_path).tail_log do |line|
    line.match("=== Log Marked") || line.match("=== Daemon Exception Backtrace")
  end
  
  output.puts lines
end