unicorn-maintained 6.2.0

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