1
2
3
4
5
6
7
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
36
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
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
| | # -*- encoding: binary -*-
# :enddoc:
require 'rainbows'
module Zbatery
# current version of Zbatery
VERSION = "0.3.0"
class << self
# runs the Zbatery HttpServer with +app+ and +options+ and does
# not return until the server has exited.
def run(app, options = {})
Rainbows::HttpServer.new(app, options).start.join
end
end
Rainbows::Const::RACK_DEFAULTS["SERVER_SOFTWARE"] = "Zbatery #{VERSION}"
# true if our Ruby implementation supports unlinked files
UnlinkedIO = begin
tmp = Unicorn::Util.tmpio
tmp.chmod(0)
tmp.close
true
rescue
false
end
# we don't actually fork workers, but allow using the
# {before,after}_fork hooks found in Unicorn/Rainbows!
# config files...
FORK_HOOK = lambda { |_,_| }
end
# :stopdoc:
# override stuff we don't need or can't use portably
module Rainbows
module Base
# master == worker in our case
def init_worker_process(worker)
after_fork.call(self, worker)
worker.user(*user) if user.kind_of?(Array) && ! worker.switched
build_app! unless preload_app
Rainbows::Response.setup(self.class)
Rainbows::MaxBody.setup
# avoid spurious wakeups and blocking-accept() with 1.8 green threads
if RUBY_VERSION.to_f < 1.9
require "io/nonblock"
HttpServer::LISTENERS.each { |l| l.nonblock = true }
end
logger.info "Zbatery #@use worker_connections=#@worker_connections"
end
end
# we can't/don't need to do the fchmod heartbeat Unicorn/Rainbows! does
def G.tick
alive
end
class HttpServer
# this class is only used to avoid breaking Unicorn user switching
class DeadIO
def chown(*args); end
end
# only used if no concurrency model is specified
def worker_loop(worker)
init_worker_process(worker)
begin
ret = IO.select(LISTENERS, nil, nil, nil) and
ret.first.each do |sock|
begin
process_client(sock.accept_nonblock)
rescue Errno::EAGAIN, Errno::ECONNABORTED
end
end
rescue Errno::EINTR
rescue Errno::EBADF, TypeError
break
rescue => e
Rainbows::Error.listen_loop(e)
end while G.alive
end
# no-op
def maintain_worker_count; end
def init_self_pipe!; end
# can't just do a graceful exit if reopening logs fails, so we just
# continue on...
def reopen_logs
logger.info "reopening logs"
Unicorn::Util.reopen_logs
logger.info "done reopening logs"
rescue => e
logger.error "failed reopening logs #{e.message}"
end
def trap_deferred(sig)
# nothing
end
def join
begin
trap(:INT) { stop(false) } # Mongrel trapped INT for Win32...
# try these anyways regardless of platform...
trap(:TERM) { stop(false) }
trap(:QUIT) { stop }
trap(:USR1) { reopen_logs }
trap(:USR2) { reexec }
# no other way to reliably switch concurrency models...
trap(:HUP) { reexec; stop }
# technically feasible in some cases, just not sanely supportable:
%w(TTIN TTOU WINCH).each do |sig|
trap(sig) { logger.info "SIG#{sig} is not handled by Zbatery" }
end
rescue => e # hopefully ignores errors on Win32...
logger.error "failed to setup signal handler: #{e.message}"
end
if ready_pipe
ready_pipe.syswrite($$.to_s)
ready_pipe.close rescue nil
self.ready_pipe = nil
end
worker = Worker.new(0, DeadIO.new)
before_fork.call(self, worker)
worker_loop(worker) # runs forever
end
def stop(graceful = true)
Rainbows::G.quit!
exit!(0) unless graceful
end
def before_fork
hook = super
hook == Zbatery::FORK_HOOK or
logger.warn "calling before_fork without forking"
hook
end
def after_fork
hook = super
hook == Zbatery::FORK_HOOK or
logger.warn "calling after_fork without having forked"
hook
end
end
end
module Unicorn
class Configurator
DEFAULTS[:before_fork] = DEFAULTS[:after_fork] = Zbatery::FORK_HOOK
end
unless Zbatery::UnlinkedIO
require 'tempfile'
class Util
# Tempfiles should get automatically unlinked by GC
def self.tmpio
fp = Tempfile.new("zbatery")
fp.binmode
fp.sync = true
fp
end
end
end
end
|