diff options
Diffstat (limited to 'lib/unicorn')
-rw-r--r-- | lib/unicorn/configurator.rb | 35 | ||||
-rw-r--r-- | lib/unicorn/http_server.rb | 5 | ||||
-rw-r--r-- | lib/unicorn/oob_gc.rb | 3 | ||||
-rw-r--r-- | lib/unicorn/socket_helper.rb | 7 | ||||
-rw-r--r-- | lib/unicorn/stream_input.rb | 9 | ||||
-rw-r--r-- | lib/unicorn/tee_input.rb | 14 | ||||
-rw-r--r-- | lib/unicorn/worker.rb | 13 |
7 files changed, 59 insertions, 27 deletions
diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb index 1e2b6e4..9f7f56f 100644 --- a/lib/unicorn/configurator.rb +++ b/lib/unicorn/configurator.rb @@ -49,10 +49,13 @@ class Unicorn::Configurator server.logger.error(m) end }, + :after_worker_ready => lambda { |server, worker| + server.logger.info("worker=#{worker.nr} ready") + }, :pid => nil, :preload_app => false, :check_client_connection => false, - :rewindable_input => true, # for Rack 2.x: (Rack::VERSION[0] <= 1), + :rewindable_input => true, :client_body_buffer_size => Unicorn::Const::MAX_BODY, } #:startdoc: @@ -172,6 +175,21 @@ class Unicorn::Configurator set_hook(:after_worker_exit, block_given? ? block : args[0], 3) end + # sets after_worker_ready hook to a given block. This block will be called + # by a worker process after it has been fully loaded, directly before it + # starts responding to requests: + # + # after_worker_ready do |server,worker| + # server.logger.info("worker #{worker.nr} ready, dropping privileges") + # worker.user('username', 'groupname') + # end + # + # Do not use Configurator#user if you rely on changing users in the + # after_worker_ready hook. + def after_worker_ready(*args, &block) + set_hook(:after_worker_ready, block_given? ? block : args[0]) + end + # sets before_fork got be a given Proc object. This Proc # object will be called by the master process before forking # each worker. @@ -487,13 +505,12 @@ class Unicorn::Configurator # Disabling rewindability can improve performance by lowering # I/O and memory usage for applications that accept uploads. # Keep in mind that the Rack 1.x spec requires - # \env[\"rack.input\"] to be rewindable, so this allows - # intentionally violating the current Rack 1.x spec. + # \env[\"rack.input\"] to be rewindable, + # but the Rack 2.x spec does not. # - # +rewindable_input+ defaults to +true+ when used with Rack 1.x for - # Rack conformance. When Rack 2.x is finalized, this will most - # likely default to +false+ while still conforming to the newer - # (less demanding) spec. + # +rewindable_input+ defaults to +true+ for compatibility. + # Setting it to +false+ may be safe for applications and + # frameworks developed for Rack 2.x and later. def rewindable_input(bool) set_bool(:rewindable_input, bool) end @@ -569,6 +586,10 @@ class Unicorn::Configurator # This switch will occur after calling the after_fork hook, and only # if the Worker#user method is not called in the after_fork hook # +group+ is optional and will not change if unspecified. + # + # Do not use Configurator#user if you rely on changing users in the + # after_worker_ready hook. Instead, you need to call Worker#user + # directly in after_worker_ready. def user(user, group = nil) # raises ArgumentError on invalid user/group Etc.getpwnam(user) diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb index c2086cb..4a2ccce 100644 --- a/lib/unicorn/http_server.rb +++ b/lib/unicorn/http_server.rb @@ -15,7 +15,7 @@ class Unicorn::HttpServer :before_fork, :after_fork, :before_exec, :listener_opts, :preload_app, :orig_app, :config, :ready_pipe, :user - attr_writer :after_worker_exit + attr_writer :after_worker_exit, :after_worker_ready attr_reader :pid, :logger include Unicorn::SocketHelper @@ -89,6 +89,7 @@ class Unicorn::HttpServer @self_pipe = [] @workers = {} # hash maps PIDs to Workers @sig_queue = [] # signal queue used for self-piping + @pid = nil # we try inheriting listeners first, so we bind them later. # we don't write the pid file until we've bound listeners in case @@ -644,7 +645,7 @@ class Unicorn::HttpServer trap(:USR1) { nr = -65536 } ready = readers.dup - @logger.info "worker=#{worker.nr} ready" + @after_worker_ready.call(self, worker) begin nr < 0 and reopen_worker_logs(worker.nr) diff --git a/lib/unicorn/oob_gc.rb b/lib/unicorn/oob_gc.rb index 5572e59..c4741a0 100644 --- a/lib/unicorn/oob_gc.rb +++ b/lib/unicorn/oob_gc.rb @@ -66,10 +66,9 @@ module Unicorn::OobGC end #:stopdoc: - PATH_INFO = "PATH_INFO" def process_client(client) super(client) # Unicorn::HttpServer#process_client - if OOBGC_PATH =~ OOBGC_ENV[PATH_INFO] && ((@@nr -= 1) <= 0) + if OOBGC_PATH =~ OOBGC_ENV['PATH_INFO'] && ((@@nr -= 1) <= 0) @@nr = OOBGC_INTERVAL OOBGC_ENV.clear disabled = GC.enable diff --git a/lib/unicorn/socket_helper.rb b/lib/unicorn/socket_helper.rb index 5371413..f52dde2 100644 --- a/lib/unicorn/socket_helper.rb +++ b/lib/unicorn/socket_helper.rb @@ -75,12 +75,15 @@ module Unicorn elsif respond_to?(:accf_arg) name = opt[:accept_filter] name = DEFAULTS[:accept_filter] if name.nil? + sock.listen(opt[:backlog]) + got = (sock.getsockopt(:SOL_SOCKET, :SO_ACCEPTFILTER) rescue nil).to_s + arg = accf_arg(name) begin - sock.setsockopt(:SOL_SOCKET, :SO_ACCEPTFILTER, accf_arg(name)) + sock.setsockopt(:SOL_SOCKET, :SO_ACCEPTFILTER, arg) rescue => e logger.error("#{sock_name(sock)} " \ "failed to set accept_filter=#{name} (#{e.inspect})") - end + end if arg != got end end diff --git a/lib/unicorn/stream_input.rb b/lib/unicorn/stream_input.rb index de5aeea..41d28a0 100644 --- a/lib/unicorn/stream_input.rb +++ b/lib/unicorn/stream_input.rb @@ -1,16 +1,17 @@ # -*- encoding: binary -*- -# When processing uploads, Unicorn may expose a StreamInput object under -# "rack.input" of the (future) Rack (2.x) environment. +# When processing uploads, unicorn may expose a StreamInput object under +# "rack.input" of the Rack environment when +# Unicorn::Configurator#rewindable_input is set to +false+ class Unicorn::StreamInput # The I/O chunk size (in +bytes+) for I/O operations where # the size cannot be user-specified when a method is called. # The default is 16 kilobytes. - @@io_chunk_size = Unicorn::Const::CHUNK_SIZE + @@io_chunk_size = Unicorn::Const::CHUNK_SIZE # :nodoc: # Initializes a new StreamInput object. You normally do not have to call # this unless you are writing an HTTP server. - def initialize(socket, request) + def initialize(socket, request) # :nodoc: @chunked = request.content_length.nil? @socket = socket @parser = request diff --git a/lib/unicorn/tee_input.rb b/lib/unicorn/tee_input.rb index 6f66162..2ccc2d9 100644 --- a/lib/unicorn/tee_input.rb +++ b/lib/unicorn/tee_input.rb @@ -1,6 +1,6 @@ # -*- encoding: binary -*- -# acts like tee(1) on an input input to provide a input-like stream +# Acts like tee(1) on an input input to provide a input-like stream # while providing rewindable semantics through a File/StringIO backing # store. On the first pass, the input is only read on demand so your # Rack application can use input notification (upload progress and @@ -9,22 +9,22 @@ # strict interpretation of Rack::Lint::InputWrapper functionality and # will not support any deviations from it. # -# When processing uploads, Unicorn exposes a TeeInput object under -# "rack.input" of the Rack environment. +# When processing uploads, unicorn exposes a TeeInput object under +# "rack.input" of the Rack environment by default. class Unicorn::TeeInput < Unicorn::StreamInput # The maximum size (in +bytes+) to buffer in memory before # resorting to a temporary file. Default is 112 kilobytes. - @@client_body_buffer_size = Unicorn::Const::MAX_BODY + @@client_body_buffer_size = Unicorn::Const::MAX_BODY # :nodoc: # sets the maximum size of request bodies to buffer in memory, # amounts larger than this are buffered to the filesystem - def self.client_body_buffer_size=(bytes) + def self.client_body_buffer_size=(bytes) # :nodoc: @@client_body_buffer_size = bytes end # returns the maximum size of request bodies to buffer in memory, # amounts larger than this are buffered to the filesystem - def self.client_body_buffer_size + def self.client_body_buffer_size # :nodoc: @@client_body_buffer_size end @@ -37,7 +37,7 @@ class Unicorn::TeeInput < Unicorn::StreamInput # Initializes a new TeeInput object. You normally do not have to call # this unless you are writing an HTTP server. - def initialize(socket, request) + def initialize(socket, request) # :nodoc: @len = request.content_length super @tmp = @len && @len <= @@client_body_buffer_size ? diff --git a/lib/unicorn/worker.rb b/lib/unicorn/worker.rb index 6748a2f..e22c1bf 100644 --- a/lib/unicorn/worker.rb +++ b/lib/unicorn/worker.rb @@ -111,9 +111,11 @@ class Unicorn::Worker # In most cases, you should be using the Unicorn::Configurator#user # directive instead. This method should only be used if you need # fine-grained control of exactly when you want to change permissions - # in your after_fork hooks. + # in your after_fork or after_worker_ready hooks, or if you want to + # use the chroot support. # - # Changes the worker process to the specified +user+ and +group+ + # Changes the worker process to the specified +user+ and +group+, + # and chroots to the current working directory if +chroot+ is set. # This is only intended to be called from within the worker # process from the +after_fork+ hook. This should be called in # the +after_fork+ hook after any privileged functions need to be @@ -123,7 +125,7 @@ class Unicorn::Worker # directly back to the caller (usually the +after_fork+ hook. # These errors commonly include ArgumentError for specifying an # invalid user/group and Errno::EPERM for insufficient privileges - def user(user, group = nil) + def user(user, group = nil, chroot = false) # we do not protect the caller, checking Process.euid == 0 is # insufficient because modern systems have fine-grained # capabilities. Let the caller handle any and all errors. @@ -134,6 +136,11 @@ class Unicorn::Worker Process.initgroups(user, gid) Process::GID.change_privilege(gid) end + if chroot + chroot = Dir.pwd if chroot == true + Dir.chroot(chroot) + Dir.chdir('/') + end Process.euid != uid and Process::UID.change_privilege(uid) @switched = true end |