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