Zbatery 0.6.0 - Rainbows! 2.1.x resync
[zbatery.git] / lib / zbatery.rb
blobd056ddd9ea0206182405a1ce3a5de38dbdc5c85e
1 # -*- encoding: binary -*-
2 # :enddoc:
3 require 'rainbows'
5 module Zbatery
7   # version of Zbatery, currently 0.6.0
8   VERSION = "0.6.0"
10   class << self
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
16     end
17   end
19   Rainbows::Const::RACK_DEFAULTS["SERVER_SOFTWARE"] = "Zbatery #{VERSION}"
21   # we don't actually fork workers, but allow using the
22   # {before,after}_fork hooks found in Unicorn/Rainbows!
23   # config files...
24   FORK_HOOK = lambda { |_,_| }
25 end
27 # :stopdoc:
28 # override stuff we don't need or can't use portably
29 module Rainbows
31   module Base
32     # master == worker in our case
33     def init_worker_process(worker)
34       after_fork.call(self, worker)
35       worker.user(*user) if user.kind_of?(Array) && ! worker.switched
36       build_app! unless preload_app
37       Rainbows::Response.setup(self.class)
38       Rainbows::MaxBody.setup
39       Rainbows::RackInput.setup
40       Rainbows::ProcessClient.const_set(:APP, @app)
42       logger.info "Zbatery #@use worker_connections=#@worker_connections"
43     end
44   end
46   # we can't/don't need to do the fchmod heartbeat Unicorn/Rainbows! does
47   def G.tick
48     alive
49   end
51   class HttpServer
53     # this class is only used to avoid breaking Unicorn user switching
54     class DeadIO
55       def chown(*args); end
56       alias fcntl chown
57     end
59     # only used if no concurrency model is specified
60     def worker_loop(worker)
61       init_worker_process(worker)
62       begin
63         ret = IO.select(LISTENERS, nil, nil, nil) and
64         ret[0].each do |sock|
65           io = sock.kgio_tryaccept and process_client(io)
66         end
67       rescue Errno::EINTR
68       rescue Errno::EBADF, TypeError
69         break
70       rescue => e
71         Rainbows::Error.listen_loop(e)
72       end while G.alive
73     end
75     # no-op
76     def maintain_worker_count; end
77     def init_self_pipe!; end
79     # can't just do a graceful exit if reopening logs fails, so we just
80     # continue on...
81     def reopen_logs
82       logger.info "reopening logs"
83       Unicorn::Util.reopen_logs
84       logger.info "done reopening logs"
85       rescue => e
86         logger.error "failed reopening logs #{e.message}"
87     end
89     def trap_deferred(sig)
90       # nothing
91     end
93     def join
94       trap(:INT) { stop(false) }
95       trap(:TERM) { stop(false) }
96       trap(:QUIT) { stop }
97       trap(:USR1) { reopen_logs }
98       trap(:USR2) { reexec }
99       trap(:HUP) { reexec; stop }
101       # technically feasible in some cases, just not sanely supportable:
102       %w(TTIN TTOU WINCH).each do |sig|
103         trap(sig) { logger.info "SIG#{sig} is not handled by Zbatery" }
104       end
106       if ready_pipe
107         ready_pipe.syswrite($$.to_s)
108         ready_pipe.close
109         self.ready_pipe = nil
110       end
111       extend(Rainbows.const_get(@use))
112       worker = Worker.new(0, DeadIO.new)
113       before_fork.call(self, worker)
114       worker_loop(worker) # runs forever
115     end
117     def stop(graceful = true)
118       Rainbows::G.quit!
119       exit!(0) unless graceful
120     end
122     def before_fork
123       hook = super
124       hook == Zbatery::FORK_HOOK or
125         logger.warn "calling before_fork without forking"
126       hook
127     end
129     def after_fork
130       hook = super
131       hook == Zbatery::FORK_HOOK or
132         logger.warn "calling after_fork without having forked"
133       hook
134     end
135   end
138 Unicorn::Configurator::DEFAULTS[:before_fork] =
139   Unicorn::Configurator::DEFAULTS[:after_fork] = Zbatery::FORK_HOOK