diff options
author | Tim Moore <tmoore@incrementalism.net> | 2013-01-30 13:45:32 +1100 |
---|---|---|
committer | James Tucker <jftucker@gmail.com> | 2013-02-07 18:34:01 -0800 |
commit | 3316749e3cd0b2bf43400e215f62b06499f00dbc (patch) | |
tree | a4243ff176cfaeae29b7f6dfaa649a9bfca146ed | |
parent | dcc7e6fa5106e1e8129f4bbe21f7e1607dbf5197 (diff) | |
download | rack-3316749e3cd0b2bf43400e215f62b06499f00dbc.tar.gz |
Stop overwriting existing pidfiles.
A race condition can arise when two servers are started simultaneously. Both
instances may complete the check for an existing pidfile before either one
writes it.
Now the pidfile is opened with ::File::EXCL, which raises an error if the file
already exists. This error is handled by retrying the check and the write.
-rw-r--r-- | lib/rack/server.rb | 5 | ||||
-rw-r--r-- | test/spec_server.rb | 16 |
2 files changed, 20 insertions, 1 deletions
diff --git a/lib/rack/server.rb b/lib/rack/server.rb index b385d4bd..dfaed3fc 100644 --- a/lib/rack/server.rb +++ b/lib/rack/server.rb @@ -329,8 +329,11 @@ module Rack end def write_pid - ::File.open(options[:pid], 'w'){ |f| f.write("#{Process.pid}") } + ::File.open(options[:pid], ::File::CREAT | ::File::EXCL | ::File::WRONLY ){ |f| f.write("#{Process.pid}") } at_exit { ::File.delete(options[:pid]) if ::File.exist?(options[:pid]) } + rescue Errno::EEXIST + check_pid! + retry end def check_pid! diff --git a/test/spec_server.rb b/test/spec_server.rb index 82495bd8..44d4bcbb 100644 --- a/test/spec_server.rb +++ b/test/spec_server.rb @@ -110,6 +110,22 @@ describe Rack::Server do server.send(:pidfile_process_status).should.eql :not_owned end + should "not write pid file when it is created after check" do + pidfile = Tempfile.open('pidfile') { |f| break f }.path + ::File.delete(pidfile) + server = Rack::Server.new(:pid => pidfile) + ::File.open(pidfile, 'w') { |f| f.write(1) } + with_stderr do |err| + should.raise(SystemExit) do + server.send(:write_pid) + end + err.rewind + output = err.read + output.should.match(/already running/) + output.should.include? pidfile + end + end + should "inform the user about existing pidfiles with running processes" do pidfile = Tempfile.open('pidfile') { |f| f.write(1); break f }.path server = Rack::Server.new(:pid => pidfile) |