configurator: set ENV["PWD"] with working_directory, too
[unicorn.git] / lib / unicorn / configurator.rb
blob93b7bf4460741a8f6af95b9b10231b2ae51accca
1 # -*- encoding: binary -*-
3 require 'socket'
4 require 'logger'
6 module Unicorn
8   # Implements a simple DSL for configuring a Unicorn server.
9   #
10   # Example (when used with the unicorn config file):
11   #   worker_processes 4
12   #   working_directory "/path/to/deploy/app/current"
13   #   listen '/tmp/my_app.sock', :backlog => 1
14   #   listen 9292, :tcp_nopush => true
15   #   timeout 10
16   #   pid "/tmp/my_app.pid"
17   #
18   #   # combine REE with "preload_app true" for memory savings
19   #   # http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
20   #   preload_app true
21   #   GC.respond_to?(:copy_on_write_friendly=) and
22   #     GC.copy_on_write_friendly = true
23   #
24   #   before_fork do |server, worker|
25   #     # the following is recomended for Rails + "preload_app true"
26   #     # as there's no need for the master process to hold a connection
27   #     defined?(ActiveRecord::Base) and
28   #       ActiveRecord::Base.connection.disconnect!
29   #
30   #     # the following allows a new master process to incrementally
31   #     # phase out the old master process with SIGTTOU to avoid a
32   #     # thundering herd (especially in the "preload_app false" case)
33   #     # when doing a transparent upgrade.  The last worker spawned
34   #     # will then kill off the old master process with a SIGQUIT.
35   #     old_pid = "#{server.config[:pid]}.oldbin"
36   #     if old_pid != server.pid
37   #       begin
38   #         sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
39   #         Process.kill(sig, File.read(old_pid).to_i)
40   #       rescue Errno::ENOENT, Errno::ESRCH
41   #       end
42   #     end
43   #
44   #     # optionally throttle the master from forking too quickly by sleeping
45   #     sleep 1
46   #   end
47   #
48   #   after_fork do |server, worker|
49   #     # per-process listener ports for debugging/admin/migrations
50   #     addr = "127.0.0.1:#{9293 + worker.nr}"
51   #     server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
52   #
53   #     # the following is required for Rails + "preload_app true",
54   #     defined?(ActiveRecord::Base) and
55   #       ActiveRecord::Base.establish_connection
56   #
57   #     # if preload_app is true, then you may also want to check and
58   #     # restart any other shared sockets/descriptors such as Memcached,
59   #     # and Redis.  TokyoCabinet file handles are safe to reuse
60   #     # between any number of forked children (assuming your kernel
61   #     # correctly implements pread()/pwrite() system calls)
62   #   end
63   class Configurator < Struct.new(:set, :config_file)
65     # Default settings for Unicorn
66     DEFAULTS = {
67       :timeout => 60,
68       :logger => Logger.new($stderr),
69       :worker_processes => 1,
70       :after_fork => lambda { |server, worker|
71           server.logger.info("worker=#{worker.nr} spawned pid=#{$$}")
72         },
73       :before_fork => lambda { |server, worker|
74           server.logger.info("worker=#{worker.nr} spawning...")
75         },
76       :before_exec => lambda { |server|
77           server.logger.info("forked child re-executing...")
78         },
79       :pid => nil,
80       :preload_app => false,
81     }
83     def initialize(defaults = {}) #:nodoc:
84       self.set = Hash.new(:unset)
85       use_defaults = defaults.delete(:use_defaults)
86       self.config_file = defaults.delete(:config_file)
87       set.merge!(DEFAULTS) if use_defaults
88       defaults.each { |key, value| self.send(key, value) }
89       Hash === set[:listener_opts] or
90           set[:listener_opts] = Hash.new { |hash,key| hash[key] = {} }
91       Array === set[:listeners] or set[:listeners] = []
92       reload
93     end
95     def reload #:nodoc:
96       instance_eval(File.read(config_file), config_file) if config_file
97     end
99     def commit!(server, options = {}) #:nodoc:
100       skip = options[:skip] || []
101       set.each do |key, value|
102         value == :unset and next
103         skip.include?(key) and next
104         server.__send__("#{key}=", value)
105       end
106     end
108     def [](key) # :nodoc:
109       set[key]
110     end
112     # sets object to the +new+ Logger-like object.  The new logger-like
113     # object must respond to the following methods:
114     #  +debug+, +info+, +warn+, +error+, +fatal+, +close+
115     def logger(new)
116       %w(debug info warn error fatal close).each do |m|
117         new.respond_to?(m) and next
118         raise ArgumentError, "logger=#{new} does not respond to method=#{m}"
119       end
121       set[:logger] = new
122     end
124     # sets after_fork hook to a given block.  This block will be called by
125     # the worker after forking.  The following is an example hook which adds
126     # a per-process listener to every worker:
127     #
128     #  after_fork do |server,worker|
129     #    # per-process listener ports for debugging/admin:
130     #    addr = "127.0.0.1:#{9293 + worker.nr}"
131     #
132     #    # the negative :tries parameter indicates we will retry forever
133     #    # waiting on the existing process to exit with a 5 second :delay
134     #    # Existing options for Unicorn::Configurator#listen such as
135     #    # :backlog, :rcvbuf, :sndbuf are available here as well.
136     #    server.listen(addr, :tries => -1, :delay => 5, :backlog => 128)
137     #
138     #    # drop permissions to "www-data" in the worker
139     #    # generally there's no reason to start Unicorn as a priviledged user
140     #    # as it is not recommended to expose Unicorn to public clients.
141     #    uid, gid = Process.euid, Process.egid
142     #    user, group = 'www-data', 'www-data'
143     #    target_uid = Etc.getpwnam(user).uid
144     #    target_gid = Etc.getgrnam(group).gid
145     #    worker.tmp.chown(target_uid, target_gid)
146     #    if uid != target_uid || gid != target_gid
147     #      Process.initgroups(user, target_gid)
148     #      Process::GID.change_privilege(target_gid)
149     #      Process::UID.change_privilege(target_uid)
150     #    end
151     #  end
152     def after_fork(*args, &block)
153       set_hook(:after_fork, block_given? ? block : args[0])
154     end
156     # sets before_fork got be a given Proc object.  This Proc
157     # object will be called by the master process before forking
158     # each worker.
159     def before_fork(*args, &block)
160       set_hook(:before_fork, block_given? ? block : args[0])
161     end
163     # sets the before_exec hook to a given Proc object.  This
164     # Proc object will be called by the master process right
165     # before exec()-ing the new unicorn binary.  This is useful
166     # for freeing certain OS resources that you do NOT wish to
167     # share with the reexeced child process.
168     # There is no corresponding after_exec hook (for obvious reasons).
169     def before_exec(*args, &block)
170       set_hook(:before_exec, block_given? ? block : args[0], 1)
171     end
173     # sets the timeout of worker processes to +seconds+.  Workers
174     # handling the request/app.call/response cycle taking longer than
175     # this time period will be forcibly killed (via SIGKILL).  This
176     # timeout is enforced by the master process itself and not subject
177     # to the scheduling limitations by the worker process.  Due the
178     # low-complexity, low-overhead implementation, timeouts of less
179     # than 3.0 seconds can be considered inaccurate and unsafe.
180     #
181     # For running Unicorn behind nginx, it is recommended to set
182     # "fail_timeout=0" for in your nginx configuration like this
183     # to have nginx always retry backends that may have had workers
184     # SIGKILL-ed due to timeouts.
185     #
186     #    # See http://wiki.nginx.org/NginxHttpUpstreamModule for more details
187     #    # on nginx upstream configuration:
188     #    upstream unicorn_backend {
189     #      # for UNIX domain socket setups:
190     #      server unix:/path/to/unicorn.sock fail_timeout=0;
191     #
192     #      # for TCP setups
193     #      server 192.168.0.7:8080 fail_timeout=0;
194     #      server 192.168.0.8:8080 fail_timeout=0;
195     #      server 192.168.0.9:8080 fail_timeout=0;
196     #    }
197     def timeout(seconds)
198       Numeric === seconds or raise ArgumentError,
199                                   "not numeric: timeout=#{seconds.inspect}"
200       seconds >= 3 or raise ArgumentError,
201                                   "too low: timeout=#{seconds.inspect}"
202       set[:timeout] = seconds
203     end
205     # sets the current number of worker_processes to +nr+.  Each worker
206     # process will serve exactly one client at a time.
207     def worker_processes(nr)
208       Integer === nr or raise ArgumentError,
209                              "not an integer: worker_processes=#{nr.inspect}"
210       nr >= 0 or raise ArgumentError,
211                              "not non-negative: worker_processes=#{nr.inspect}"
212       set[:worker_processes] = nr
213     end
215     # sets listeners to the given +addresses+, replacing or augmenting the
216     # current set.  This is for the global listener pool shared by all
217     # worker processes.  For per-worker listeners, see the after_fork example
218     # This is for internal API use only, do not use it in your Unicorn
219     # config file.  Use listen instead.
220     def listeners(addresses) # :nodoc:
221       Array === addresses or addresses = Array(addresses)
222       addresses.map! { |addr| expand_addr(addr) }
223       set[:listeners] = addresses
224     end
226     # adds an +address+ to the existing listener set.
227     #
228     # The following options may be specified (but are generally not needed):
229     #
230     # +:backlog+: this is the backlog of the listen() syscall.
231     #
232     # Some operating systems allow negative values here to specify the
233     # maximum allowable value.  In most cases, this number is only
234     # recommendation and there are other OS-specific tunables and
235     # variables that can affect this number.  See the listen(2)
236     # syscall documentation of your OS for the exact semantics of
237     # this.
238     #
239     # If you are running unicorn on multiple machines, lowering this number
240     # can help your load balancer detect when a machine is overloaded
241     # and give requests to a different machine.
242     #
243     # Default: 1024
244     #
245     # +:rcvbuf+, +:sndbuf+: maximum receive and send buffer sizes of sockets
246     #
247     # These correspond to the SO_RCVBUF and SO_SNDBUF settings which
248     # can be set via the setsockopt(2) syscall.  Some kernels
249     # (e.g. Linux 2.4+) have intelligent auto-tuning mechanisms and
250     # there is no need (and it is sometimes detrimental) to specify them.
251     #
252     # See the socket API documentation of your operating system
253     # to determine the exact semantics of these settings and
254     # other operating system-specific knobs where they can be
255     # specified.
256     #
257     # Defaults: operating system defaults
258     #
259     # +:tcp_nodelay+: disables Nagle's algorithm on TCP sockets
260     #
261     # This has no effect on UNIX sockets.
262     #
263     # Default: operating system defaults (usually Nagle's algorithm enabled)
264     #
265     # +:tcp_nopush+: enables TCP_CORK in Linux or TCP_NOPUSH in FreeBSD
266     #
267     # This will prevent partial TCP frames from being sent out.
268     # Enabling +tcp_nopush+ is generally not needed or recommended as
269     # controlling +tcp_nodelay+ already provides sufficient latency
270     # reduction whereas Unicorn does not know when the best times are
271     # for flushing corked sockets.
272     #
273     # This has no effect on UNIX sockets.
274     #
275     # +:tries+: times to retry binding a socket if it is already in use
276     #
277     # A negative number indicates we will retry indefinitely, this is
278     # useful for migrations and upgrades when individual workers
279     # are binding to different ports.
280     #
281     # Default: 5
282     #
283     # +:delay+: seconds to wait between successive +tries+
284     #
285     # Default: 0.5 seconds
286     def listen(address, opt = {})
287       address = expand_addr(address)
288       if String === address
289         [ :backlog, :sndbuf, :rcvbuf, :tries ].each do |key|
290           value = opt[key] or next
291           Integer === value or
292             raise ArgumentError, "not an integer: #{key}=#{value.inspect}"
293         end
294         [ :tcp_nodelay, :tcp_nopush ].each do |key|
295           (value = opt[key]).nil? and next
296           TrueClass === value || FalseClass === value or
297             raise ArgumentError, "not boolean: #{key}=#{value.inspect}"
298         end
299         unless (value = opt[:delay]).nil?
300           Numeric === value or
301             raise ArgumentError, "not numeric: delay=#{value.inspect}"
302         end
303         set[:listener_opts][address].merge!(opt)
304       end
306       set[:listeners] << address
307     end
309     # sets the +path+ for the PID file of the unicorn master process
310     def pid(path); set_path(:pid, path); end
312     # Enabling this preloads an application before forking worker
313     # processes.  This allows memory savings when using a
314     # copy-on-write-friendly GC but can cause bad things to happen when
315     # resources like sockets are opened at load time by the master
316     # process and shared by multiple children.  People enabling this are
317     # highly encouraged to look at the before_fork/after_fork hooks to
318     # properly close/reopen sockets.  Files opened for logging do not
319     # have to be reopened as (unbuffered-in-userspace) files opened with
320     # the File::APPEND flag are written to atomically on UNIX.
321     #
322     # In addition to reloading the unicorn-specific config settings,
323     # SIGHUP will reload application code in the working
324     # directory/symlink when workers are gracefully restarted.
325     def preload_app(bool)
326       case bool
327       when TrueClass, FalseClass
328         set[:preload_app] = bool
329       else
330         raise ArgumentError, "preload_app=#{bool.inspect} not a boolean"
331       end
332     end
334     # Allow redirecting $stderr to a given path.  Unlike doing this from
335     # the shell, this allows the unicorn process to know the path its
336     # writing to and rotate the file if it is used for logging.  The
337     # file will be opened with the File::APPEND flag and writes
338     # synchronized to the kernel (but not necessarily to _disk_) so
339     # multiple processes can safely append to it.
340     def stderr_path(path)
341       set_path(:stderr_path, path)
342     end
344     # Same as stderr_path, except for $stdout
345     def stdout_path(path)
346       set_path(:stdout_path, path)
347     end
349     # sets the working directory for Unicorn.  This ensures USR2 will
350     # start a new instance of Unicorn in this directory.  This may be
351     # a symlink.  You should specify this directive near the top or
352     # your config file before any relative paths for other config
353     # directives (or avoid relative paths entirely).
354     def working_directory(path)
355       # just let chdir raise errors
356       path = File.expand_path(path)
357       Dir.chdir(path)
358       HttpServer::START_CTX[:cwd] = ENV["PWD"] = path
359     end
361     # expands "unix:path/to/foo" to a socket relative to the current path
362     # expands pathnames of sockets if relative to "~" or "~username"
363     # expands "*:port and ":port" to "0.0.0.0:port"
364     def expand_addr(address) #:nodoc
365       return "0.0.0.0:#{address}" if Integer === address
366       return address unless String === address
368       case address
369       when %r{\Aunix:(.*)\z}
370         File.expand_path($1)
371       when %r{\A~}
372         File.expand_path(address)
373       when %r{\A(?:\*:)?(\d+)\z}
374         "0.0.0.0:#$1"
375       when %r{\A(.*):(\d+)\z}
376         # canonicalize the name
377         packed = Socket.pack_sockaddr_in($2.to_i, $1)
378         Socket.unpack_sockaddr_in(packed).reverse!.join(':')
379       else
380         address
381       end
382     end
384   private
386     def set_path(var, path) #:nodoc:
387       case path
388       when NilClass
389       when String
390         path = File.expand_path(path)
391         File.writable?(File.dirname(path)) or \
392                raise ArgumentError, "directory for #{var}=#{path} not writable"
393       else
394         raise ArgumentError
395       end
396       set[var] = path
397     end
399     def set_hook(var, my_proc, req_arity = 2) #:nodoc:
400       case my_proc
401       when Proc
402         arity = my_proc.arity
403         (arity == req_arity) or \
404           raise ArgumentError,
405                 "#{var}=#{my_proc.inspect} has invalid arity: " \
406                 "#{arity} (need #{req_arity})"
407       when NilClass
408         my_proc = DEFAULTS[var]
409       else
410         raise ArgumentError, "invalid type: #{var}=#{my_proc.inspect}"
411       end
412       set[var] = my_proc
413     end
415   end