gitlab-puma 4.3.1.gitlab.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +1537 -0
  3. data/LICENSE +26 -0
  4. data/README.md +291 -0
  5. data/bin/puma +10 -0
  6. data/bin/puma-wild +31 -0
  7. data/bin/pumactl +12 -0
  8. data/docs/architecture.md +37 -0
  9. data/docs/deployment.md +111 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/nginx.md +80 -0
  14. data/docs/plugins.md +38 -0
  15. data/docs/restart.md +41 -0
  16. data/docs/signals.md +96 -0
  17. data/docs/systemd.md +290 -0
  18. data/docs/tcp_mode.md +96 -0
  19. data/ext/puma_http11/PumaHttp11Service.java +19 -0
  20. data/ext/puma_http11/ext_help.h +15 -0
  21. data/ext/puma_http11/extconf.rb +28 -0
  22. data/ext/puma_http11/http11_parser.c +1044 -0
  23. data/ext/puma_http11/http11_parser.h +65 -0
  24. data/ext/puma_http11/http11_parser.java.rl +145 -0
  25. data/ext/puma_http11/http11_parser.rl +147 -0
  26. data/ext/puma_http11/http11_parser_common.rl +54 -0
  27. data/ext/puma_http11/io_buffer.c +155 -0
  28. data/ext/puma_http11/mini_ssl.c +553 -0
  29. data/ext/puma_http11/org/jruby/puma/Http11.java +226 -0
  30. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +455 -0
  31. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
  32. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +363 -0
  33. data/ext/puma_http11/puma_http11.c +502 -0
  34. data/lib/puma.rb +31 -0
  35. data/lib/puma/accept_nonblock.rb +29 -0
  36. data/lib/puma/app/status.rb +80 -0
  37. data/lib/puma/binder.rb +385 -0
  38. data/lib/puma/cli.rb +239 -0
  39. data/lib/puma/client.rb +494 -0
  40. data/lib/puma/cluster.rb +554 -0
  41. data/lib/puma/commonlogger.rb +108 -0
  42. data/lib/puma/configuration.rb +362 -0
  43. data/lib/puma/const.rb +242 -0
  44. data/lib/puma/control_cli.rb +289 -0
  45. data/lib/puma/detect.rb +15 -0
  46. data/lib/puma/dsl.rb +740 -0
  47. data/lib/puma/events.rb +156 -0
  48. data/lib/puma/io_buffer.rb +4 -0
  49. data/lib/puma/jruby_restart.rb +84 -0
  50. data/lib/puma/launcher.rb +475 -0
  51. data/lib/puma/minissl.rb +278 -0
  52. data/lib/puma/minissl/context_builder.rb +76 -0
  53. data/lib/puma/null_io.rb +44 -0
  54. data/lib/puma/plugin.rb +120 -0
  55. data/lib/puma/plugin/tmp_restart.rb +36 -0
  56. data/lib/puma/rack/builder.rb +301 -0
  57. data/lib/puma/rack/urlmap.rb +93 -0
  58. data/lib/puma/rack_default.rb +9 -0
  59. data/lib/puma/reactor.rb +400 -0
  60. data/lib/puma/runner.rb +192 -0
  61. data/lib/puma/server.rb +1053 -0
  62. data/lib/puma/single.rb +123 -0
  63. data/lib/puma/state_file.rb +31 -0
  64. data/lib/puma/tcp_logger.rb +41 -0
  65. data/lib/puma/thread_pool.rb +348 -0
  66. data/lib/puma/util.rb +124 -0
  67. data/lib/rack/handler/puma.rb +115 -0
  68. data/tools/docker/Dockerfile +16 -0
  69. data/tools/jungle/README.md +19 -0
  70. data/tools/jungle/init.d/README.md +61 -0
  71. data/tools/jungle/init.d/puma +421 -0
  72. data/tools/jungle/init.d/run-puma +18 -0
  73. data/tools/jungle/rc.d/README.md +74 -0
  74. data/tools/jungle/rc.d/puma +61 -0
  75. data/tools/jungle/rc.d/puma.conf +10 -0
  76. data/tools/jungle/upstart/README.md +61 -0
  77. data/tools/jungle/upstart/puma-manager.conf +31 -0
  78. data/tools/jungle/upstart/puma.conf +69 -0
  79. data/tools/trickletest.rb +44 -0
  80. metadata +147 -0
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puma/const'
4
+ require "puma/null_io"
5
+ require 'stringio'
6
+
7
+ module Puma
8
+ # The default implement of an event sink object used by Server
9
+ # for when certain kinds of events occur in the life of the server.
10
+ #
11
+ # The methods available are the events that the Server fires.
12
+ #
13
+ class Events
14
+ class DefaultFormatter
15
+ def call(str)
16
+ str
17
+ end
18
+ end
19
+
20
+ class PidFormatter
21
+ def call(str)
22
+ "[#{$$}] #{str}"
23
+ end
24
+ end
25
+
26
+ include Const
27
+
28
+ # Create an Events object that prints to +stdout+ and +stderr+.
29
+ #
30
+ def initialize(stdout, stderr)
31
+ @formatter = DefaultFormatter.new
32
+ @stdout = stdout
33
+ @stderr = stderr
34
+
35
+ @stdout.sync = true
36
+ @stderr.sync = true
37
+
38
+ @debug = ENV.key? 'PUMA_DEBUG'
39
+
40
+ @hooks = Hash.new { |h,k| h[k] = [] }
41
+ end
42
+
43
+ attr_reader :stdout, :stderr
44
+ attr_accessor :formatter
45
+
46
+ # Fire callbacks for the named hook
47
+ #
48
+ def fire(hook, *args)
49
+ @hooks[hook].each { |t| t.call(*args) }
50
+ end
51
+
52
+ # Register a callback for a given hook
53
+ #
54
+ def register(hook, obj=nil, &blk)
55
+ if obj and blk
56
+ raise "Specify either an object or a block, not both"
57
+ end
58
+
59
+ h = obj || blk
60
+
61
+ @hooks[hook] << h
62
+
63
+ h
64
+ end
65
+
66
+ # Write +str+ to +@stdout+
67
+ #
68
+ def log(str)
69
+ @stdout.puts format(str)
70
+ end
71
+
72
+ def write(str)
73
+ @stdout.write format(str)
74
+ end
75
+
76
+ def debug(str)
77
+ log("% #{str}") if @debug
78
+ end
79
+
80
+ # Write +str+ to +@stderr+
81
+ #
82
+ def error(str)
83
+ @stderr.puts format("ERROR: #{str}")
84
+ exit 1
85
+ end
86
+
87
+ def format(str)
88
+ formatter.call(str)
89
+ end
90
+
91
+ # An HTTP parse error has occurred.
92
+ # +server+ is the Server object, +env+ the request, and +error+ a
93
+ # parsing exception.
94
+ #
95
+ def parse_error(server, env, error)
96
+ @stderr.puts "#{Time.now}: HTTP parse error, malformed request " \
97
+ "(#{env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR]}#{env[REQUEST_PATH]}): " \
98
+ "#{error.inspect}" \
99
+ "\n---\n"
100
+ end
101
+
102
+ # An SSL error has occurred.
103
+ # +server+ is the Server object, +peeraddr+ peer address, +peercert+
104
+ # any peer certificate (if present), and +error+ an exception object.
105
+ #
106
+ def ssl_error(server, peeraddr, peercert, error)
107
+ subject = peercert ? peercert.subject : nil
108
+ @stderr.puts "#{Time.now}: SSL error, peer: #{peeraddr}, peer cert: #{subject}, #{error.inspect}"
109
+ end
110
+
111
+ # An unknown error has occurred.
112
+ # +server+ is the Server object, +error+ an exception object,
113
+ # +kind+ some additional info, and +env+ the request.
114
+ #
115
+ def unknown_error(server, error, kind="Unknown", env=nil)
116
+ if error.respond_to? :render
117
+ error.render "#{Time.now}: #{kind} error", @stderr
118
+ else
119
+ if env
120
+ string_block = [ "#{Time.now}: #{kind} error handling request { #{env['REQUEST_METHOD']} #{env['PATH_INFO']} }" ]
121
+ string_block << error.inspect
122
+ else
123
+ string_block = [ "#{Time.now}: #{kind} error: #{error.inspect}" ]
124
+ end
125
+ string_block << error.backtrace
126
+ @stderr.puts string_block.join("\n")
127
+ end
128
+ end
129
+
130
+ def on_booted(&block)
131
+ register(:on_booted, &block)
132
+ end
133
+
134
+ def fire_on_booted!
135
+ fire(:on_booted)
136
+ end
137
+
138
+ DEFAULT = new(STDOUT, STDERR)
139
+
140
+ # Returns an Events object which writes its status to 2 StringIO
141
+ # objects.
142
+ #
143
+ def self.strings
144
+ Events.new StringIO.new, StringIO.new
145
+ end
146
+
147
+ def self.stdio
148
+ Events.new $stdout, $stderr
149
+ end
150
+
151
+ def self.null
152
+ n = NullIO.new
153
+ Events.new n, n
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puma/detect'
4
+ require 'puma/puma_http11'
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ffi'
4
+
5
+ module Puma
6
+ module JRubyRestart
7
+ extend FFI::Library
8
+ ffi_lib 'c'
9
+
10
+ attach_function :execlp, [:string, :varargs], :int
11
+ attach_function :chdir, [:string], :int
12
+ attach_function :fork, [], :int
13
+ attach_function :exit, [:int], :void
14
+ attach_function :setsid, [], :int
15
+
16
+ def self.chdir_exec(dir, argv)
17
+ chdir(dir)
18
+ cmd = argv.first
19
+ argv = ([:string] * argv.size).zip(argv).flatten
20
+ argv << :string
21
+ argv << nil
22
+ execlp(cmd, *argv)
23
+ raise SystemCallError.new(FFI.errno)
24
+ end
25
+
26
+ PermKey = 'PUMA_DAEMON_PERM'
27
+ RestartKey = 'PUMA_DAEMON_RESTART'
28
+
29
+ # Called to tell things "Your now always in daemon mode,
30
+ # don't try to reenter it."
31
+ #
32
+ def self.perm_daemonize
33
+ ENV[PermKey] = "1"
34
+ end
35
+
36
+ def self.daemon?
37
+ ENV.key?(PermKey) || ENV.key?(RestartKey)
38
+ end
39
+
40
+ def self.daemon_init
41
+ return true if ENV.key?(PermKey)
42
+
43
+ return false unless ENV.key? RestartKey
44
+
45
+ master = ENV[RestartKey]
46
+
47
+ # In case the master disappears early
48
+ begin
49
+ Process.kill "SIGUSR2", master.to_i
50
+ rescue SystemCallError => e
51
+ end
52
+
53
+ ENV[RestartKey] = ""
54
+
55
+ setsid
56
+
57
+ null = File.open "/dev/null", "w+"
58
+ STDIN.reopen null
59
+ STDOUT.reopen null
60
+ STDERR.reopen null
61
+
62
+ true
63
+ end
64
+
65
+ def self.daemon_start(dir, argv)
66
+ ENV[RestartKey] = Process.pid.to_s
67
+
68
+ if k = ENV['PUMA_JRUBY_DAEMON_OPTS']
69
+ ENV['JRUBY_OPTS'] = k
70
+ end
71
+
72
+ cmd = argv.first
73
+ argv = ([:string] * argv.size).zip(argv).flatten
74
+ argv << :string
75
+ argv << nil
76
+
77
+ chdir(dir)
78
+ ret = fork
79
+ return ret if ret != 0
80
+ execlp(cmd, *argv)
81
+ raise SystemCallError.new(FFI.errno)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,475 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puma/events'
4
+ require 'puma/detect'
5
+ require 'puma/cluster'
6
+ require 'puma/single'
7
+ require 'puma/const'
8
+ require 'puma/binder'
9
+
10
+ module Puma
11
+ # Puma::Launcher is the single entry point for starting a Puma server based on user
12
+ # configuration. It is responsible for taking user supplied arguments and resolving them
13
+ # with configuration in `config/puma.rb` or `config/puma/<env>.rb`.
14
+ #
15
+ # It is responsible for either launching a cluster of Puma workers or a single
16
+ # puma server.
17
+ class Launcher
18
+ KEYS_NOT_TO_PERSIST_IN_STATE = [
19
+ :logger, :lowlevel_error_handler,
20
+ :before_worker_shutdown, :before_worker_boot, :before_worker_fork,
21
+ :after_worker_boot, :before_fork, :on_restart
22
+ ]
23
+ # Returns an instance of Launcher
24
+ #
25
+ # +conf+ A Puma::Configuration object indicating how to run the server.
26
+ #
27
+ # +launcher_args+ A Hash that currently has one required key `:events`,
28
+ # this is expected to hold an object similar to an `Puma::Events.stdio`,
29
+ # this object will be responsible for broadcasting Puma's internal state
30
+ # to a logging destination. An optional key `:argv` can be supplied,
31
+ # this should be an array of strings, these arguments are re-used when
32
+ # restarting the puma server.
33
+ #
34
+ # Examples:
35
+ #
36
+ # conf = Puma::Configuration.new do |user_config|
37
+ # user_config.threads 1, 10
38
+ # user_config.app do |env|
39
+ # [200, {}, ["hello world"]]
40
+ # end
41
+ # end
42
+ # Puma::Launcher.new(conf, events: Puma::Events.stdio).run
43
+ def initialize(conf, launcher_args={})
44
+ @runner = nil
45
+ @events = launcher_args[:events] || Events::DEFAULT
46
+ @argv = launcher_args[:argv] || []
47
+ @original_argv = @argv.dup
48
+ @config = conf
49
+
50
+ @binder = Binder.new(@events)
51
+ @binder.import_from_env
52
+
53
+ @environment = conf.environment
54
+
55
+ # Advertise the Configuration
56
+ Puma.cli_config = @config if defined?(Puma.cli_config)
57
+
58
+ @config.load
59
+
60
+ @options = @config.options
61
+ @config.clamp
62
+
63
+ @events.formatter = Events::PidFormatter.new if clustered?
64
+ @events.formatter = options[:log_formatter] if @options[:log_formatter]
65
+
66
+ generate_restart_data
67
+
68
+ if clustered? && !Process.respond_to?(:fork)
69
+ unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform"
70
+ end
71
+
72
+ if @options[:daemon] && Puma.windows?
73
+ unsupported 'daemon mode not supported on Windows'
74
+ end
75
+
76
+ Dir.chdir(@restart_dir)
77
+
78
+ prune_bundler if prune_bundler?
79
+
80
+ @environment = @options[:environment] if @options[:environment]
81
+ set_rack_environment
82
+
83
+ if clustered?
84
+ @options[:logger] = @events
85
+
86
+ @runner = Cluster.new(self, @events)
87
+ else
88
+ @runner = Single.new(self, @events)
89
+ end
90
+ Puma.stats_object = @runner
91
+
92
+ @status = :run
93
+ end
94
+
95
+ attr_reader :binder, :events, :config, :options, :restart_dir
96
+
97
+ # Return stats about the server
98
+ def stats
99
+ @runner.stats
100
+ end
101
+
102
+ # Write a state file that can be used by pumactl to control
103
+ # the server
104
+ def write_state
105
+ write_pid
106
+
107
+ path = @options[:state]
108
+ return unless path
109
+
110
+ require 'puma/state_file'
111
+
112
+ sf = StateFile.new
113
+ sf.pid = Process.pid
114
+ sf.control_url = @options[:control_url]
115
+ sf.control_auth_token = @options[:control_auth_token]
116
+
117
+ sf.save path
118
+ end
119
+
120
+ # Delete the configured pidfile
121
+ def delete_pidfile
122
+ path = @options[:pidfile]
123
+ File.unlink(path) if path && File.exist?(path)
124
+ end
125
+
126
+ # Begin async shutdown of the server
127
+ def halt
128
+ @status = :halt
129
+ @runner.halt
130
+ end
131
+
132
+ # Begin async shutdown of the server gracefully
133
+ def stop
134
+ @status = :stop
135
+ @runner.stop
136
+ end
137
+
138
+ # Begin async restart of the server
139
+ def restart
140
+ @status = :restart
141
+ @runner.restart
142
+ end
143
+
144
+ # Begin a phased restart if supported
145
+ def phased_restart
146
+ unless @runner.respond_to?(:phased_restart) and @runner.phased_restart
147
+ log "* phased-restart called but not available, restarting normally."
148
+ return restart
149
+ end
150
+ true
151
+ end
152
+
153
+ # Run the server. This blocks until the server is stopped
154
+ def run
155
+ previous_env =
156
+ if defined?(Bundler)
157
+ env = Bundler::ORIGINAL_ENV.dup
158
+ # add -rbundler/setup so we load from Gemfile when restarting
159
+ bundle = "-rbundler/setup"
160
+ env["RUBYOPT"] = [env["RUBYOPT"], bundle].join(" ").lstrip unless env["RUBYOPT"].to_s.include?(bundle)
161
+ env
162
+ else
163
+ ENV.to_h
164
+ end
165
+
166
+ @config.clamp
167
+
168
+ @config.plugins.fire_starts self
169
+
170
+ setup_signals
171
+ set_process_title
172
+ @runner.run
173
+
174
+ case @status
175
+ when :halt
176
+ log "* Stopping immediately!"
177
+ when :run, :stop
178
+ graceful_stop
179
+ when :restart
180
+ log "* Restarting..."
181
+ ENV.replace(previous_env)
182
+ @runner.before_restart
183
+ restart!
184
+ when :exit
185
+ # nothing
186
+ end
187
+ @binder.close_unix_paths
188
+ end
189
+
190
+ # Return which tcp port the launcher is using, if it's using TCP
191
+ def connected_port
192
+ @binder.connected_port
193
+ end
194
+
195
+ def restart_args
196
+ cmd = @options[:restart_cmd]
197
+ if cmd
198
+ cmd.split(' ') + @original_argv
199
+ else
200
+ @restart_argv
201
+ end
202
+ end
203
+
204
+ def close_binder_listeners
205
+ @binder.close_listeners
206
+ end
207
+
208
+ private
209
+
210
+ # If configured, write the pid of the current process out
211
+ # to a file.
212
+ def write_pid
213
+ path = @options[:pidfile]
214
+ return unless path
215
+
216
+ File.open(path, 'w') { |f| f.puts Process.pid }
217
+ cur = Process.pid
218
+ at_exit do
219
+ delete_pidfile if cur == Process.pid
220
+ end
221
+ end
222
+
223
+ def reload_worker_directory
224
+ @runner.reload_worker_directory if @runner.respond_to?(:reload_worker_directory)
225
+ end
226
+
227
+ def restart!
228
+ @config.run_hooks :on_restart, self
229
+
230
+ if Puma.jruby?
231
+ close_binder_listeners
232
+
233
+ require 'puma/jruby_restart'
234
+ JRubyRestart.chdir_exec(@restart_dir, restart_args)
235
+ elsif Puma.windows?
236
+ close_binder_listeners
237
+
238
+ argv = restart_args
239
+ Dir.chdir(@restart_dir)
240
+ Kernel.exec(*argv)
241
+ else
242
+ argv = restart_args
243
+ Dir.chdir(@restart_dir)
244
+ argv += [@binder.redirects_for_restart]
245
+ Kernel.exec(*argv)
246
+ end
247
+ end
248
+
249
+ def dependencies_and_files_to_require_after_prune
250
+ puma = spec_for_gem("puma")
251
+
252
+ deps = puma.runtime_dependencies.map do |d|
253
+ "#{d.name}:#{spec_for_gem(d.name).version}"
254
+ end
255
+
256
+ [deps, require_paths_for_gem(puma) + extra_runtime_deps_directories]
257
+ end
258
+
259
+ def extra_runtime_deps_directories
260
+ Array(@options[:extra_runtime_dependencies]).map do |d_name|
261
+ if (spec = spec_for_gem(d_name))
262
+ require_paths_for_gem(spec)
263
+ else
264
+ log "* Could not load extra dependency: #{d_name}"
265
+ nil
266
+ end
267
+ end.flatten.compact
268
+ end
269
+
270
+ def puma_wild_location
271
+ puma = spec_for_gem("puma")
272
+ dirs = require_paths_for_gem(puma)
273
+ puma_lib_dir = dirs.detect { |x| File.exist? File.join(x, '../bin/puma-wild') }
274
+ File.expand_path(File.join(puma_lib_dir, "../bin/puma-wild"))
275
+ end
276
+
277
+ def prune_bundler
278
+ return unless defined?(Bundler)
279
+ require_rubygems_min_version!(Gem::Version.new("2.2"), "prune_bundler")
280
+ unless puma_wild_location
281
+ log "! Unable to prune Bundler environment, continuing"
282
+ return
283
+ end
284
+
285
+ deps, dirs = dependencies_and_files_to_require_after_prune
286
+
287
+ log '* Pruning Bundler environment'
288
+ home = ENV['GEM_HOME']
289
+ Bundler.with_clean_env do
290
+ ENV['GEM_HOME'] = home
291
+ ENV['PUMA_BUNDLER_PRUNED'] = '1'
292
+ args = [Gem.ruby, puma_wild_location, '-I', dirs.join(':'), deps.join(',')] + @original_argv
293
+ # Ruby 2.0+ defaults to true which breaks socket activation
294
+ args += [{:close_others => false}]
295
+ Kernel.exec(*args)
296
+ end
297
+ end
298
+
299
+ def spec_for_gem(gem_name)
300
+ Bundler.rubygems.loaded_specs(gem_name)
301
+ end
302
+
303
+ def require_paths_for_gem(gem_spec)
304
+ gem_spec.full_require_paths
305
+ end
306
+
307
+ def log(str)
308
+ @events.log str
309
+ end
310
+
311
+ def clustered?
312
+ (@options[:workers] || 0) > 0
313
+ end
314
+
315
+ def unsupported(str)
316
+ @events.error(str)
317
+ raise UnsupportedOption
318
+ end
319
+
320
+ def graceful_stop
321
+ @runner.stop_blocked
322
+ log "=== puma shutdown: #{Time.now} ==="
323
+ log "- Goodbye!"
324
+ end
325
+
326
+ def log_thread_status
327
+ Thread.list.each do |thread|
328
+ log "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}"
329
+ logstr = "Thread: TID-#{thread.object_id.to_s(36)}"
330
+ logstr += " #{thread.name}" if thread.respond_to?(:name)
331
+ log logstr
332
+
333
+ if thread.backtrace
334
+ log thread.backtrace.join("\n")
335
+ else
336
+ log "<no backtrace available>"
337
+ end
338
+ end
339
+ end
340
+
341
+ def set_process_title
342
+ Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
343
+ end
344
+
345
+ def title
346
+ buffer = "puma #{Puma::Const::VERSION} (#{@options[:binds].join(',')})"
347
+ buffer += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
348
+ buffer
349
+ end
350
+
351
+ def set_rack_environment
352
+ @options[:environment] = environment
353
+ ENV['RACK_ENV'] = environment
354
+ end
355
+
356
+ def environment
357
+ @environment
358
+ end
359
+
360
+ def prune_bundler?
361
+ @options[:prune_bundler] && clustered? && !@options[:preload_app]
362
+ end
363
+
364
+ def generate_restart_data
365
+ if dir = @options[:directory]
366
+ @restart_dir = dir
367
+
368
+ elsif Puma.windows?
369
+ # I guess the value of PWD is garbage on windows so don't bother
370
+ # using it.
371
+ @restart_dir = Dir.pwd
372
+
373
+ # Use the same trick as unicorn, namely favor PWD because
374
+ # it will contain an unresolved symlink, useful for when
375
+ # the pwd is /data/releases/current.
376
+ elsif dir = ENV['PWD']
377
+ s_env = File.stat(dir)
378
+ s_pwd = File.stat(Dir.pwd)
379
+
380
+ if s_env.ino == s_pwd.ino and (Puma.jruby? or s_env.dev == s_pwd.dev)
381
+ @restart_dir = dir
382
+ end
383
+ end
384
+
385
+ @restart_dir ||= Dir.pwd
386
+
387
+ # if $0 is a file in the current directory, then restart
388
+ # it the same, otherwise add -S on there because it was
389
+ # picked up in PATH.
390
+ #
391
+ if File.exist?($0)
392
+ arg0 = [Gem.ruby, $0]
393
+ else
394
+ arg0 = [Gem.ruby, "-S", $0]
395
+ end
396
+
397
+ # Detect and reinject -Ilib from the command line, used for testing without bundler
398
+ # cruby has an expanded path, jruby has just "lib"
399
+ lib = File.expand_path "lib"
400
+ arg0[1,0] = ["-I", lib] if [lib, "lib"].include?($LOAD_PATH[0])
401
+
402
+ if defined? Puma::WILD_ARGS
403
+ @restart_argv = arg0 + Puma::WILD_ARGS + @original_argv
404
+ else
405
+ @restart_argv = arg0 + @original_argv
406
+ end
407
+ end
408
+
409
+ def setup_signals
410
+ begin
411
+ Signal.trap "SIGUSR2" do
412
+ restart
413
+ end
414
+ rescue Exception
415
+ log "*** SIGUSR2 not implemented, signal based restart unavailable!"
416
+ end
417
+
418
+ unless Puma.jruby?
419
+ begin
420
+ Signal.trap "SIGUSR1" do
421
+ phased_restart
422
+ end
423
+ rescue Exception
424
+ log "*** SIGUSR1 not implemented, signal based restart unavailable!"
425
+ end
426
+ end
427
+
428
+ begin
429
+ Signal.trap "SIGTERM" do
430
+ graceful_stop
431
+
432
+ raise(SignalException, "SIGTERM") if @options[:raise_exception_on_sigterm]
433
+ end
434
+ rescue Exception
435
+ log "*** SIGTERM not implemented, signal based gracefully stopping unavailable!"
436
+ end
437
+
438
+ begin
439
+ Signal.trap "SIGINT" do
440
+ stop
441
+ end
442
+ rescue Exception
443
+ log "*** SIGINT not implemented, signal based gracefully stopping unavailable!"
444
+ end
445
+
446
+ begin
447
+ Signal.trap "SIGHUP" do
448
+ if @runner.redirected_io?
449
+ @runner.redirect_io
450
+ else
451
+ stop
452
+ end
453
+ end
454
+ rescue Exception
455
+ log "*** SIGHUP not implemented, signal based logs reopening unavailable!"
456
+ end
457
+
458
+ begin
459
+ Signal.trap "SIGINFO" do
460
+ log_thread_status
461
+ end
462
+ rescue Exception
463
+ # Not going to log this one, as SIGINFO is *BSD only and would be pretty annoying
464
+ # to see this constantly on Linux.
465
+ end
466
+ end
467
+
468
+ def require_rubygems_min_version!(min_version, feature)
469
+ return if min_version <= Gem::Version.new(Gem::VERSION)
470
+
471
+ raise "#{feature} is not supported on your version of RubyGems. " \
472
+ "You must have RubyGems #{min_version}+ to use this feature."
473
+ end
474
+ end
475
+ end