mega-max-tool 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. checksums.yaml +7 -0
  2. data/mega-max-tool.gemspec +12 -0
  3. data/unicorn-6.1.0/Application_Timeouts +77 -0
  4. data/unicorn-6.1.0/CONTRIBUTORS +39 -0
  5. data/unicorn-6.1.0/COPYING +674 -0
  6. data/unicorn-6.1.0/DESIGN +95 -0
  7. data/unicorn-6.1.0/Documentation/unicorn.1 +222 -0
  8. data/unicorn-6.1.0/Documentation/unicorn_rails.1 +207 -0
  9. data/unicorn-6.1.0/FAQ +70 -0
  10. data/unicorn-6.1.0/GIT-VERSION-FILE +1 -0
  11. data/unicorn-6.1.0/GIT-VERSION-GEN +39 -0
  12. data/unicorn-6.1.0/GNUmakefile +313 -0
  13. data/unicorn-6.1.0/HACKING +112 -0
  14. data/unicorn-6.1.0/ISSUES +97 -0
  15. data/unicorn-6.1.0/KNOWN_ISSUES +79 -0
  16. data/unicorn-6.1.0/LATEST +24 -0
  17. data/unicorn-6.1.0/LICENSE +67 -0
  18. data/unicorn-6.1.0/Links +58 -0
  19. data/unicorn-6.1.0/NEWS +2890 -0
  20. data/unicorn-6.1.0/PHILOSOPHY +139 -0
  21. data/unicorn-6.1.0/README +151 -0
  22. data/unicorn-6.1.0/Rakefile +16 -0
  23. data/unicorn-6.1.0/SIGNALS +123 -0
  24. data/unicorn-6.1.0/Sandbox +104 -0
  25. data/unicorn-6.1.0/TODO +3 -0
  26. data/unicorn-6.1.0/TUNING +119 -0
  27. data/unicorn-6.1.0/archive/slrnpull.conf +4 -0
  28. data/unicorn-6.1.0/bin/unicorn +128 -0
  29. data/unicorn-6.1.0/bin/unicorn_rails +209 -0
  30. data/unicorn-6.1.0/examples/big_app_gc.rb +2 -0
  31. data/unicorn-6.1.0/examples/echo.ru +27 -0
  32. data/unicorn-6.1.0/examples/init.sh +102 -0
  33. data/unicorn-6.1.0/examples/logger_mp_safe.rb +25 -0
  34. data/unicorn-6.1.0/examples/logrotate.conf +44 -0
  35. data/unicorn-6.1.0/examples/nginx.conf +156 -0
  36. data/unicorn-6.1.0/examples/unicorn.conf.minimal.rb +13 -0
  37. data/unicorn-6.1.0/examples/unicorn.conf.rb +110 -0
  38. data/unicorn-6.1.0/examples/unicorn.socket +11 -0
  39. data/unicorn-6.1.0/examples/unicorn@.service +40 -0
  40. data/unicorn-6.1.0/ext/unicorn_http/CFLAGS +13 -0
  41. data/unicorn-6.1.0/ext/unicorn_http/c_util.h +116 -0
  42. data/unicorn-6.1.0/ext/unicorn_http/common_field_optimization.h +128 -0
  43. data/unicorn-6.1.0/ext/unicorn_http/epollexclusive.h +124 -0
  44. data/unicorn-6.1.0/ext/unicorn_http/ext_help.h +38 -0
  45. data/unicorn-6.1.0/ext/unicorn_http/extconf.rb +37 -0
  46. data/unicorn-6.1.0/ext/unicorn_http/global_variables.h +97 -0
  47. data/unicorn-6.1.0/ext/unicorn_http/httpdate.c +79 -0
  48. data/unicorn-6.1.0/ext/unicorn_http/unicorn_http.c +4319 -0
  49. data/unicorn-6.1.0/ext/unicorn_http/unicorn_http.rl +1025 -0
  50. data/unicorn-6.1.0/ext/unicorn_http/unicorn_http_common.rl +76 -0
  51. data/unicorn-6.1.0/lib/unicorn/app/old_rails/static.rb +59 -0
  52. data/unicorn-6.1.0/lib/unicorn/app/old_rails.rb +35 -0
  53. data/unicorn-6.1.0/lib/unicorn/cgi_wrapper.rb +147 -0
  54. data/unicorn-6.1.0/lib/unicorn/configurator.rb +743 -0
  55. data/unicorn-6.1.0/lib/unicorn/const.rb +21 -0
  56. data/unicorn-6.1.0/lib/unicorn/http_request.rb +201 -0
  57. data/unicorn-6.1.0/lib/unicorn/http_response.rb +61 -0
  58. data/unicorn-6.1.0/lib/unicorn/http_server.rb +893 -0
  59. data/unicorn-6.1.0/lib/unicorn/launcher.rb +62 -0
  60. data/unicorn-6.1.0/lib/unicorn/oob_gc.rb +81 -0
  61. data/unicorn-6.1.0/lib/unicorn/preread_input.rb +33 -0
  62. data/unicorn-6.1.0/lib/unicorn/select_waiter.rb +6 -0
  63. data/unicorn-6.1.0/lib/unicorn/socket_helper.rb +211 -0
  64. data/unicorn-6.1.0/lib/unicorn/stream_input.rb +147 -0
  65. data/unicorn-6.1.0/lib/unicorn/tee_input.rb +131 -0
  66. data/unicorn-6.1.0/lib/unicorn/tmpio.rb +33 -0
  67. data/unicorn-6.1.0/lib/unicorn/util.rb +90 -0
  68. data/unicorn-6.1.0/lib/unicorn/version.rb +1 -0
  69. data/unicorn-6.1.0/lib/unicorn/worker.rb +165 -0
  70. data/unicorn-6.1.0/lib/unicorn.rb +138 -0
  71. data/unicorn-6.1.0/man/man1/unicorn.1 +222 -0
  72. data/unicorn-6.1.0/man/man1/unicorn_rails.1 +207 -0
  73. data/unicorn-6.1.0/setup.rb +1586 -0
  74. data/unicorn-6.1.0/t/GNUmakefile +5 -0
  75. data/unicorn-6.1.0/t/README +42 -0
  76. data/unicorn-6.1.0/t/bin/content-md5-put +36 -0
  77. data/unicorn-6.1.0/t/bin/sha1sum.rb +17 -0
  78. data/unicorn-6.1.0/t/bin/unused_listen +40 -0
  79. data/unicorn-6.1.0/t/broken-app.ru +12 -0
  80. data/unicorn-6.1.0/t/detach.ru +11 -0
  81. data/unicorn-6.1.0/t/env.ru +3 -0
  82. data/unicorn-6.1.0/t/fails-rack-lint.ru +5 -0
  83. data/unicorn-6.1.0/t/heartbeat-timeout.ru +12 -0
  84. data/unicorn-6.1.0/t/listener_names.ru +4 -0
  85. data/unicorn-6.1.0/t/my-tap-lib.sh +201 -0
  86. data/unicorn-6.1.0/t/oob_gc.ru +20 -0
  87. data/unicorn-6.1.0/t/oob_gc_path.ru +20 -0
  88. data/unicorn-6.1.0/t/pid.ru +3 -0
  89. data/unicorn-6.1.0/t/preread_input.ru +17 -0
  90. data/unicorn-6.1.0/t/rack-input-tests.ru +21 -0
  91. data/unicorn-6.1.0/t/t0000-http-basic.sh +50 -0
  92. data/unicorn-6.1.0/t/t0001-reload-bad-config.sh +53 -0
  93. data/unicorn-6.1.0/t/t0002-config-conflict.sh +49 -0
  94. data/unicorn-6.1.0/t/t0002-parser-error.sh +94 -0
  95. data/unicorn-6.1.0/t/t0003-working_directory.sh +51 -0
  96. data/unicorn-6.1.0/t/t0004-heartbeat-timeout.sh +69 -0
  97. data/unicorn-6.1.0/t/t0004-working_directory_broken.sh +24 -0
  98. data/unicorn-6.1.0/t/t0005-working_directory_app.rb.sh +40 -0
  99. data/unicorn-6.1.0/t/t0006-reopen-logs.sh +83 -0
  100. data/unicorn-6.1.0/t/t0006.ru +13 -0
  101. data/unicorn-6.1.0/t/t0007-working_directory_no_embed_cli.sh +44 -0
  102. data/unicorn-6.1.0/t/t0008-back_out_of_upgrade.sh +110 -0
  103. data/unicorn-6.1.0/t/t0009-broken-app.sh +56 -0
  104. data/unicorn-6.1.0/t/t0009-winch_ttin.sh +59 -0
  105. data/unicorn-6.1.0/t/t0010-reap-logging.sh +55 -0
  106. data/unicorn-6.1.0/t/t0011-active-unix-socket.sh +79 -0
  107. data/unicorn-6.1.0/t/t0012-reload-empty-config.sh +86 -0
  108. data/unicorn-6.1.0/t/t0013-rewindable-input-false.sh +24 -0
  109. data/unicorn-6.1.0/t/t0013.ru +12 -0
  110. data/unicorn-6.1.0/t/t0014-rewindable-input-true.sh +24 -0
  111. data/unicorn-6.1.0/t/t0014.ru +12 -0
  112. data/unicorn-6.1.0/t/t0015-configurator-internals.sh +25 -0
  113. data/unicorn-6.1.0/t/t0018-write-on-close.sh +23 -0
  114. data/unicorn-6.1.0/t/t0019-max_header_len.sh +49 -0
  115. data/unicorn-6.1.0/t/t0020-at_exit-handler.sh +49 -0
  116. data/unicorn-6.1.0/t/t0021-process_detach.sh +29 -0
  117. data/unicorn-6.1.0/t/t0022-listener_names-preload_app.sh +32 -0
  118. data/unicorn-6.1.0/t/t0100-rack-input-tests.sh +124 -0
  119. data/unicorn-6.1.0/t/t0116-client_body_buffer_size.sh +80 -0
  120. data/unicorn-6.1.0/t/t0116.ru +16 -0
  121. data/unicorn-6.1.0/t/t0300-no-default-middleware.sh +20 -0
  122. data/unicorn-6.1.0/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
  123. data/unicorn-6.1.0/t/t0301.ru +13 -0
  124. data/unicorn-6.1.0/t/t9000-preread-input.sh +48 -0
  125. data/unicorn-6.1.0/t/t9001-oob_gc.sh +47 -0
  126. data/unicorn-6.1.0/t/t9002-oob_gc-path.sh +75 -0
  127. data/unicorn-6.1.0/t/test-lib.sh +129 -0
  128. data/unicorn-6.1.0/t/write-on-close.ru +11 -0
  129. data/unicorn-6.1.0/test/aggregate.rb +15 -0
  130. data/unicorn-6.1.0/test/benchmark/README +60 -0
  131. data/unicorn-6.1.0/test/benchmark/dd.ru +18 -0
  132. data/unicorn-6.1.0/test/benchmark/ddstream.ru +50 -0
  133. data/unicorn-6.1.0/test/benchmark/readinput.ru +40 -0
  134. data/unicorn-6.1.0/test/benchmark/stack.ru +8 -0
  135. data/unicorn-6.1.0/test/benchmark/uconnect.perl +66 -0
  136. data/unicorn-6.1.0/test/exec/README +5 -0
  137. data/unicorn-6.1.0/test/exec/test_exec.rb +1101 -0
  138. data/unicorn-6.1.0/test/test_helper.rb +306 -0
  139. data/unicorn-6.1.0/test/unit/test_ccc.rb +91 -0
  140. data/unicorn-6.1.0/test/unit/test_configurator.rb +175 -0
  141. data/unicorn-6.1.0/test/unit/test_droplet.rb +28 -0
  142. data/unicorn-6.1.0/test/unit/test_http_parser.rb +884 -0
  143. data/unicorn-6.1.0/test/unit/test_http_parser_ng.rb +714 -0
  144. data/unicorn-6.1.0/test/unit/test_request.rb +182 -0
  145. data/unicorn-6.1.0/test/unit/test_response.rb +111 -0
  146. data/unicorn-6.1.0/test/unit/test_server.rb +342 -0
  147. data/unicorn-6.1.0/test/unit/test_signals.rb +188 -0
  148. data/unicorn-6.1.0/test/unit/test_socket_helper.rb +201 -0
  149. data/unicorn-6.1.0/test/unit/test_stream_input.rb +203 -0
  150. data/unicorn-6.1.0/test/unit/test_tee_input.rb +304 -0
  151. data/unicorn-6.1.0/test/unit/test_upload.rb +301 -0
  152. data/unicorn-6.1.0/test/unit/test_util.rb +131 -0
  153. data/unicorn-6.1.0/test/unit/test_waiter.rb +34 -0
  154. data/unicorn-6.1.0/unicorn.gemspec +49 -0
  155. data/unicorn-6.1.0/unicorn_1 +1 -0
  156. data/unicorn-6.1.0/unicorn_rails_1 +1 -0
  157. metadata +196 -0
@@ -0,0 +1,893 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ # This is the process manager of Unicorn. This manages worker
4
+ # processes which in turn handle the I/O and application process.
5
+ # Listener sockets are started in the master process and shared with
6
+ # forked worker children.
7
+ #
8
+ # Users do not need to know the internals of this class, but reading the
9
+ # {source}[https://yhbt.net/unicorn.git/tree/lib/unicorn/http_server.rb]
10
+ # is education for programmers wishing to learn how unicorn works.
11
+ # See Unicorn::Configurator for information on how to configure unicorn.
12
+ class Unicorn::HttpServer
13
+ # :stopdoc:
14
+ attr_accessor :app, :timeout, :worker_processes,
15
+ :before_fork, :after_fork, :before_exec,
16
+ :listener_opts, :preload_app,
17
+ :orig_app, :config, :ready_pipe, :user,
18
+ :default_middleware, :early_hints
19
+ attr_writer :after_worker_exit, :after_worker_ready, :worker_exec
20
+
21
+ attr_reader :pid, :logger
22
+ include Unicorn::SocketHelper
23
+ include Unicorn::HttpResponse
24
+
25
+ # all bound listener sockets
26
+ # note: this is public used by raindrops, but not recommended for use
27
+ # in new projects
28
+ LISTENERS = []
29
+
30
+ # listeners we have yet to bind
31
+ NEW_LISTENERS = []
32
+
33
+ # :startdoc:
34
+ # We populate this at startup so we can figure out how to reexecute
35
+ # and upgrade the currently running instance of Unicorn
36
+ # This Hash is considered a stable interface and changing its contents
37
+ # will allow you to switch between different installations of Unicorn
38
+ # or even different installations of the same applications without
39
+ # downtime. Keys of this constant Hash are described as follows:
40
+ #
41
+ # * 0 - the path to the unicorn executable
42
+ # * :argv - a deep copy of the ARGV array the executable originally saw
43
+ # * :cwd - the working directory of the application, this is where
44
+ # you originally started Unicorn.
45
+ #
46
+ # To change your unicorn executable to a different path without downtime,
47
+ # you can set the following in your Unicorn config file, HUP and then
48
+ # continue with the traditional USR2 + QUIT upgrade steps:
49
+ #
50
+ # Unicorn::HttpServer::START_CTX[0] = "/home/bofh/2.3.0/bin/unicorn"
51
+ START_CTX = {
52
+ :argv => ARGV.map(&:dup),
53
+ 0 => $0.dup,
54
+ }
55
+ # We favor ENV['PWD'] since it is (usually) symlink aware for Capistrano
56
+ # and like systems
57
+ START_CTX[:cwd] = begin
58
+ a = File.stat(pwd = ENV['PWD'])
59
+ b = File.stat(Dir.pwd)
60
+ a.ino == b.ino && a.dev == b.dev ? pwd : Dir.pwd
61
+ rescue
62
+ Dir.pwd
63
+ end
64
+ # :stopdoc:
65
+
66
+ # Creates a working server on host:port (strange things happen if
67
+ # port isn't a Number). Use HttpServer::run to start the server and
68
+ # HttpServer.run.join to join the thread that's processing
69
+ # incoming requests on the socket.
70
+ def initialize(app, options = {})
71
+ @app = app
72
+ @reexec_pid = 0
73
+ @default_middleware = true
74
+ options = options.dup
75
+ @ready_pipe = options.delete(:ready_pipe)
76
+ @init_listeners = options[:listeners] ? options[:listeners].dup : []
77
+ options[:use_defaults] = true
78
+ self.config = Unicorn::Configurator.new(options)
79
+ self.listener_opts = {}
80
+
81
+ # We use @self_pipe differently in the master and worker processes:
82
+ #
83
+ # * The master process never closes or reinitializes this once
84
+ # initialized. Signal handlers in the master process will write to
85
+ # it to wake up the master from IO.select in exactly the same manner
86
+ # djb describes in https://cr.yp.to/docs/selfpipe.html
87
+ #
88
+ # * The workers immediately close the pipe they inherit. See the
89
+ # Unicorn::Worker class for the pipe workers use.
90
+ @self_pipe = []
91
+ @workers = {} # hash maps PIDs to Workers
92
+ @sig_queue = [] # signal queue used for self-piping
93
+ @pid = nil
94
+
95
+ # we try inheriting listeners first, so we bind them later.
96
+ # we don't write the pid file until we've bound listeners in case
97
+ # unicorn was started twice by mistake. Even though our #pid= method
98
+ # checks for stale/existing pid files, race conditions are still
99
+ # possible (and difficult/non-portable to avoid) and can be likely
100
+ # to clobber the pid if the second start was in quick succession
101
+ # after the first, so we rely on the listener binding to fail in
102
+ # that case. Some tests (in and outside of this source tree) and
103
+ # monitoring tools may also rely on pid files existing before we
104
+ # attempt to connect to the listener(s)
105
+ config.commit!(self, :skip => [:listeners, :pid])
106
+ @orig_app = app
107
+ # list of signals we care about and trap in master.
108
+ @queue_sigs = [
109
+ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
110
+
111
+ @worker_data = if worker_data = ENV['UNICORN_WORKER']
112
+ worker_data = worker_data.split(',').map!(&:to_i)
113
+ worker_data[1] = worker_data.slice!(1..2).map do |i|
114
+ Kgio::Pipe.for_fd(i)
115
+ end
116
+ worker_data
117
+ end
118
+ end
119
+
120
+ # Runs the thing. Returns self so you can run join on it
121
+ def start
122
+ inherit_listeners!
123
+ # this pipe is used to wake us up from select(2) in #join when signals
124
+ # are trapped. See trap_deferred.
125
+ @self_pipe.replace(Unicorn.pipe)
126
+ @master_pid = @worker_data ? Process.ppid : $$
127
+
128
+ # setup signal handlers before writing pid file in case people get
129
+ # trigger happy and send signals as soon as the pid file exists.
130
+ # Note that signals don't actually get handled until the #join method
131
+ @queue_sigs.each { |sig| trap(sig) { @sig_queue << sig; awaken_master } }
132
+ trap(:CHLD) { awaken_master }
133
+
134
+ # write pid early for Mongrel compatibility if we're not inheriting sockets
135
+ # This is needed for compatibility some Monit setups at least.
136
+ # This unfortunately has the side effect of clobbering valid PID if
137
+ # we upgrade and the upgrade breaks during preload_app==true && build_app!
138
+ self.pid = config[:pid]
139
+
140
+ build_app! if preload_app
141
+ bind_new_listeners!
142
+
143
+ spawn_missing_workers
144
+ self
145
+ end
146
+
147
+ # replaces current listener set with +listeners+. This will
148
+ # close the socket if it will not exist in the new listener set
149
+ def listeners=(listeners)
150
+ cur_names, dead_names = [], []
151
+ listener_names.each do |name|
152
+ if name.start_with?('/')
153
+ # mark unlinked sockets as dead so we can rebind them
154
+ (File.socket?(name) ? cur_names : dead_names) << name
155
+ else
156
+ cur_names << name
157
+ end
158
+ end
159
+ set_names = listener_names(listeners)
160
+ dead_names.concat(cur_names - set_names).uniq!
161
+
162
+ LISTENERS.delete_if do |io|
163
+ if dead_names.include?(sock_name(io))
164
+ (io.close rescue nil).nil? # true
165
+ else
166
+ set_server_sockopt(io, listener_opts[sock_name(io)])
167
+ false
168
+ end
169
+ end
170
+
171
+ (set_names - cur_names).each { |addr| listen(addr) }
172
+ end
173
+
174
+ def stdout_path=(path); redirect_io($stdout, path); end
175
+ def stderr_path=(path); redirect_io($stderr, path); end
176
+
177
+ def logger=(obj)
178
+ Unicorn::HttpRequest::DEFAULTS["rack.logger"] = @logger = obj
179
+ end
180
+
181
+ def clobber_pid(path)
182
+ unlink_pid_safe(@pid) if @pid
183
+ if path
184
+ fp = begin
185
+ tmp = "#{File.dirname(path)}/#{rand}.#$$"
186
+ File.open(tmp, File::RDWR|File::CREAT|File::EXCL, 0644)
187
+ rescue Errno::EEXIST
188
+ retry
189
+ end
190
+ fp.syswrite("#$$\n")
191
+ File.rename(fp.path, path)
192
+ fp.close
193
+ end
194
+ end
195
+
196
+ # sets the path for the PID file of the master process
197
+ def pid=(path)
198
+ if path
199
+ if x = valid_pid?(path)
200
+ return path if pid && path == pid && x == $$
201
+ if x == @reexec_pid && pid.end_with?('.oldbin')
202
+ logger.warn("will not set pid=#{path} while reexec-ed "\
203
+ "child is running PID:#{x}")
204
+ return
205
+ end
206
+ raise ArgumentError, "Already running on PID:#{x} " \
207
+ "(or pid=#{path} is stale)"
208
+ end
209
+ end
210
+
211
+ # rename the old pid if possible
212
+ if @pid && path
213
+ begin
214
+ File.rename(@pid, path)
215
+ rescue Errno::ENOENT, Errno::EXDEV
216
+ # a user may have accidentally removed the original,
217
+ # obviously cross-FS renames don't work, either.
218
+ clobber_pid(path)
219
+ end
220
+ else
221
+ clobber_pid(path)
222
+ end
223
+ @pid = path
224
+ end
225
+
226
+ # add a given address to the +listeners+ set, idempotently
227
+ # Allows workers to add a private, per-process listener via the
228
+ # after_fork hook. Very useful for debugging and testing.
229
+ # +:tries+ may be specified as an option for the number of times
230
+ # to retry, and +:delay+ may be specified as the time in seconds
231
+ # to delay between retries.
232
+ # A negative value for +:tries+ indicates the listen will be
233
+ # retried indefinitely, this is useful when workers belonging to
234
+ # different masters are spawned during a transparent upgrade.
235
+ def listen(address, opt = {}.merge(listener_opts[address] || {}))
236
+ address = config.expand_addr(address)
237
+ return if String === address && listener_names.include?(address)
238
+
239
+ delay = opt[:delay] || 0.5
240
+ tries = opt[:tries] || 5
241
+ begin
242
+ io = bind_listen(address, opt)
243
+ unless Kgio::TCPServer === io || Kgio::UNIXServer === io
244
+ io.autoclose = false
245
+ io = server_cast(io)
246
+ end
247
+ logger.info "listening on addr=#{sock_name(io)} fd=#{io.fileno}"
248
+ LISTENERS << io
249
+ io
250
+ rescue Errno::EADDRINUSE => err
251
+ logger.error "adding listener failed addr=#{address} (in use)"
252
+ raise err if tries == 0
253
+ tries -= 1
254
+ logger.error "retrying in #{delay} seconds " \
255
+ "(#{tries < 0 ? 'infinite' : tries} tries left)"
256
+ sleep(delay)
257
+ retry
258
+ rescue => err
259
+ logger.fatal "error adding listener addr=#{address}"
260
+ raise err
261
+ end
262
+ end
263
+
264
+ # monitors children and receives signals forever
265
+ # (or until a termination signal is sent). This handles signals
266
+ # one-at-a-time time and we'll happily drop signals in case somebody
267
+ # is signalling us too often.
268
+ def join
269
+ respawn = true
270
+ last_check = time_now
271
+
272
+ proc_name 'master'
273
+ logger.info "master process ready" # test_exec.rb relies on this message
274
+ if @ready_pipe
275
+ begin
276
+ @ready_pipe.syswrite($$.to_s)
277
+ rescue => e
278
+ logger.warn("grandparent died too soon?: #{e.message} (#{e.class})")
279
+ end
280
+ @ready_pipe = @ready_pipe.close rescue nil
281
+ end
282
+ begin
283
+ reap_all_workers
284
+ case @sig_queue.shift
285
+ when nil
286
+ # avoid murdering workers after our master process (or the
287
+ # machine) comes out of suspend/hibernation
288
+ if (last_check + @timeout) >= (last_check = time_now)
289
+ sleep_time = murder_lazy_workers
290
+ else
291
+ sleep_time = @timeout/2.0 + 1
292
+ @logger.debug("waiting #{sleep_time}s after suspend/hibernation")
293
+ end
294
+ maintain_worker_count if respawn
295
+ master_sleep(sleep_time)
296
+ when :QUIT # graceful shutdown
297
+ break
298
+ when :TERM, :INT # immediate shutdown
299
+ stop(false)
300
+ break
301
+ when :USR1 # rotate logs
302
+ logger.info "master reopening logs..."
303
+ Unicorn::Util.reopen_logs
304
+ logger.info "master done reopening logs"
305
+ soft_kill_each_worker(:USR1)
306
+ when :USR2 # exec binary, stay alive in case something went wrong
307
+ reexec
308
+ when :WINCH
309
+ if $stdin.tty?
310
+ logger.info "SIGWINCH ignored because we're not daemonized"
311
+ else
312
+ respawn = false
313
+ logger.info "gracefully stopping all workers"
314
+ soft_kill_each_worker(:QUIT)
315
+ self.worker_processes = 0
316
+ end
317
+ when :TTIN
318
+ respawn = true
319
+ self.worker_processes += 1
320
+ when :TTOU
321
+ self.worker_processes -= 1 if self.worker_processes > 0
322
+ when :HUP
323
+ respawn = true
324
+ if config.config_file
325
+ load_config!
326
+ else # exec binary and exit if there's no config file
327
+ logger.info "config_file not present, reexecuting binary"
328
+ reexec
329
+ end
330
+ end
331
+ rescue => e
332
+ Unicorn.log_error(@logger, "master loop error", e)
333
+ end while true
334
+ stop # gracefully shutdown all workers on our way out
335
+ logger.info "master complete"
336
+ unlink_pid_safe(pid) if pid
337
+ end
338
+
339
+ # Terminates all workers, but does not exit master process
340
+ def stop(graceful = true)
341
+ self.listeners = []
342
+ limit = time_now + timeout
343
+ until @workers.empty? || time_now > limit
344
+ if graceful
345
+ soft_kill_each_worker(:QUIT)
346
+ else
347
+ kill_each_worker(:TERM)
348
+ end
349
+ sleep(0.1)
350
+ reap_all_workers
351
+ end
352
+ kill_each_worker(:KILL)
353
+ end
354
+
355
+ def rewindable_input
356
+ Unicorn::HttpRequest.input_class.method_defined?(:rewind)
357
+ end
358
+
359
+ def rewindable_input=(bool)
360
+ Unicorn::HttpRequest.input_class = bool ?
361
+ Unicorn::TeeInput : Unicorn::StreamInput
362
+ end
363
+
364
+ def client_body_buffer_size
365
+ Unicorn::TeeInput.client_body_buffer_size
366
+ end
367
+
368
+ def client_body_buffer_size=(bytes)
369
+ Unicorn::TeeInput.client_body_buffer_size = bytes
370
+ end
371
+
372
+ def check_client_connection
373
+ Unicorn::HttpRequest.check_client_connection
374
+ end
375
+
376
+ def check_client_connection=(bool)
377
+ Unicorn::HttpRequest.check_client_connection = bool
378
+ end
379
+
380
+ private
381
+
382
+ # wait for a signal hander to wake us up and then consume the pipe
383
+ def master_sleep(sec)
384
+ @self_pipe[0].wait(sec) or return
385
+ # 11 bytes is the maximum string length which can be embedded within
386
+ # the Ruby itself and not require a separate malloc (on 32-bit MRI 1.9+).
387
+ # Most reads are only one byte here and uncommon, so it's not worth a
388
+ # persistent buffer, either:
389
+ @self_pipe[0].kgio_tryread(11)
390
+ end
391
+
392
+ def awaken_master
393
+ return if $$ != @master_pid
394
+ @self_pipe[1].kgio_trywrite('.') # wakeup master process from select
395
+ end
396
+
397
+ # reaps all unreaped workers
398
+ def reap_all_workers
399
+ begin
400
+ wpid, status = Process.waitpid2(-1, Process::WNOHANG)
401
+ wpid or return
402
+ if @reexec_pid == wpid
403
+ logger.error "reaped #{status.inspect} exec()-ed"
404
+ @reexec_pid = 0
405
+ self.pid = pid.chomp('.oldbin') if pid
406
+ proc_name 'master'
407
+ else
408
+ worker = @workers.delete(wpid) and worker.close rescue nil
409
+ @after_worker_exit.call(self, worker, status)
410
+ end
411
+ rescue Errno::ECHILD
412
+ break
413
+ end while true
414
+ end
415
+
416
+ # reexecutes the START_CTX with a new binary
417
+ def reexec
418
+ if @reexec_pid > 0
419
+ begin
420
+ Process.kill(0, @reexec_pid)
421
+ logger.error "reexec-ed child already running PID:#@reexec_pid"
422
+ return
423
+ rescue Errno::ESRCH
424
+ @reexec_pid = 0
425
+ end
426
+ end
427
+
428
+ if pid
429
+ old_pid = "#{pid}.oldbin"
430
+ begin
431
+ self.pid = old_pid # clear the path for a new pid file
432
+ rescue ArgumentError
433
+ logger.error "old PID:#{valid_pid?(old_pid)} running with " \
434
+ "existing pid=#{old_pid}, refusing rexec"
435
+ return
436
+ rescue => e
437
+ logger.error "error writing pid=#{old_pid} #{e.class} #{e.message}"
438
+ return
439
+ end
440
+ end
441
+
442
+ @reexec_pid = fork do
443
+ listener_fds = listener_sockets
444
+ ENV['UNICORN_FD'] = listener_fds.keys.join(',')
445
+ Dir.chdir(START_CTX[:cwd])
446
+ cmd = [ START_CTX[0] ].concat(START_CTX[:argv])
447
+
448
+ # avoid leaking FDs we don't know about, but let before_exec
449
+ # unset FD_CLOEXEC, if anything else in the app eventually
450
+ # relies on FD inheritence.
451
+ close_sockets_on_exec(listener_fds)
452
+
453
+ # exec(command, hash) works in at least 1.9.1+, but will only be
454
+ # required in 1.9.4/2.0.0 at earliest.
455
+ cmd << listener_fds
456
+ logger.info "executing #{cmd.inspect} (in #{Dir.pwd})"
457
+ before_exec.call(self)
458
+ exec(*cmd)
459
+ end
460
+ proc_name 'master (old)'
461
+ end
462
+
463
+ def worker_spawn(worker)
464
+ listener_fds = listener_sockets
465
+ env = {}
466
+ env['UNICORN_FD'] = listener_fds.keys.join(',')
467
+
468
+ listener_fds[worker.to_io.fileno] = worker.to_io
469
+ listener_fds[worker.master.fileno] = worker.master
470
+
471
+ worker_info = [worker.nr, worker.to_io.fileno, worker.master.fileno]
472
+ env['UNICORN_WORKER'] = worker_info.join(',')
473
+
474
+ close_sockets_on_exec(listener_fds)
475
+
476
+ Process.spawn(env, START_CTX[0], *START_CTX[:argv], listener_fds)
477
+ end
478
+
479
+ def listener_sockets
480
+ listener_fds = {}
481
+ LISTENERS.each do |sock|
482
+ sock.close_on_exec = false
483
+ listener_fds[sock.fileno] = sock
484
+ end
485
+ listener_fds
486
+ end
487
+
488
+ def close_sockets_on_exec(sockets)
489
+ (3..1024).each do |io|
490
+ next if sockets.include?(io)
491
+ io = IO.for_fd(io) rescue next
492
+ io.autoclose = false
493
+ io.close_on_exec = true
494
+ end
495
+ end
496
+
497
+ # forcibly terminate all workers that haven't checked in in timeout seconds. The timeout is implemented using an unlinked File
498
+ def murder_lazy_workers
499
+ next_sleep = @timeout - 1
500
+ now = time_now.to_i
501
+ @workers.dup.each_pair do |wpid, worker|
502
+ tick = worker.tick
503
+ 0 == tick and next # skip workers that haven't processed any clients
504
+ diff = now - tick
505
+ tmp = @timeout - diff
506
+ if tmp >= 0
507
+ next_sleep > tmp and next_sleep = tmp
508
+ next
509
+ end
510
+ next_sleep = 0
511
+ logger.error "worker=#{worker.nr} PID:#{wpid} timeout " \
512
+ "(#{diff}s > #{@timeout}s), killing"
513
+ kill_worker(:KILL, wpid) # take no prisoners for timeout violations
514
+ end
515
+ next_sleep <= 0 ? 1 : next_sleep
516
+ end
517
+
518
+ def after_fork_internal
519
+ @self_pipe.each(&:close).clear # this is master-only, now
520
+ @ready_pipe.close if @ready_pipe
521
+ Unicorn::Configurator::RACKUP.clear
522
+ @ready_pipe = @init_listeners = @before_exec = @before_fork = nil
523
+
524
+ # The OpenSSL PRNG is seeded with only the pid, and apps with frequently
525
+ # dying workers can recycle pids
526
+ OpenSSL::Random.seed(rand.to_s) if defined?(OpenSSL::Random)
527
+ end
528
+
529
+ def spawn_missing_workers
530
+ if @worker_data
531
+ worker = Unicorn::Worker.new(*@worker_data)
532
+ after_fork_internal
533
+ worker_loop(worker)
534
+ exit
535
+ end
536
+
537
+ worker_nr = -1
538
+ until (worker_nr += 1) == @worker_processes
539
+ @workers.value?(worker_nr) and next
540
+ worker = Unicorn::Worker.new(worker_nr)
541
+ before_fork.call(self, worker)
542
+
543
+ pid = @worker_exec ? worker_spawn(worker) : fork
544
+
545
+ unless pid
546
+ after_fork_internal
547
+ worker_loop(worker)
548
+ exit
549
+ end
550
+
551
+ @workers[pid] = worker
552
+ worker.atfork_parent
553
+ end
554
+ rescue => e
555
+ @logger.error(e) rescue nil
556
+ exit!
557
+ end
558
+
559
+ def maintain_worker_count
560
+ (off = @workers.size - worker_processes) == 0 and return
561
+ off < 0 and return spawn_missing_workers
562
+ @workers.each_value { |w| w.nr >= worker_processes and w.soft_kill(:QUIT) }
563
+ end
564
+
565
+ # if we get any error, try to write something back to the client
566
+ # assuming we haven't closed the socket, but don't get hung up
567
+ # if the socket is already closed or broken. We'll always ensure
568
+ # the socket is closed at the end of this function
569
+ def handle_error(client, e)
570
+ code = case e
571
+ when EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::ENOTCONN
572
+ # client disconnected on us and there's nothing we can do
573
+ when Unicorn::RequestURITooLongError
574
+ 414
575
+ when Unicorn::RequestEntityTooLargeError
576
+ 413
577
+ when Unicorn::HttpParserError # try to tell the client they're bad
578
+ 400
579
+ else
580
+ Unicorn.log_error(@logger, "app error", e)
581
+ 500
582
+ end
583
+ if code
584
+ client.kgio_trywrite(err_response(code, @request.response_start_sent))
585
+ end
586
+ client.close
587
+ rescue
588
+ end
589
+
590
+ def e103_response_write(client, headers)
591
+ response = if @request.response_start_sent
592
+ "103 Early Hints\r\n"
593
+ else
594
+ "HTTP/1.1 103 Early Hints\r\n"
595
+ end
596
+
597
+ headers.each_pair do |k, vs|
598
+ next if !vs || vs.empty?
599
+ values = vs.to_s.split("\n".freeze)
600
+ values.each do |v|
601
+ response << "#{k}: #{v}\r\n"
602
+ end
603
+ end
604
+ response << "\r\n".freeze
605
+ response << "HTTP/1.1 ".freeze if @request.response_start_sent
606
+ client.write(response)
607
+ end
608
+
609
+ def e100_response_write(client, env)
610
+ # We use String#freeze to avoid allocations under Ruby 2.1+
611
+ # Not many users hit this code path, so it's better to reduce the
612
+ # constant table sizes even for Ruby 2.0 users who'll hit extra
613
+ # allocations here.
614
+ client.write(@request.response_start_sent ?
615
+ "100 Continue\r\n\r\nHTTP/1.1 ".freeze :
616
+ "HTTP/1.1 100 Continue\r\n\r\n".freeze)
617
+ env.delete('HTTP_EXPECT'.freeze)
618
+ end
619
+
620
+ # once a client is accepted, it is processed in its entirety here
621
+ # in 3 easy steps: read request, call app, write app response
622
+ def process_client(client)
623
+ @request = Unicorn::HttpRequest.new
624
+ env = @request.read(client)
625
+
626
+ if early_hints
627
+ env["rack.early_hints"] = lambda do |headers|
628
+ e103_response_write(client, headers)
629
+ end
630
+ end
631
+
632
+ env["rack.after_reply"] = []
633
+
634
+ status, headers, body = @app.call(env)
635
+
636
+ begin
637
+ return if @request.hijacked?
638
+
639
+ if 100 == status.to_i
640
+ e100_response_write(client, env)
641
+ status, headers, body = @app.call(env)
642
+ return if @request.hijacked?
643
+ end
644
+ @request.headers? or headers = nil
645
+ http_response_write(client, status, headers, body, @request)
646
+ ensure
647
+ body.respond_to?(:close) and body.close
648
+ end
649
+
650
+ unless client.closed? # rack.hijack may've close this for us
651
+ client.shutdown # in case of fork() in Rack app
652
+ client.close # flush and uncork socket immediately, no keepalive
653
+ end
654
+ rescue => e
655
+ handle_error(client, e)
656
+ ensure
657
+ env["rack.after_reply"].each(&:call) if env
658
+ end
659
+
660
+ def nuke_listeners!(readers)
661
+ # only called from the worker, ordering is important here
662
+ tmp = readers.dup
663
+ readers.replace([false]) # ensure worker does not continue ASAP
664
+ tmp.each { |io| io.close rescue nil } # break out of IO.select
665
+ end
666
+
667
+ # gets rid of stuff the worker has no business keeping track of
668
+ # to free some resources and drops all sig handlers.
669
+ # traps for USR1, USR2, and HUP may be set in the after_fork Proc
670
+ # by the user.
671
+ def init_worker_process(worker)
672
+ worker.atfork_child
673
+ # we'll re-trap :QUIT later for graceful shutdown iff we accept clients
674
+ exit_sigs = [ :QUIT, :TERM, :INT ]
675
+ exit_sigs.each { |sig| trap(sig) { exit!(0) } }
676
+ exit!(0) if (@sig_queue & exit_sigs)[0]
677
+ (@queue_sigs - exit_sigs).each { |sig| trap(sig, nil) }
678
+ trap(:CHLD, 'DEFAULT')
679
+ @sig_queue.clear
680
+ proc_name "worker[#{worker.nr}]"
681
+ START_CTX.clear
682
+ @workers.clear
683
+
684
+ after_fork.call(self, worker) # can drop perms and create listeners
685
+ LISTENERS.each { |sock| sock.close_on_exec = true }
686
+
687
+ worker.user(*user) if user.kind_of?(Array) && ! worker.switched
688
+ @config = nil
689
+ build_app! unless preload_app
690
+ @after_fork = @listener_opts = @orig_app = nil
691
+ readers = LISTENERS.dup
692
+ readers << worker
693
+ trap(:QUIT) { nuke_listeners!(readers) }
694
+ readers
695
+ end
696
+
697
+ def reopen_worker_logs(worker_nr)
698
+ logger.info "worker=#{worker_nr} reopening logs..."
699
+ Unicorn::Util.reopen_logs
700
+ logger.info "worker=#{worker_nr} done reopening logs"
701
+ false
702
+ rescue => e
703
+ logger.error(e) rescue nil
704
+ exit!(77) # EX_NOPERM in sysexits.h
705
+ end
706
+
707
+ def prep_readers(readers)
708
+ wtr = Unicorn::Waiter.prep_readers(readers)
709
+ @timeout *= 500 # to milliseconds for epoll, but halved
710
+ wtr
711
+ rescue
712
+ require_relative 'select_waiter'
713
+ @timeout /= 2.0 # halved for IO.select
714
+ Unicorn::SelectWaiter.new
715
+ end
716
+
717
+ # runs inside each forked worker, this sits around and waits
718
+ # for connections and doesn't die until the parent dies (or is
719
+ # given a INT, QUIT, or TERM signal)
720
+ def worker_loop(worker)
721
+ readers = init_worker_process(worker)
722
+ waiter = prep_readers(readers)
723
+ reopen = false
724
+
725
+ # this only works immediately if the master sent us the signal
726
+ # (which is the normal case)
727
+ trap(:USR1) { reopen = true }
728
+
729
+ ready = readers.dup
730
+ @after_worker_ready.call(self, worker)
731
+
732
+ begin
733
+ reopen = reopen_worker_logs(worker.nr) if reopen
734
+ worker.tick = time_now.to_i
735
+ while sock = ready.shift
736
+ # Unicorn::Worker#kgio_tryaccept is not like accept(2) at all,
737
+ # but that will return false
738
+ if client = sock.kgio_tryaccept
739
+ process_client(client)
740
+ worker.tick = time_now.to_i
741
+ end
742
+ break if reopen
743
+ end
744
+
745
+ # timeout so we can .tick and keep parent from SIGKILL-ing us
746
+ worker.tick = time_now.to_i
747
+ waiter.get_readers(ready, readers, @timeout)
748
+ rescue => e
749
+ redo if reopen && readers[0]
750
+ Unicorn.log_error(@logger, "listen loop error", e) if readers[0]
751
+ end while readers[0]
752
+ end
753
+
754
+ # delivers a signal to a worker and fails gracefully if the worker
755
+ # is no longer running.
756
+ def kill_worker(signal, wpid)
757
+ Process.kill(signal, wpid)
758
+ rescue Errno::ESRCH
759
+ worker = @workers.delete(wpid) and worker.close rescue nil
760
+ end
761
+
762
+ # delivers a signal to each worker
763
+ def kill_each_worker(signal)
764
+ @workers.keys.each { |wpid| kill_worker(signal, wpid) }
765
+ end
766
+
767
+ def soft_kill_each_worker(signal)
768
+ @workers.each_value { |worker| worker.soft_kill(signal) }
769
+ end
770
+
771
+ # unlinks a PID file at given +path+ if it contains the current PID
772
+ # still potentially racy without locking the directory (which is
773
+ # non-portable and may interact badly with other programs), but the
774
+ # window for hitting the race condition is small
775
+ def unlink_pid_safe(path)
776
+ (File.read(path).to_i == $$ and File.unlink(path)) rescue nil
777
+ end
778
+
779
+ # returns a PID if a given path contains a non-stale PID file,
780
+ # nil otherwise.
781
+ def valid_pid?(path)
782
+ wpid = File.read(path).to_i
783
+ wpid <= 0 and return
784
+ Process.kill(0, wpid)
785
+ wpid
786
+ rescue Errno::EPERM
787
+ logger.info "pid=#{path} possibly stale, got EPERM signalling PID:#{wpid}"
788
+ nil
789
+ rescue Errno::ESRCH, Errno::ENOENT
790
+ # don't unlink stale pid files, racy without non-portable locking...
791
+ end
792
+
793
+ def load_config!
794
+ loaded_app = app
795
+ logger.info "reloading config_file=#{config.config_file}"
796
+ config[:listeners].replace(@init_listeners)
797
+ config.reload
798
+ config.commit!(self)
799
+ soft_kill_each_worker(:QUIT)
800
+ Unicorn::Util.reopen_logs
801
+ self.app = @orig_app
802
+ build_app! if preload_app
803
+ logger.info "done reloading config_file=#{config.config_file}"
804
+ rescue StandardError, LoadError, SyntaxError => e
805
+ Unicorn.log_error(@logger,
806
+ "error reloading config_file=#{config.config_file}", e)
807
+ self.app = loaded_app
808
+ end
809
+
810
+ # returns an array of string names for the given listener array
811
+ def listener_names(listeners = LISTENERS)
812
+ listeners.map { |io| sock_name(io) }
813
+ end
814
+
815
+ def build_app!
816
+ if app.respond_to?(:arity) && (app.arity == 0 || app.arity == 2)
817
+ if defined?(Gem) && Gem.respond_to?(:refresh)
818
+ logger.info "Refreshing Gem list"
819
+ Gem.refresh
820
+ end
821
+ self.app = app.arity == 0 ? app.call : app.call(nil, self)
822
+ end
823
+ end
824
+
825
+ def proc_name(tag)
826
+ $0 = ([ File.basename(START_CTX[0]), tag
827
+ ]).concat(START_CTX[:argv]).join(' ')
828
+ end
829
+
830
+ def redirect_io(io, path)
831
+ File.open(path, 'ab') { |fp| io.reopen(fp) } if path
832
+ io.sync = true
833
+ end
834
+
835
+ def inherit_listeners!
836
+ # inherit sockets from parents, they need to be plain Socket objects
837
+ # before they become Kgio::UNIXServer or Kgio::TCPServer
838
+ inherited = ENV['UNICORN_FD'].to_s.split(',')
839
+
840
+ # emulate sd_listen_fds() for systemd
841
+ sd_pid, sd_fds = ENV.values_at('LISTEN_PID', 'LISTEN_FDS')
842
+ if sd_pid.to_i == $$ # n.b. $$ can never be zero
843
+ # 3 = SD_LISTEN_FDS_START
844
+ inherited.concat((3...(3 + sd_fds.to_i)).to_a)
845
+ end
846
+ # to ease debugging, we will not unset LISTEN_PID and LISTEN_FDS
847
+
848
+ inherited.map! do |fd|
849
+ io = Socket.for_fd(fd.to_i)
850
+ io.autoclose = false
851
+ io = server_cast(io)
852
+ set_server_sockopt(io, listener_opts[sock_name(io)])
853
+ logger.info "inherited addr=#{sock_name(io)} fd=#{io.fileno}"
854
+ io
855
+ end
856
+
857
+ config_listeners = config[:listeners].dup
858
+ LISTENERS.replace(inherited)
859
+
860
+ # we start out with generic Socket objects that get cast to either
861
+ # Kgio::TCPServer or Kgio::UNIXServer objects; but since the Socket
862
+ # objects share the same OS-level file descriptor as the higher-level
863
+ # *Server objects; we need to prevent Socket objects from being
864
+ # garbage-collected
865
+ config_listeners -= listener_names
866
+ if config_listeners.empty? && LISTENERS.empty?
867
+ config_listeners << Unicorn::Const::DEFAULT_LISTEN
868
+ @init_listeners << Unicorn::Const::DEFAULT_LISTEN
869
+ START_CTX[:argv] << "-l#{Unicorn::Const::DEFAULT_LISTEN}"
870
+ end
871
+ NEW_LISTENERS.replace(config_listeners)
872
+ end
873
+
874
+ # call only after calling inherit_listeners!
875
+ # This binds any listeners we did NOT inherit from the parent
876
+ def bind_new_listeners!
877
+ NEW_LISTENERS.each { |addr| listen(addr) }.clear
878
+ raise ArgumentError, "no listeners" if LISTENERS.empty?
879
+ end
880
+
881
+ # try to use the monotonic clock in Ruby >= 2.1, it is immune to clock
882
+ # offset adjustments and generates less garbage (Float vs Time object)
883
+ begin
884
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
885
+ def time_now
886
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
887
+ end
888
+ rescue NameError, NoMethodError
889
+ def time_now # Ruby <= 2.0
890
+ Time.now
891
+ end
892
+ end
893
+ end