piesync-puma 3.12.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +1429 -0
  3. data/LICENSE +26 -0
  4. data/README.md +280 -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 +36 -0
  9. data/docs/deployment.md +91 -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 +28 -0
  15. data/docs/restart.md +39 -0
  16. data/docs/signals.md +96 -0
  17. data/docs/systemd.md +272 -0
  18. data/ext/puma_http11/PumaHttp11Service.java +17 -0
  19. data/ext/puma_http11/ext_help.h +15 -0
  20. data/ext/puma_http11/extconf.rb +15 -0
  21. data/ext/puma_http11/http11_parser.c +1071 -0
  22. data/ext/puma_http11/http11_parser.h +65 -0
  23. data/ext/puma_http11/http11_parser.java.rl +161 -0
  24. data/ext/puma_http11/http11_parser.rl +149 -0
  25. data/ext/puma_http11/http11_parser_common.rl +54 -0
  26. data/ext/puma_http11/io_buffer.c +155 -0
  27. data/ext/puma_http11/mini_ssl.c +494 -0
  28. data/ext/puma_http11/org/jruby/puma/Http11.java +234 -0
  29. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +470 -0
  30. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +352 -0
  31. data/ext/puma_http11/puma_http11.c +500 -0
  32. data/lib/puma.rb +23 -0
  33. data/lib/puma/accept_nonblock.rb +23 -0
  34. data/lib/puma/app/status.rb +74 -0
  35. data/lib/puma/binder.rb +413 -0
  36. data/lib/puma/cli.rb +235 -0
  37. data/lib/puma/client.rb +480 -0
  38. data/lib/puma/cluster.rb +531 -0
  39. data/lib/puma/commonlogger.rb +108 -0
  40. data/lib/puma/compat.rb +14 -0
  41. data/lib/puma/configuration.rb +361 -0
  42. data/lib/puma/const.rb +239 -0
  43. data/lib/puma/control_cli.rb +264 -0
  44. data/lib/puma/convenient.rb +25 -0
  45. data/lib/puma/daemon_ext.rb +33 -0
  46. data/lib/puma/delegation.rb +13 -0
  47. data/lib/puma/detect.rb +15 -0
  48. data/lib/puma/dsl.rb +518 -0
  49. data/lib/puma/events.rb +153 -0
  50. data/lib/puma/io_buffer.rb +9 -0
  51. data/lib/puma/java_io_buffer.rb +47 -0
  52. data/lib/puma/jruby_restart.rb +84 -0
  53. data/lib/puma/launcher.rb +433 -0
  54. data/lib/puma/minissl.rb +285 -0
  55. data/lib/puma/null_io.rb +44 -0
  56. data/lib/puma/plugin.rb +117 -0
  57. data/lib/puma/plugin/tmp_restart.rb +34 -0
  58. data/lib/puma/rack/backports/uri/common_193.rb +33 -0
  59. data/lib/puma/rack/builder.rb +299 -0
  60. data/lib/puma/rack/urlmap.rb +91 -0
  61. data/lib/puma/rack_default.rb +7 -0
  62. data/lib/puma/reactor.rb +347 -0
  63. data/lib/puma/runner.rb +184 -0
  64. data/lib/puma/server.rb +1072 -0
  65. data/lib/puma/single.rb +123 -0
  66. data/lib/puma/state_file.rb +31 -0
  67. data/lib/puma/tcp_logger.rb +41 -0
  68. data/lib/puma/thread_pool.rb +346 -0
  69. data/lib/puma/util.rb +129 -0
  70. data/lib/rack/handler/puma.rb +115 -0
  71. data/tools/jungle/README.md +19 -0
  72. data/tools/jungle/init.d/README.md +61 -0
  73. data/tools/jungle/init.d/puma +421 -0
  74. data/tools/jungle/init.d/run-puma +18 -0
  75. data/tools/jungle/rc.d/README.md +74 -0
  76. data/tools/jungle/rc.d/puma +61 -0
  77. data/tools/jungle/rc.d/puma.conf +10 -0
  78. data/tools/jungle/upstart/README.md +61 -0
  79. data/tools/jungle/upstart/puma-manager.conf +31 -0
  80. data/tools/jungle/upstart/puma.conf +69 -0
  81. data/tools/trickletest.rb +45 -0
  82. metadata +131 -0
@@ -0,0 +1,153 @@
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 (#{env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR]}): #{error.inspect}\n---\n"
97
+ end
98
+
99
+ # An SSL error has occurred.
100
+ # +server+ is the Server object, +peeraddr+ peer address, +peercert+
101
+ # any peer certificate (if present), and +error+ an exception object.
102
+ #
103
+ def ssl_error(server, peeraddr, peercert, error)
104
+ subject = peercert ? peercert.subject : nil
105
+ @stderr.puts "#{Time.now}: SSL error, peer: #{peeraddr}, peer cert: #{subject}, #{error.inspect}"
106
+ end
107
+
108
+ # An unknown error has occurred.
109
+ # +server+ is the Server object, +error+ an exception object,
110
+ # +kind+ some additional info, and +env+ the request.
111
+ #
112
+ def unknown_error(server, error, kind="Unknown", env=nil)
113
+ if error.respond_to? :render
114
+ error.render "#{Time.now}: #{kind} error", @stderr
115
+ else
116
+ if env
117
+ string_block = [ "#{Time.now}: #{kind} error handling request { #{env['REQUEST_METHOD']} #{env['PATH_INFO']} }" ]
118
+ string_block << error.inspect
119
+ else
120
+ string_block = [ "#{Time.now}: #{kind} error: #{error.inspect}" ]
121
+ end
122
+ string_block << error.backtrace
123
+ @stderr.puts string_block.join("\n")
124
+ end
125
+ end
126
+
127
+ def on_booted(&block)
128
+ register(:on_booted, &block)
129
+ end
130
+
131
+ def fire_on_booted!
132
+ fire(:on_booted)
133
+ end
134
+
135
+ DEFAULT = new(STDOUT, STDERR)
136
+
137
+ # Returns an Events object which writes its status to 2 StringIO
138
+ # objects.
139
+ #
140
+ def self.strings
141
+ Events.new StringIO.new, StringIO.new
142
+ end
143
+
144
+ def self.stdio
145
+ Events.new $stdout, $stderr
146
+ end
147
+
148
+ def self.null
149
+ n = NullIO.new
150
+ Events.new n, n
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puma/detect'
4
+
5
+ if Puma.jruby?
6
+ require 'puma/java_io_buffer'
7
+ else
8
+ require 'puma/puma_http11'
9
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'java'
4
+
5
+ # Conservative native JRuby/Java implementation of IOBuffer
6
+ # backed by a ByteArrayOutputStream and conversion between
7
+ # Ruby String and Java bytes
8
+ module Puma
9
+ class JavaIOBuffer < java.io.ByteArrayOutputStream
10
+ field_reader :buf
11
+ end
12
+
13
+ class IOBuffer
14
+ BUF_DEFAULT_SIZE = 4096
15
+
16
+ def initialize
17
+ @buf = JavaIOBuffer.new(BUF_DEFAULT_SIZE)
18
+ end
19
+
20
+ def reset
21
+ @buf.reset
22
+ end
23
+
24
+ def <<(str)
25
+ bytes = str.to_java_bytes
26
+ @buf.write(bytes, 0, bytes.length)
27
+ end
28
+
29
+ def append(*strs)
30
+ strs.each { |s| self << s; }
31
+ end
32
+
33
+ def to_s
34
+ String.from_java_bytes @buf.to_byte_array
35
+ end
36
+
37
+ alias_method :to_str, :to_s
38
+
39
+ def used
40
+ @buf.size
41
+ end
42
+
43
+ def capacity
44
+ @buf.buf.length
45
+ end
46
+ end
47
+ end
@@ -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,433 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puma/events'
4
+ require 'puma/detect'
5
+
6
+ require 'puma/cluster'
7
+ require 'puma/single'
8
+
9
+ require 'puma/const'
10
+
11
+ require 'puma/binder'
12
+
13
+ module Puma
14
+ # Puma::Launcher is the single entry point for starting a Puma server based on user
15
+ # configuration. It is responsible for taking user supplied arguments and resolving them
16
+ # with configuration in `config/puma.rb` or `config/puma/<env>.rb`.
17
+ #
18
+ # It is responsible for either launching a cluster of Puma workers or a single
19
+ # puma server.
20
+ class Launcher
21
+ KEYS_NOT_TO_PERSIST_IN_STATE = [
22
+ :logger, :lowlevel_error_handler,
23
+ :before_worker_shutdown, :before_worker_boot, :before_worker_fork,
24
+ :after_worker_boot, :before_fork, :on_restart
25
+ ]
26
+ # Returns an instance of Launcher
27
+ #
28
+ # +conf+ A Puma::Configuration object indicating how to run the server.
29
+ #
30
+ # +launcher_args+ A Hash that currently has one required key `:events`,
31
+ # this is expected to hold an object similar to an `Puma::Events.stdio`,
32
+ # this object will be responsible for broadcasting Puma's internal state
33
+ # to a logging destination. An optional key `:argv` can be supplied,
34
+ # this should be an array of strings, these arguments are re-used when
35
+ # restarting the puma server.
36
+ #
37
+ # Examples:
38
+ #
39
+ # conf = Puma::Configuration.new do |user_config|
40
+ # user_config.threads 1, 10
41
+ # user_config.app do |env|
42
+ # [200, {}, ["hello world"]]
43
+ # end
44
+ # end
45
+ # Puma::Launcher.new(conf, events: Puma::Events.stdio).run
46
+ def initialize(conf, launcher_args={})
47
+ @runner = nil
48
+ @events = launcher_args[:events] || Events::DEFAULT
49
+ @argv = launcher_args[:argv] || []
50
+ @original_argv = @argv.dup
51
+ @config = conf
52
+
53
+ @binder = Binder.new(@events)
54
+ @binder.import_from_env
55
+
56
+ @environment = conf.environment
57
+
58
+ # Advertise the Configuration
59
+ Puma.cli_config = @config if defined?(Puma.cli_config)
60
+
61
+ @config.load
62
+
63
+ @options = @config.options
64
+ @config.clamp
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
+ @events.formatter = Events::PidFormatter.new
85
+ @options[:logger] = @events
86
+
87
+ @runner = Cluster.new(self, @events)
88
+ else
89
+ @runner = Single.new(self, @events)
90
+ end
91
+ Puma.stats_object = @runner
92
+
93
+ @status = :run
94
+ end
95
+
96
+ attr_reader :binder, :events, :config, :options, :restart_dir
97
+
98
+ # Return stats about the server
99
+ def stats
100
+ @runner.stats
101
+ end
102
+
103
+ # Write a state file that can be used by pumactl to control
104
+ # the server
105
+ def write_state
106
+ write_pid
107
+
108
+ path = @options[:state]
109
+ return unless path
110
+
111
+ require 'puma/state_file'
112
+
113
+ sf = StateFile.new
114
+ sf.pid = Process.pid
115
+ sf.control_url = @options[:control_url]
116
+ sf.control_auth_token = @options[:control_auth_token]
117
+
118
+ sf.save path
119
+ end
120
+
121
+ # Delete the configured pidfile
122
+ def delete_pidfile
123
+ path = @options[:pidfile]
124
+ File.unlink(path) if path && File.exist?(path)
125
+ end
126
+
127
+ # If configured, write the pid of the current process out
128
+ # to a file.
129
+ def write_pid
130
+ path = @options[:pidfile]
131
+ return unless path
132
+
133
+ File.open(path, 'w') { |f| f.puts Process.pid }
134
+ cur = Process.pid
135
+ at_exit do
136
+ delete_pidfile if cur == Process.pid
137
+ end
138
+ end
139
+
140
+ # Begin async shutdown of the server
141
+ def halt
142
+ @status = :halt
143
+ @runner.halt
144
+ end
145
+
146
+ # Begin async shutdown of the server gracefully
147
+ def stop
148
+ @status = :stop
149
+ @runner.stop
150
+ end
151
+
152
+ # Begin async restart of the server
153
+ def restart
154
+ @status = :restart
155
+ @runner.restart
156
+ end
157
+
158
+ # Begin a phased restart if supported
159
+ def phased_restart
160
+ unless @runner.respond_to?(:phased_restart) and @runner.phased_restart
161
+ log "* phased-restart called but not available, restarting normally."
162
+ return restart
163
+ end
164
+ true
165
+ end
166
+
167
+ # Run the server. This blocks until the server is stopped
168
+ def run
169
+ previous_env =
170
+ if defined?(Bundler)
171
+ env = Bundler::ORIGINAL_ENV.dup
172
+ # add -rbundler/setup so we load from Gemfile when restarting
173
+ bundle = "-rbundler/setup"
174
+ env["RUBYOPT"] = [env["RUBYOPT"], bundle].join(" ").lstrip unless env["RUBYOPT"].to_s.include?(bundle)
175
+ env
176
+ else
177
+ ENV.to_h
178
+ end
179
+
180
+ @config.clamp
181
+
182
+ @config.plugins.fire_starts self
183
+
184
+ setup_signals
185
+ set_process_title
186
+ @runner.run
187
+
188
+ case @status
189
+ when :halt
190
+ log "* Stopping immediately!"
191
+ when :run, :stop
192
+ graceful_stop
193
+ when :restart
194
+ log "* Restarting..."
195
+ ENV.replace(previous_env)
196
+ @runner.before_restart
197
+ restart!
198
+ when :exit
199
+ # nothing
200
+ end
201
+ end
202
+
203
+ # Return which tcp port the launcher is using, if it's using TCP
204
+ def connected_port
205
+ @binder.connected_port
206
+ end
207
+
208
+ def restart_args
209
+ cmd = @options[:restart_cmd]
210
+ if cmd
211
+ cmd.split(' ') + @original_argv
212
+ else
213
+ @restart_argv
214
+ end
215
+ end
216
+
217
+ private
218
+
219
+ def reload_worker_directory
220
+ @runner.reload_worker_directory if @runner.respond_to?(:reload_worker_directory)
221
+ end
222
+
223
+ def restart!
224
+ @config.run_hooks :on_restart, self
225
+
226
+ if Puma.jruby?
227
+ close_binder_listeners
228
+
229
+ require 'puma/jruby_restart'
230
+ JRubyRestart.chdir_exec(@restart_dir, restart_args)
231
+ elsif Puma.windows?
232
+ close_binder_listeners
233
+
234
+ argv = restart_args
235
+ Dir.chdir(@restart_dir)
236
+ Kernel.exec(*argv)
237
+ else
238
+ redirects = {:close_others => true}
239
+ @binder.listeners.each_with_index do |(l, io), i|
240
+ ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
241
+ redirects[io.to_i] = io.to_i
242
+ end
243
+
244
+ argv = restart_args
245
+ Dir.chdir(@restart_dir)
246
+ argv += [redirects] if RUBY_VERSION >= '1.9'
247
+ Kernel.exec(*argv)
248
+ end
249
+ end
250
+
251
+ def prune_bundler
252
+ return unless defined?(Bundler)
253
+ puma = Bundler.rubygems.loaded_specs("puma")
254
+ dirs = puma.require_paths.map { |x| File.join(puma.full_gem_path, x) }
255
+ puma_lib_dir = dirs.detect { |x| File.exist? File.join(x, '../bin/puma-wild') }
256
+
257
+ unless puma_lib_dir
258
+ log "! Unable to prune Bundler environment, continuing"
259
+ return
260
+ end
261
+
262
+ deps = puma.runtime_dependencies.map do |d|
263
+ spec = Bundler.rubygems.loaded_specs(d.name)
264
+ "#{d.name}:#{spec.version.to_s}"
265
+ end
266
+
267
+ log '* Pruning Bundler environment'
268
+ home = ENV['GEM_HOME']
269
+ Bundler.with_clean_env do
270
+ ENV['GEM_HOME'] = home
271
+ ENV['PUMA_BUNDLER_PRUNED'] = '1'
272
+ wild = File.expand_path(File.join(puma_lib_dir, "../bin/puma-wild"))
273
+ args = [Gem.ruby, wild, '-I', dirs.join(':'), deps.join(',')] + @original_argv
274
+ # Ruby 2.0+ defaults to true which breaks socket activation
275
+ args += [{:close_others => false}] if RUBY_VERSION >= '2.0'
276
+ Kernel.exec(*args)
277
+ end
278
+ end
279
+
280
+ def log(str)
281
+ @events.log str
282
+ end
283
+
284
+ def clustered?
285
+ (@options[:workers] || 0) > 0
286
+ end
287
+
288
+ def unsupported(str)
289
+ @events.error(str)
290
+ raise UnsupportedOption
291
+ end
292
+
293
+ def graceful_stop
294
+ @runner.stop_blocked
295
+ log "=== puma shutdown: #{Time.now} ==="
296
+ log "- Goodbye!"
297
+ end
298
+
299
+ def set_process_title
300
+ Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
301
+ end
302
+
303
+ def title
304
+ buffer = "puma #{Puma::Const::VERSION} (#{@options[:binds].join(',')})"
305
+ buffer += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
306
+ buffer
307
+ end
308
+
309
+ def set_rack_environment
310
+ @options[:environment] = environment
311
+ ENV['RACK_ENV'] = environment
312
+ end
313
+
314
+ def environment
315
+ @environment
316
+ end
317
+
318
+ def prune_bundler?
319
+ @options[:prune_bundler] && clustered? && !@options[:preload_app]
320
+ end
321
+
322
+ def close_binder_listeners
323
+ @binder.listeners.each do |l, io|
324
+ io.close
325
+ uri = URI.parse(l)
326
+ next unless uri.scheme == 'unix'
327
+ File.unlink("#{uri.host}#{uri.path}")
328
+ end
329
+ end
330
+
331
+
332
+ def generate_restart_data
333
+ if dir = @options[:directory]
334
+ @restart_dir = dir
335
+
336
+ elsif Puma.windows?
337
+ # I guess the value of PWD is garbage on windows so don't bother
338
+ # using it.
339
+ @restart_dir = Dir.pwd
340
+
341
+ # Use the same trick as unicorn, namely favor PWD because
342
+ # it will contain an unresolved symlink, useful for when
343
+ # the pwd is /data/releases/current.
344
+ elsif dir = ENV['PWD']
345
+ s_env = File.stat(dir)
346
+ s_pwd = File.stat(Dir.pwd)
347
+
348
+ if s_env.ino == s_pwd.ino and (Puma.jruby? or s_env.dev == s_pwd.dev)
349
+ @restart_dir = dir
350
+ end
351
+ end
352
+
353
+ @restart_dir ||= Dir.pwd
354
+
355
+ # if $0 is a file in the current directory, then restart
356
+ # it the same, otherwise add -S on there because it was
357
+ # picked up in PATH.
358
+ #
359
+ if File.exist?($0)
360
+ arg0 = [Gem.ruby, $0]
361
+ else
362
+ arg0 = [Gem.ruby, "-S", $0]
363
+ end
364
+
365
+ # Detect and reinject -Ilib from the command line, used for testing without bundler
366
+ # cruby has an expanded path, jruby has just "lib"
367
+ lib = File.expand_path "lib"
368
+ arg0[1,0] = ["-I", lib] if [lib, "lib"].include?($LOAD_PATH[0])
369
+
370
+ if defined? Puma::WILD_ARGS
371
+ @restart_argv = arg0 + Puma::WILD_ARGS + @original_argv
372
+ else
373
+ @restart_argv = arg0 + @original_argv
374
+ end
375
+ end
376
+
377
+ def setup_signals
378
+ begin
379
+ Signal.trap "SIGUSR2" do
380
+ restart
381
+ end
382
+ rescue Exception
383
+ log "*** SIGUSR2 not implemented, signal based restart unavailable!"
384
+ end
385
+
386
+ unless Puma.jruby?
387
+ begin
388
+ Signal.trap "SIGUSR1" do
389
+ phased_restart
390
+ end
391
+ rescue Exception
392
+ log "*** SIGUSR1 not implemented, signal based restart unavailable!"
393
+ end
394
+ end
395
+
396
+ begin
397
+ Signal.trap "SIGTERM" do
398
+ graceful_stop
399
+
400
+ raise SignalException, "SIGTERM"
401
+ end
402
+ rescue Exception
403
+ log "*** SIGTERM not implemented, signal based gracefully stopping unavailable!"
404
+ end
405
+
406
+ begin
407
+ Signal.trap "SIGINT" do
408
+ if Puma.jruby?
409
+ @status = :exit
410
+ graceful_stop
411
+ exit
412
+ end
413
+
414
+ stop
415
+ end
416
+ rescue Exception
417
+ log "*** SIGINT not implemented, signal based gracefully stopping unavailable!"
418
+ end
419
+
420
+ begin
421
+ Signal.trap "SIGHUP" do
422
+ if @runner.redirected_io?
423
+ @runner.redirect_io
424
+ else
425
+ stop
426
+ end
427
+ end
428
+ rescue Exception
429
+ log "*** SIGHUP not implemented, signal based logs reopening unavailable!"
430
+ end
431
+ end
432
+ end
433
+ end