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