1 # -*- encoding: binary -*-
7 # current version of Zbatery
12 # runs the Zbatery HttpServer with +app+ and +options+ and does
13 # not return until the server has exited.
14 def run(app, options = {})
15 Rainbows::HttpServer.new(app, options).start.join
19 Rainbows::Const::RACK_DEFAULTS["SERVER_SOFTWARE"] = "Zbatery #{VERSION}"
21 # true if our Ruby implementation supports unlinked files
23 tmp = Unicorn::Util.tmpio
31 # we don't actually fork workers, but allow using the
32 # {before,after}_fork hooks found in Unicorn/Rainbows!
34 FORK_HOOK = lambda { |_,_| }
39 # override stuff we don't need or can't use portably
43 # master == worker in our case
44 def init_worker_process(worker)
45 after_fork.call(self, worker)
46 worker.user(*user) if user.kind_of?(Array) && ! worker.switched
47 build_app! unless preload_app
48 Rainbows::Response.setup(self.class)
49 Rainbows::MaxBody.setup
51 # avoid spurious wakeups and blocking-accept() with 1.8 green threads
52 if RUBY_VERSION.to_f < 1.9
54 HttpServer::LISTENERS.each { |l| l.nonblock = true }
57 logger.info "Zbatery #@use worker_connections=#@worker_connections"
61 # we can't/don't need to do the fchmod heartbeat Unicorn/Rainbows! does
68 # this class is only used to avoid breaking Unicorn user switching
73 # only used if no concurrency model is specified
74 def worker_loop(worker)
75 init_worker_process(worker)
77 ret = IO.select(LISTENERS, nil, nil, nil) and
78 ret.first.each do |sock|
80 process_client(sock.accept_nonblock)
81 rescue Errno::EAGAIN, Errno::ECONNABORTED
85 rescue Errno::EBADF, TypeError
88 Rainbows::Error.listen_loop(e)
93 def maintain_worker_count; end
94 def init_self_pipe!; end
96 # can't just do a graceful exit if reopening logs fails, so we just
99 logger.info "reopening logs"
100 Unicorn::Util.reopen_logs
101 logger.info "done reopening logs"
103 logger.error "failed reopening logs #{e.message}"
106 def trap_deferred(sig)
112 trap(:INT) { stop(false) } # Mongrel trapped INT for Win32...
114 # try these anyways regardless of platform...
115 trap(:TERM) { stop(false) }
117 trap(:USR1) { reopen_logs }
118 trap(:USR2) { reexec }
120 # no other way to reliably switch concurrency models...
121 trap(:HUP) { reexec; stop }
123 # technically feasible in some cases, just not sanely supportable:
124 %w(TTIN TTOU WINCH).each do |sig|
125 trap(sig) { logger.info "SIG#{sig} is not handled by Zbatery" }
127 rescue => e # hopefully ignores errors on Win32...
128 logger.error "failed to setup signal handler: #{e.message}"
132 ready_pipe.syswrite($$.to_s)
133 ready_pipe.close rescue nil
134 self.ready_pipe = nil
137 worker = Worker.new(0, DeadIO.new)
138 before_fork.call(self, worker)
139 worker_loop(worker) # runs forever
142 def stop(graceful = true)
144 exit!(0) unless graceful
149 hook == Zbatery::FORK_HOOK or
150 logger.warn "calling before_fork without forking"
156 hook == Zbatery::FORK_HOOK or
157 logger.warn "calling after_fork without having forked"
166 DEFAULTS[:before_fork] = DEFAULTS[:after_fork] = Zbatery::FORK_HOOK
169 unless Zbatery::UnlinkedIO
173 # Tempfiles should get automatically unlinked by GC
175 fp = Tempfile.new("zbatery")