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