Zbatery 0.5.0 - Rainbows! 2.0.x sync
[zbatery.git] / lib / zbatery.rb
blob071658b26213eec837875789b292b9a5d4f05e8b
1 # -*- encoding: binary -*-
2 # :enddoc:
3 require 'rainbows'
5 module Zbatery
7   # version of Zbatery, currently 0.5.0
8   VERSION = "0.5.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::ProcessClient.const_set(:APP, @app)
41       logger.info "Zbatery #@use worker_connections=#@worker_connections"
42     end
43   end
45   # we can't/don't need to do the fchmod heartbeat Unicorn/Rainbows! does
46   def G.tick
47     alive
48   end
50   class HttpServer
52     # this class is only used to avoid breaking Unicorn user switching
53     class DeadIO
54       def chown(*args); end
55       alias fcntl chown
56     end
58     # only used if no concurrency model is specified
59     def worker_loop(worker)
60       init_worker_process(worker)
61       begin
62         ret = IO.select(LISTENERS, nil, nil, nil) and
63         ret[0].each do |sock|
64           io = sock.kgio_tryaccept and process_client(io)
65         end
66       rescue Errno::EINTR
67       rescue Errno::EBADF, TypeError
68         break
69       rescue => e
70         Rainbows::Error.listen_loop(e)
71       end while G.alive
72     end
74     # no-op
75     def maintain_worker_count; end
76     def init_self_pipe!; end
78     # can't just do a graceful exit if reopening logs fails, so we just
79     # continue on...
80     def reopen_logs
81       logger.info "reopening logs"
82       Unicorn::Util.reopen_logs
83       logger.info "done reopening logs"
84       rescue => e
85         logger.error "failed reopening logs #{e.message}"
86     end
88     def trap_deferred(sig)
89       # nothing
90     end
92     def join
93       trap(:INT) { stop(false) }
94       trap(:TERM) { stop(false) }
95       trap(:QUIT) { stop }
96       trap(:USR1) { reopen_logs }
97       trap(:USR2) { reexec }
98       trap(:HUP) { reexec; stop }
100       # technically feasible in some cases, just not sanely supportable:
101       %w(TTIN TTOU WINCH).each do |sig|
102         trap(sig) { logger.info "SIG#{sig} is not handled by Zbatery" }
103       end
105       if ready_pipe
106         ready_pipe.syswrite($$.to_s)
107         ready_pipe.close
108         self.ready_pipe = nil
109       end
110       extend(Rainbows.const_get(@use))
111       worker = Worker.new(0, DeadIO.new)
112       before_fork.call(self, worker)
113       worker_loop(worker) # runs forever
114     end
116     def stop(graceful = true)
117       Rainbows::G.quit!
118       exit!(0) unless graceful
119     end
121     def before_fork
122       hook = super
123       hook == Zbatery::FORK_HOOK or
124         logger.warn "calling before_fork without forking"
125       hook
126     end
128     def after_fork
129       hook = super
130       hook == Zbatery::FORK_HOOK or
131         logger.warn "calling after_fork without having forked"
132       hook
133     end
134   end
137 Unicorn::Configurator::DEFAULTS[:before_fork] =
138   Unicorn::Configurator::DEFAULTS[:after_fork] = Zbatery::FORK_HOOK