Class: Daemon

Inherits:
Object
  • Object
show all
Defined in:
lib/daemon.rb

Defined Under Namespace

Classes: LockError

Class Method Summary collapse

Class Method Details

.daemonize(pid_file, log_file = nil, sync = true) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/daemon.rb', line 8

def self.daemonize(pid_file, log_file = nil, sync = true)
	if block_given?
		spawn do |send_ok, send_error|
			log = begin
				# try to lock before we kill stdin/out
				lock(pid_file)

				# close I/O
				disconnect(log_file, sync)
			rescue => error
				send_error.call(error)
			end

			send_ok.call

			yield log
		end # => pid, wait
	else
		# become new process group leader
		fence

		# try to lock before we kill stdin/out
		lock(pid_file)

		# close I/O
		disconnect(log_file, sync) # => log
	end
end

.disconnect(log_file = nil, sync = true) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/daemon.rb', line 96

def self.disconnect(log_file = nil, sync = true)
	if log_file
		log = File.open(log_file, File::WRONLY | File::CREAT | File::APPEND | File::BINARY)
		log.sync = sync
	else
		# don't raise on STDOUT/STDERR write
		log = File.new('/dev/null', File::WRONLY | File::BINARY)
	end

	# disconnect
	STDIN.close # raise IOError on STDIN.read
	STDOUT.reopen log
	STDERR.reopen log

	# provide log IO
	return log
end

.fenceObject



72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/daemon.rb', line 72

def self.fence
	if block_given?
		fork do
			Process.setsid # become new session leader
			# now in child
			yield
		end # => pid
	else
		exit! 0 if fork
		Process.setsid # become new session leader
		# now in child
	end
end

.lock(pid_file) ⇒ Object

Raises:



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

def self.lock(pid_file)
	pf = File.open(pid_file, File::RDWR | File::CREAT)
	raise LockError, pf unless pf.flock(File::LOCK_EX | File::LOCK_NB)
	pf.truncate(0)
	pf.write(Process.pid.to_s + "\n")
	pf.flush

	@pf = pf # keep it open and locked until process exits
end

.spawnObject



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/daemon.rb', line 37

def self.spawn
	r, w = IO.pipe

	pid = fence do
		r.close

		yield(
			->{
				w.write Marshal.dump(nil) # send OK to parent
				w.close
			},
			->(error){
				w.write Marshal.dump(error) # send error to parent
				w.close
				exit 42
			}
		)
	end
	thr = Process.detach(pid)

	w.close
	data = r.read
	data.empty? and fail 'ok/error handler not called!'

	if error = Marshal.load(data)
		thr.join # wait for child to die
		raise error
	end

	return pid, thr
ensure
	w.close unless w.closed?
	r.close
end